Files
zn-ai/src/renderer/views/browser/README.md
2025-11-22 21:17:40 +08:00

149 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Electron 多标签浏览器实现计划(高性能方案)
## 目标
- 在现有 Electron + Vue + TypeScript + TailwindCSS 项目中实现类似 Chrome 的多标签页浏览体验:创建、切换、关闭、前进、后退、刷新、地址输入、拓展插件入口、收藏入口。
- 采用高性能与安全优先的架构:主进程 `BrowserView` 管理;渲染层仅通过受控 APIIPC 类型明确;持久化与插件机制可扩展。
## 当前项目结构与集成点
- 主进程入口:`src/main.ts`(窗口创建与 IPC 注册,参考 `src/main.ts:11-57`
- 预加载:`src/preload.ts`(已暴露 `window.api`,参考 `src/preload.ts:7-17`
- 渲染层入口:`index.html` + `src/renderer.ts`Vue 应用挂载,参考 `src/renderer.ts:35-45`
- 现有 IPC 控制器:`src/controller/changeWindowSize.js`(窗口操作)
## 高性能技术方案
- 选型:使用 `BrowserView` 每个标签一个 `BrowserView`,由主进程统一管理。
- 原因:`BrowserView` 与主窗口同进程但独立渲染API 完整(`webContents` 导航、生命周期事件),相较 `<webview>` 更易管控、性能与兼容性更好。
- 视图复用与生命周期:
- 活跃标签:将其 `BrowserView` attach 到主窗口非活跃标签detach保留引用与状态避免多视图同时渲染占用资源。
- 限制最大同时 attach 数量(默认 1保障 GPU/CPU 负载稳定。
- 会话与隔离:
- 默认共享 `session``partition: 'persist:main'`),减少多会话开销。
- 可按需支持隔离会话(隐私标签):`partition: 'tab:<id>'`
- 导航性能与安全:
- 导航统一通过主进程 `view.webContents.loadURL(url)`;前进/后退/刷新使用 `goBack/goForward/reload`
- 外链打开统一通过 `shell.openExternal(url)`,白名单校验(已存在 `external-open` 通道,参考 `src/main.ts:33-43`)。
- 地址栏与状态:
- 渲染层地址栏仅发起 IPC主进程执行导航并广播当前标签的 `url/title/loading/historyState`
- 书签与持久化:
- 存储方案优先使用 `electron-store`JSON轻量、跨平台高并发或大数据可切换 `better-sqlite3`
- 插件入口:
- 设计内部插件机制(非 Chrome 扩展):定义 `onTabCreated/onNavigate/beforeLoad/afterLoad` 等钩子;插件注册于主进程。
- Chrome 扩展仅作为可选(`session.loadExtension`),受限于兼容性与审核,不作为默认方案。
## 模块设计
### 主进程(`src/main/`
- `TabManager`:管理 `BrowserView` 生命周期与状态
- 方法:`create(url?)``switch(tabId)``close(tabId)``navigate(tabId, url)``reload(tabId)``goBack(tabId)``goForward(tabId)``list()`
- 事件:`tab-updated`title/url/loading`tab-created``tab-closed``tab-switched`
- `IPCRegistry`:集中注册通道
- `tab:create | tab:switch | tab:close | tab:navigate | tab:reload | tab:back | tab:forward | tab:list`
- `bookmark:add | bookmark:remove | bookmark:list | bookmark:folders`
- `plugin:invoke(hook, payload)`(内部插件钩子转发)
- `BookmarkStore`:封装 `electron-store` 或 SQLite可插拔
- `PluginHost`:插件注册与钩子执行(有序、可熔断)
### 预加载(`src/preload.ts`
- 扩展现有 `window.api`
- `tabs.*`:与上述 IPC 一一对应,统一 `invoke` 调用;`tabs.on(event, handler)` 用于订阅主进程广播(通过 `ipcRenderer.on` 包装)。
- `bookmarks.*`:增删改查接口。
- `plugins.invoke(hook, payload)`:触发插件钩子。
### 渲染层Vue
- 布局页面:`BrowserLayout`(地址栏、标签条、控制区、书签/插件入口)
- 组件:
- `TabBar`:显示标签列表,支持新建/切换/关闭、拖拽排序(后续)
- `AddressBar`URL 输入与状态展示加载中、锁标识、HTTPS
- `Controls`:后退/前进/刷新/新建标签按钮
- `BookmarksPane`:收藏夹入口(侧栏或菜单)
- `PluginsMenu`:插件入口(菜单或面板)
- 路由:`/browser` 作为应用主界面;初始打开一个空白或主页标签。
## IPC 通道与类型(示例)
```ts
type TabId = string;
interface TabInfo { id: TabId; url: string; title: string; isLoading: boolean; canGoBack: boolean; canGoForward: boolean }
// 请求
tab:create(url?: string) => TabInfo
tab:switch(tabId: TabId) => void
tab:close(tabId: TabId) => void
tab:navigate({ tabId, url }: { tabId: TabId, url: string }) => void
tab:reload(tabId: TabId) => void
tab:back(tabId: TabId) => void
tab:forward(tabId: TabId) => void
tab:list() => TabInfo[]
// 广播
tab-updated: TabInfo
tab-created: TabInfo
tab-closed: { tabId: TabId }
tab-switched: { tabId: TabId }
```
## 数据模型
- `Tab``{ id, view, url, title, isLoading, createdAt }`
- `Bookmark``{ id, title, url, folderId?, createdAt }`
- `Folder``{ id, name, parentId? }`
- `Plugin``{ id, name, hooks }`
## 性能优化要点
- 仅 attach 当前活跃 `BrowserView` 到窗口;对非活跃标签 `detach` 保留状态。
- 控制最大标签数(例如 20超过时启用 LRU 释放策略(可配置)。
- 启用硬件加速,保持默认;避免禁用 GPU。
- 监听 `did-stop-loading`/`did-finish-load` 更新状态,减少渲染层轮询。
- 对地址栏与状态广播使用节流(例如 100ms
- 持久化异步批量写入(书签、会话恢复)。
## 安全策略
- 保持 `contextIsolation: true``sandbox: true``nodeIntegration: false`(已在 `src/main.ts:20-26`
- 外链统一 `shell.openExternal` + 白名单(已在 `src/main.ts:33-43`
- 严格 CSP 保留于渲染层 `index.html``BrowserView` 加载外域不受该 CSP 限制,但需根据业务限制域名。
## 迭代计划(三阶段)
1. 核心能力(主进程 + IPC + 预加载)
- 实现 `TabManager` 与全部导航方法
- 注册 IPCtabs/bookmarks/plugins与广播
- 预加载扩展 `window.ipcAPI.tabs/*`、事件订阅封装
2. 渲染层界面
- 新建 `BrowserLayout` 与基础组件TabBar、AddressBar、Controls
- 打通地址栏与导航;同步标题与加载状态
- 书签入口基础增删与列表展示
3. 插件与高级能力
- `PluginHost` 与钩子机制;内置示例插件(如:拦截导航统计)
- 标签拖拽排序、会话恢复、快捷键Ctrl/Cmd+T/W、Ctrl/Cmd+L、Ctrl/Cmd+R、Alt+←/→)
## 集成步骤(细化)
- 主进程:新增 `src/main/tab-manager.ts``src/main/ipc.ts``src/main/bookmark-store.ts``src/main/plugin-host.ts`;在 `src/main.ts` 初始化与注册。
- 预加载:扩展 `window.api`,新增 `tabs``bookmarks``plugins` 命名空间。
- 渲染层:新增 `/browser` 页面与组件,替换应用首页或通过路由进入。
## 决策点(需确认)
1. 标签视图方案:仅 `BrowserView`(推荐),允许 `<webview>` 兼容模式
2. 最大标签数量与释放策略:默认 20接受LRU 释放
3. 会话策略:全部共享 `session` ,支持隐私标签独立 `partition`
4. 书签存储:默认 `electron-store` ,暂不考虑 SQLite`better-sqlite3`
5. 插件能力边界:需要支持加载 Chrome 扩展(`session.loadExtension`
6. 首页与地址栏行为:默认主页 URL、空白页`about:blank`),可自定义欢迎页
7. 路由集成:将 `/browser` 作为默认首页,保留现有 `/about` 等页面
## 验收与测试
- 开发启动:`npm run start`,验证创建/切换/关闭/导航/刷新基本路径
- 事件广播:在 Vue 中订阅 `tab-updated`,确保标题/加载进度实时更新
- 书签:新增/删除/持久化验证与重启恢复
- 性能:在 10+ 标签场景观察 CPU/GPU 占用与 UI 响应
- 安全:尝试非法域名导航,验证白名单拦截与错误提示
---
后续我将基于以上计划逐步实现。在“决策点”中的问题请先确认,我将据此微调实现(例如是否支持隐私标签、是否引入 SQLite、是否兼容 `<webview>`)。
**待反馈决策**
- 顶部 UI 高度与 `BrowserView` 边界
- 当前使用固定偏移 `64px` 对齐顶部 UI。是否改为渲染层动态上报高度例如在布局变化时通过 IPC 设置),以适配不同分辨率与主题?
- Chrome 扩展加载
- 文档中确认“需要支持加载 Chrome 扩展”。建议作为后续阶段实现,采用 `session.loadExtension` 并隔离到特定 `partition`,以减少对主会话的影响;请确认是否需要默认加载的扩展清单或仅提供入口。
如果以上决策点确认,我将继续第二阶段:完善 UI 高度动态设置与扩展加载入口,同时补充书签持久化(基于 `userData` 目录 JSON 文件)与插件钩子框架。