一、项目背景:当AI阅读遇到跨平台需求

Saga Reader(麒睿智库)是一款基于AI技术的轻量级跨平台阅读器,核心功能涵盖RSS订阅、内容智能抓取、AI内容处理(如翻译、摘要)及本地存储。项目采用Rust(后端)+Svelte(前端)+Tauri(跨平台框架)的技术组合,目标是在老旧设备上实现"低于10MB内存占用"的极致性能,同时提供流畅的用户交互体验。关于Saga Reader的渊源,见《开源我的一款自用AI阅读器,引流Web前端、Rust、Tauri、AI应用开发》

关于Saga Reader

基于Tauri开发的开源AI驱动的智库式阅读器(前端部分使用Web框架),能根据用户指定的主题和偏好关键词自动从互联网上检索信息。它使用云端或本地大型模型进行总结和提供指导,并包括一个AI驱动的互动阅读伴读功能,你可以与AI讨论和交换阅读内容的想法。

这个项目我5月刚放到Github上(Github - Saga Reader),欢迎大家关注分享。‍码农‍开源不易,各位好人路过请给个小星星Star

核心技术栈:Rust + Tauri(跨平台)+ Svelte(前端)+ LLM(大语言模型集成),支持本地 / 云端双模式

关键词:端智能,边缘大模型;Tauri 2.0;桌面端安装包 < 5MB,内存占用 < 20MB。

功能架构

graph TD
A[前端架构] --> B[组件层]
A --> C[状态管理层]
A --> D[工具层]
A --> E[构建与运行时]

B --> B1[UI组件]
B --> B2[通用组件]
B1 --> B11[订阅列表组件]
B1 --> B12[阅读详情组件]
B1 --> B13[设置面板组件]
B2 --> B21[Markdown渲染组件]
B2 --> B22[上下文菜单组件]
B2 --> B23[保存操作面板组件]

C --> C1[Writable Store]
C --> C2[Derived Store]
C --> C3[Readable Store]
C1 --> C11[用户配置]
C2 --> C21[当前订阅]
C3 --> C31[加载状态]

D --> D1[国际化]
D --> D2[主题管理]
D --> D3[实用工具]
D --> D4[混合API]

E --> E1[Vite构建工具]
E --> E2[Svelte编译时优化]
E --> E3[Tauri运行时环境]

核心业务流程图

sequenceDiagram
participant Scrap as Scrap模块(Rust)
participant Intelligent as Intelligent模块(Rust)
participant Recorder as Recorder模块(Rust)
participant TauriEvent as Tauri事件
participant ReadableStore as Readable Store(前端)
participant DerivedStore as Derived Store(前端)
participant UI as 前端UI组件

Scrap->>Intelligent: 传递抓取内容
Intelligent->>Recorder: 传递AI处理结果(翻译/摘要)
Recorder->>TauriEvent: 触发"article-updated"事件<mcfile name="lib.rs" path="crates/recorder/src/lib.rs"></mcfile>
TauriEvent->>ReadableStore: 前端监听事件(listenArticleUpdate)<mcfile name="tauri-api.ts" path="app/src/lib/tauri-api.ts"></mcfile>
ReadableStore->>DerivedStore: 更新currentFeed Store
DerivedStore->>UI: 触发Svelte响应式更新($:块)<mcfile name="Reader.svelte" path="app/src/routes/Reader.svelte"></mcfile>
UI->>UI: 仅更新受影响DOM(如翻译进度条、内容区域)

运行截图

二、技术选型:为什么选择Svelte 5 + Tauri?

传统Electron+React方案在桌面应用中常见,但存在两大痛点:

  1. 性能瓶颈:Electron基于Chromium内核,内存占用普遍在50MB以上;React的虚拟DOMdiff在复杂状态变更时易产生性能损耗。
  2. 开发复杂度:React需要额外引入Redux/MobX等状态管理库,增加了学习成本和代码体积。

Svelte 5与Tauri的组合完美解决了这些问题:

  • Tauri基于Rust开发,运行时仅需MB级内存,且通过tauri-plugin-feed-api插件实现前端与Rust模块的高效通信;
  • Svelte作为编译时框架,在构建阶段将响应式逻辑转换为直接操作真实DOM的代码,避免了虚拟DOM的运行时开销,天然支持细粒度更新;
  • Svelte 5的内置状态管理方案(Svelte Store)无需额外依赖,语法简洁(如$:响应式声明),大幅降低开发复杂度。

