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

8.6 KiB
Raw Blame History

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.tsVue 应用挂载,参考 src/renderer.ts:35-45
  • 现有 IPC 控制器:src/controller/changeWindowSize.js(窗口操作)

高性能技术方案

  • 选型:使用 BrowserView 每个标签一个 BrowserView,由主进程统一管理。
    • 原因:BrowserView 与主窗口同进程但独立渲染API 完整(webContents 导航、生命周期事件),相较 <webview> 更易管控、性能与兼容性更好。
  • 视图复用与生命周期:
    • 活跃标签:将其 BrowserView attach 到主窗口非活跃标签detach保留引用与状态避免多视图同时渲染占用资源。
    • 限制最大同时 attach 数量(默认 1保障 GPU/CPU 负载稳定。
  • 会话与隔离:
    • 默认共享 sessionpartition: '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-storeJSON轻量、跨平台高并发或大数据可切换 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-updatedtitle/url/loadingtab-createdtab-closedtab-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:显示标签列表,支持新建/切换/关闭、拖拽排序(后续)
    • AddressBarURL 输入与状态展示加载中、锁标识、HTTPS
    • Controls:后退/前进/刷新/新建标签按钮
    • BookmarksPane:收藏夹入口(侧栏或菜单)
    • PluginsMenu:插件入口(菜单或面板)
  • 路由:/browser 作为应用主界面;初始打开一个空白或主页标签。

IPC 通道与类型(示例)

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: truesandbox: truenodeIntegration: false(已在 src/main.ts:20-26
  • 外链统一 shell.openExternal + 白名单(已在 src/main.ts:33-43
  • 严格 CSP 保留于渲染层 index.htmlBrowserView 加载外域不受该 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.tssrc/main/ipc.tssrc/main/bookmark-store.tssrc/main/plugin-host.ts;在 src/main.ts 初始化与注册。
  • 预加载:扩展 window.api,新增 tabsbookmarksplugins 命名空间。
  • 渲染层:新增 /browser 页面与组件,替换应用首页或通过路由进入。

决策点(需确认)

  1. 标签视图方案:仅 BrowserView(推荐),允许 <webview> 兼容模式
  2. 最大标签数量与释放策略:默认 20接受LRU 释放
  3. 会话策略:全部共享 session ,支持隐私标签独立 partition
  4. 书签存储:默认 electron-store ,暂不考虑 SQLitebetter-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 文件)与插件钩子框架。