如何从零用Electron构建一个自定义Web浏览器:Cartographist源码的10个核心借鉴点
2026/8/25 8:42:48 网站建设 项目流程

如何从零用Electron构建一个自定义Web浏览器:Cartographist源码的10个核心借鉴点

【免费下载链接】cartographistexperimental web browser optimized for rabbit-holing项目地址: https://gitcode.com/gh_mirrors/ca/cartographist

Cartographist 是一个用 Electron 构建的实验性 Web 浏览器,它不依赖传统的新窗口和线性历史,而是用横向滚动面板、树状浏览记录和可保存的"Trail"会话来重新定义"深度阅读(rabbit-holing)"。读完这份 Cartographist 源码剖析,你将获得用 Electron 从零搭建自定义 Web 浏览器所需的 10 个核心借鉴点。

上面就是 Cartographist 的实际运行效果:左侧是一棵"浏览树",右侧是可以水平滚动的网页面板。整个项目不到 20 个源文件,却被作者设计成了一个功能完整的浏览器,非常适合新手学习。

1. 用 BrowserWindow + webviewTag 搭建浏览器骨架

一切从 src/main/index.js 开始。主进程只做了三件事:创建BrowserWindow、开启webviewTag、注册应用菜单。

关键在webPreferences中打开webviewTag: true。有了它,渲染进程就能在页面里嵌入<webview>标签,让每个面板独立加载网页、独立走自己的导航栈——这是"自定义浏览器"而非"套壳网页"的分水岭。

2. 隐藏原生标题栏,用 CSS 打造自定义窗口

想做出 Chrome 之外的浏览器观感,第一步是去掉系统标题栏。源码用了两个配合的技巧:

  • 主进程中设置titleBarStyle: "hiddenInset",保留 macOS 的红黄绿窗口按钮;
  • 渲染进程里用WebkitAppRegion: "drag"把顶部 38px 区域变成可拖动区域(见src/renderer/index.js)。

只需两行配置,你就拥有了完全自定义的窗口外观。

3. 把"线性历史"升级为"树状历史模型"

传统浏览器的历史是一条直线,而 Cartographist 用一棵树记录浏览轨迹:每个节点包含urltitlechildrentimestamp,前进后退只是沿着父子关系移动。

核心逻辑全部收敛在src/renderer/history.js的 5 个纯函数中:navigate(从 A 跳到 B 就挂一个子节点)、back(返回父节点)、forward(进入最后一个子节点)、getprocess。纯函数 + 纯数据,天然适合测试和持久化,这是最值得偷师的设计。

4. 新标签页?不,是横向滚动的面板

Cmd+T在新浏览器里意味着新窗口,在 Cartographist 里意味着右侧滑出一个 640px 宽的新面板(src/renderer/index.js中的panes数组 +overflow-x-scroll容器)。面板可以关闭、全屏、点击历史树上任意节点重新打开。多任务对比阅读从此告别窗口切换,这也是"rabbit-holing 优化"的核心体验。

5. 用 Immer 自定义 Hook 管理全局状态

整个浏览器——历史树、面板列表、全屏状态——只是一个 React state 对象。作者在src/renderer/use-immer.js中把useState和 Immer 的produce组合成 8 行的小 Hook,让更新逻辑写成"直接修改 draft"的直觉式写法,彻底告别冗长的不可变更新样板代码。

6. localStorage 持久化:关闭浏览器不丢进度

src/renderer/use-persisted-immer.js在前一个 Hook 之上加了 18 行:每次 state 变化写入localStorage,启动时读回。这样关闭再打开浏览器,你的浏览树和所有面板原样恢复。做个人效率型浏览器时,这种"零配置"的会话持久化几乎是必备项。

7. 主进程与渲染进程的 IPC 命令通道

浏览器功能大多由菜单触发,但菜单活在主进程、状态活在渲染进程。src/main/menu.js的模式很典型:菜单项的click回调只调用webContents.send("NEW_PANE")发一个字符串命令,渲染进程在src/renderer/index.js里用ipcRenderer.on监听并执行真正的逻辑。命令名(NEW_PANE/NEW_TRAIL/SAVE_TRAIL/LOAD_TRAIL)就是两个世界之间的契约。

8. Trail:把整次研究会话存成一个文件

Cartographist 最独特的功能是"Trail"——把当前完整状态(历史树 + 面板)序列化为 JSON 存成.trail文件,下次一键加载,研究可以跨几周进行。实现在src/renderer/index.jsSAVE_TRAIL/LOAD_TRAIL监听器里,配合md5哈希检测"未保存改动",在新建 Trail 前弹出确认框。这是把"会话即数据"做到极致的示范。

9. preload 脚本注入:读取网页里的悬停链接

用户悬停任意链接时,面板底部会浮现目标 URL 预览。实现是经典三段式:

  1. src/renderer/inject.js作为<webview>的 preload 脚本跑在网页上下文中,监听mouseover后通过ipcRenderer.sendToHost上报链接;
  2. src/renderer/webview.js监听ipc-message事件收到链接并显示;
  3. 顺带演示了new-windowdid-navigatepage-title-updated等 webview 事件的接管——所有<a target="_blank">点击都被转成"开新面板"。

这就是在不碰 Chromium 的前提下扩展网页行为的轻量方案。

10. callcc:用"异常当跳转"写极简树遍历

src/renderer/utils.js里有一个 20 行的小工具callcc:把回调传进函数,想提前退出就调用exit(arg),内部靠抛一个特殊的Exit错误实现"从任意深度瞬间返回"。配合递归traverse,树搜索、父链查找都写得像命令式代码一样直白。写自定义数据结构操作时,这个小技巧能省下大量标志位与递归传参。

结语:小项目里藏着大架构

Cartographist 用不足 20 个文件回答了"Electron 浏览器怎么造"的全部关键问题:src/main/负责窗口与菜单,src/renderer/webview.js负责页面容器,src/renderer/history.js负责数据模型,src/renderer/history-visualization.js负责用 SVG 画出浏览树。建议动手顺序:先跑npm install && npm start体验一遍,再按本文 1→10 的顺序对照源码阅读。

如果你希望在自己的分支上修改,仓库地址如下:

git clone https://gitcode.com/gh_mirrors/ca/cartographist

把一个"有趣的想法"变成可运行的浏览器,只需要一个周末和一个敢拆的开源项目。

【免费下载链接】cartographistexperimental web browser optimized for rabbit-holing项目地址: https://gitcode.com/gh_mirrors/ca/cartographist

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询