三、状态管理实战:从多模块协作到UI更新的全链路优化

3.1 技术难点:跨模块状态同步的复杂性

Saga Reader的业务流程涉及多个模块协作:

scrap模块(内容抓取)→ intelligent模块(AI处理)→ recorder模块(存储)→ 前端UI(展示)

每个环节都需要状态同步,具体挑战包括:

  • 跨进程通信:前端(Svelte)与后端(Rust)通过Tauri插件通信,需处理异步调用的状态同步;
  • 多组件依赖:订阅列表、阅读详情、设置面板等组件需共享用户配置(如主题、语言)和内容状态(如未读计数);
  • 性能敏感:AI处理可能产生高频状态变更(如实时翻译进度),需避免不必要的UI重渲染。

3.2 Svelte 5的解决方案:Store + 响应式系统的组合拳

3.2.1 核心工具:Svelte Store的分层设计

项目采用三级Store结构管理状态:

项目采用writable(可写)→ derived(派生)→ readable(只读)三级Store结构,精准覆盖不同状态类型。以下是核心实现(参考app/src/lib/store.ts设计):

// 1. 持久化配置Store(writable)
import { writable } from 'svelte/store';
import { localStorage } from '@tauri-apps/plugin-storage'; // 初始值从本地存储加载
const loadConfig = async () => {
const config = await localStorage.get('userConfig');
return config || { theme: 'light', fontSize: 14, lang: 'zh' };
}; export const userConfig = writable({ theme: 'light', fontSize: 14, lang: 'zh' });
// 自动同步到本地存储
userConfig.subscribe(async (value) => {
await localStorage.set('userConfig', value);
await localStorage.save();
}); // 2. 派生内容状态Store(derived)
import { derived } from 'svelte/store';
import { activeFeedId } from './feedStore';
import { allFeeds } from './dataStore'; export const currentFeed = derived(
[activeFeedId, allFeeds],
([$activeFeedId, $allFeeds]) => {
return $allFeeds.find(feed => feed.id === $activeFeedId);
}
); // 3. 异步操作状态Store(readable)
import { readable } from 'svelte/store';
import { tauri } from '@tauri-apps/api'; export const loadingStatus = readable(false, (set) => {
let isLoading = false;
// 监听Rust模块的"抓取开始"事件
const onFetchStart = () => { isLoading = true; set(true); };
// 监听Rust模块的"抓取完成"事件
const onFetchEnd = () => { isLoading = false; set(false); }; tauri.listen('fetch-start', onFetchStart);
tauri.listen('fetch-end', onFetchEnd); // 清理函数
return () => {
tauri.unlisten('fetch-start', onFetchStart);
tauri.unlisten('fetch-end', onFetchEnd);
};
});

设计逻辑

  • writable Store:通过Tauri的localStorage插件实现配置持久化,订阅器自动同步变更,避免手动调用存储API;
  • derived Store:基于多个基础Store(如当前选中订阅ID和所有订阅列表)动态计算派生状态,仅当任一依赖变化时触发更新;
  • readable Store:封装Tauri事件监听逻辑,统一管理异步操作的加载状态(如内容抓取、AI处理),避免事件监听器泄漏。

3.2.2 细粒度更新:避免无效渲染的关键

Svelte的响应式系统通过$:标记自动追踪依赖,仅当依赖变量变化时更新相关代码块。例如:

Svelte的$:响应式声明通过编译时依赖分析,自动追踪变量的使用场景,仅在依赖变更时执行代码块。以app/src/routes/Reader.svelte的AI翻译进度更新为例:

<script>
import { currentFeed } from '$lib/store';
import { translateWithLLM } from '$lib/ai-service'; let translationProgress = 0;
let translatedContent = ''; // 响应式块:仅当currentFeed变化时执行
$: if ($currentFeed?.content) {
// 重置进度
translationProgress = 0;
translatedContent = '';
// 调用AI翻译(模拟异步流)
translateWithLLM($currentFeed.content).then((stream) => {
for await (const chunk of stream) {
translatedContent += chunk.text;
translationProgress = chunk.progress;
}
});
}
</script> <div class="reader-container">
<!-- 仅content变化时更新 -->
<article class="original-content">{$currentFeed?.content}</article> <!-- 仅translatedContent变化时更新 -->
<article class="translated-content">{$translatedContent}</article> <!-- 仅translationProgress变化时更新 -->
{#if translationProgress > 0}
<progress
value={translationProgress}
max="100"
class="translation-progress"
/>
{/if}
</div>

性能优势

  • 传统框架(如React)需手动通过useMemo/useCallback优化,Svelte通过编译时分析自动实现;
  • 在AI翻译的高频进度更新(50次/秒)中,仅progress组件和translated-content区域会被重新渲染,其他DOM节点保持稳定。根据docs/Introduction-of-the-solution-zh.md的实测数据,此机制使列表滚动流畅度提升30%,复杂表单提交延迟降低25%。

3.2.3 与Tauri的协同:状态同步的最后一公里

Rust的recorder模块完成内容存储后,通过Tauri事件通知前端:

// Rust端:存储完成后触发事件
pub fn save_article(article: Article) -> Result<(), Error> {
// 存储逻辑...
// 触发Tauri事件,通知前端更新
tauri::event::emit_all("article-updated", article)?;
Ok(())
}

前端通过readable Store监听事件并更新状态:

import { tauri } from '@tauri-apps/api';
import { currentFeed } from '$lib/store'; // 监听"article-updated"事件
export const listenArticleUpdate = () => {
tauri.listen('article-updated', (event) => {
const updatedArticle = event.payload as Article;
// 更新currentFeed Store(触发UI自动刷新)
currentFeed.update(feed => {
if (feed?.id === updatedArticle.feedId) {
feed.articles = feed.articles.map(art =>
art.id === updatedArticle.id ? updatedArticle : art
);
}
return feed;
});
});
};

优势

  • 避免手动调用setStatedispatch,通过Store的订阅机制自动同步;
  • 事件驱动模式解耦前后端,Rust模块无需关心前端状态结构,仅需传递基础数据。

四、性能与体验的双重提升:Svelte的"编译时优化"魔法

4.1 运行时性能:真实DOM操作 vs 虚拟DOM

Svelte在构建阶段将组件编译为直接操作真实DOM的代码,避免了React的虚拟DOM diff开销。根据docs/Introduction-of-the-solution-zh.md的对比测试:

  • 内存占用:Svelte+Tauri方案(≤10MB) vs Electron+React(≥50MB);
  • 渲染延迟:复杂列表渲染延迟从80ms降至50ms,滚动流畅度提升30%。

4.2 开发体验:简洁语法与零依赖

Svelte的状态管理无需额外库(如Redux的dispatch/reducer),通过writable/derived/readable即可完成90%的状态管理需求。开发团队反馈:"实现相同功能的代码量比React+Redux减少40%,调试状态变更的复杂度降低50%。"。

4.3 构建效率:Vite集成的极速体验

项目vite.config.ts中集成了Svelte插件,配合Vite的HMR(热更新),修改状态管理代码后,UI更新耗时仅200ms左右,极大提升了开发效率。

五、总结:Svelte 5 + Tauri,复杂应用的高效解

Saga Reader的实践证明,Svelte 5的状态管理方案在跨平台应用中具备高适应性低维护成本

  • 技术选型:Svelte的编译时优化与Tauri的轻量级运行时完美互补,适合对性能敏感的桌面应用;
  • 状态设计:三级Store体系(writable→derived→readable)可覆盖95%以上的状态类型,建议优先采用;
  • 协同优化:与Tauri的事件驱动同步机制,是解决跨进程状态一致性的高效方案。

未来可探索Svelte 5的context API优化深层组件状态传递,或结合Rust的tokio异步运行时进一步降低跨进程通信延迟。对于希望兼顾开发效率与运行性能的团队,Svelte 5+Tauri是值得优先尝试的技术组合。

Svelte 5状态管理实战:基于Tauri框架的AI阅读器Saga Reader开发实践的更多相关文章

  1. 基于 Angularjs&Node.js 云编辑器架构设计及开发实践

    基于 Angularjs&Node.js 云编辑器架构设计及开发实践 一.产品背景 二.总体架构 1. 前端架构 a.前端层次 b.核心基础模块设计 c.业务模块设计 2. Node.js端设 ...

  2. Vue3 企业级优雅实战 - 组件库框架 - 7 组件库文档的开发和构建

    该系列已更新文章: 分享一个实用的 vite + vue3 组件库脚手架工具,提升开发效率 开箱即用 yyg-cli 脚手架:快速创建 vue3 组件库和vue3 全家桶项目 Vue3 企业级优雅实战 ...

  3. 基于VUE框架 与 其他框架间的基本对比

    基于VUE框架的基本描述 与 其他框架间的基本对比 2018-11-03  11:01:14 A B React React 和 Vue 有许多相似之处,它们都有: 使用 Virtual DOM 提供 ...

  4. vue创建状态管理(vuex的store机制)

    1:为什么说要是永远状态管理 在使用 Vue 框架做单页面应用时,我们时常会遇到传值,组件公用状态的问题.(子父间传值文章传送门) ,如果是简单的应用,兄弟组件之间通信还能使用 eventBus 来作 ...

  5. Vue之状态管理(vuex)与接口调用

    Vue之状态管理(vuex)与接口调用 一,介绍与需求 1.1,介绍 1,状态管理(vuex) Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态 ...

  6. 基于ssh框架web示例

    基于ssh框架web示例 介绍 Spring Boot Web 开发非常简单,该示例包括包括目前web开发基本都需要用到的内容 - 序列化(json)输出 - 过滤器(filters) - 监视器(l ...

  7. 基于JSP的RSS阅读器的设计与实现

    阅读器访问地址:http://easyrss.tk/,欢迎体验! 阅读导览 一.    概述  二.    设计的基本概念和原理 三.    设计方案 四.    主要源代码 五.    阅读器使用说 ...

  8. 基于cookie的用户登录状态管理

    cookie是什么 先来花5分钟看完这篇文章:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies 看完上文,相信大家对cookie已经有 ...

  9. 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

    状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...

  10. 文献综述十八:基于SSH框架的进销存管理系统设计与实现

    一.基本信息 标题:基于SSH框架的进销存管理系统设计与实现 时间:2017 出版源:内蒙古科技与经济 文件分类:对框架的研究 二.研究背景 进销存管理系统在各企业中广泛应用,使用SSH框架,很大程度 ...

随机推荐

  1. JS处理数据四舍五入(tofixed与round的区别详解)

    1 .tofixed方法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字.例如将数据Num保留2位小数,则表示为:toFixed(Num):但是其四舍五入的规则与数学中的规则 ...

  2. 工作面试必备:SQL 中的各种连接 JOIN 的区别总结!

    前言 尽管大多数开发者在日常工作中经常用到Join操作,如Inner Join.Left Join.Right Join等,但在面对特定查询需求时,选择哪种Join类型以及如何使用On和Where子句 ...

  3. wordpress:nginx负载均衡+nignweb服务器+mysql数据库+nfs-lsync+rsync备份

    目录 所有知识结合,注意正式环境慎用: mariadb服务器 NFS服务器配置 web服务器配置 Nginx负载均衡 backup备份服务器配置rsync NFS服务器安装lsync进行实时同步 所有 ...

  4. CAS架构与原理简介

    1. 会话与Cookie HTTP是无状态协议,客户端与服务端之间的每次通信都是独立的,而会话机制可以让服务端鉴别每次通讯过程中的客户端是否是同一个,从而保证业务的关联性. Session是服务器使用 ...

  5. Linux下启动Oracle命令

    1.进入LInux,切换到Oracle用户权限,输入数据库密码.su - oracle    1在这里插入图片描述2.输入sqlplus "/as sysdba"    1在这里插 ...

  6. 【JVM之内存与垃圾回收篇】垃圾回收概述

    垃圾回收概述 概念 这次我们主要关注的是黄色部分,内存的分配与回收 垃圾收集 垃圾收集,不是 Java 语言的伴生产物.早在 1960 年,第一门开始使用内存动态分配和垃圾收集技术的 Lisp 语言诞 ...

  7. Condition的await()方法底层源码

    一.Condition的await()方法底层源码 以下是 ConditionObject 中 await 方法的源码及其详细分析: public final void await() throws ...

  8. Google Adsense中文设置

    1. 入口 https://www.google.com/adsense 2. 菜单 Account -> settings -> Personal settings 3. 切换语言 Di ...

  9. STM32F407——使用systick定时器裸机制作延时函数

    准备工作: 软件:keil5 硬件:STM32F407ZET6芯片,gec6818开发板,st-link调试器 文档:<开发板原理图>,<Cortex M3与M4权威指南>,& ...

  10. 【记录】Python3|将元组/列表/集合或字典解析成多个参数传入函数,以及定义可变参数函数

    [说人话版] 将元组/列表/集合或字典直接作为多个函数参数传入,只需要添加*或**即可. 样例如下. 在Python中,将元组.列表.集合或字典作为多个函数参数传入是一种非常方便的技巧,可以帮助我们在 ...