在 Web 应用性能优化的讨论中,Linear 常被当作标杆提及。当传统 CRUD 应用更新一个 issue 需要约 300ms 时,Linear 仅需几毫秒。这种差异并非来自某个单一的技术选型,而是一整套从架构设计到细节打磨的系统性工程。本文将从技术实现层面拆解 Linear 的性能策略,提炼出可落地的优化思路。
本地优先:把数据库搬进浏览器
Linear 性能架构的基石是一个反直觉的设计决策:将数据库搬到浏览器端。与传统 Web 应用 "点击→HTTP 请求→服务器查询→返回→重绘" 的循环不同,Linear 将 IndexedDB 作为 UI 的直接数据源,配合 MobX 的 observable 机制构建本地状态管理。
这种架构的核心优势在于消除网络延迟对 UI 响应的影响。当用户修改 issue 标题时,代码执行路径是:issue.title = "新标题" 同步更新内存中的 MobX observable,UI 立即重绘;issue.save() 则将变更写入 IndexedDB 的事务队列,并在后台异步同步到服务器。整个过程没有 loading spinner,因为 UI 不等待网络请求完成。
Linear 联合创始人 Tuomas 在 2024 年的一次会议演讲中透露,他们写的第一行代码就是 sync engine—— 这在创业初期极为罕见。这种 "本地优先" 的架构选择意味着用户感知到的速度完全脱离网络状况,即使在弱网环境下也能保持流畅的交互体验。
对于大多数应用而言,完全自建 sync engine 成本过高,但 ** 乐观更新(Optimistic Updates)** 的策略可以借鉴。使用 TanStack Query 或 SWR 等库,在发起网络请求前先更新本地状态,待服务器确认后再做最终同步。这种 "先假设成功,后台验证" 的模式能显著改善交互感知速度。
首屏加载:从构建到缓存的全链路优化
本地优先架构解决了运行时的性能问题,但首屏加载仍是 CSR(Client-Side Rendering)应用必须面对的挑战。Linear 的优化策略贯穿构建、传输、缓存三个层面。
构建阶段:极致的代码分割
Linear 的构建工具经历了 Parcel → Rollup → Vite → Rolldown 的演进,每次迁移都围绕同一个目标:减少传输体积。通过仅支持现代浏览器(esnext 目标,无 polyfill)、激进的死代码消除和细粒度代码分割,他们实现了:
- 代码体积减少 50%
- 压缩后体积减少 30%
- 冷缓存页面加载速度提升 10-30%
- 首屏绘制时间降低 59%(Safari 环境)
- 内存使用降低 70-80%
Linear 的代码分割策略值得借鉴:每个 npm 包独立成 chunk,而非打包成单一的 vendor.js。这意味着更新一个依赖不会使整个缓存失效,只有被修改的包需要重新下载。
传输阶段:并行预加载
代码分割带来的问题是模块依赖形成瀑布流请求。Linear 的解决方案是在 HTML 的 <head> 中注入 modulepreload 链接,让浏览器在解析 entry script 前就并行获取所有依赖 chunk。配合 crossorigin 属性确保 preload 缓存能被后续 import 复用,避免了重复请求。
缓存阶段:Service Worker 预缓存
Linear 的 Service Worker 维护着约 1200 个资源的预缓存清单,涵盖路由 chunk、图标和字体。用户首次访问登录页后,后台会在几秒内拉取完整的应用代码。后续导航完全绕过网络,从 Service Worker 直接响应。结合本地优先的 sync engine,Linear 实现了真正的离线可用 —— 用户可以创建、编辑 issue,所有操作在本地排队,待网络恢复后自动同步。
细粒度渲染:Observable 架构的级联控制
即使数据已在本地,频繁的 UI 更新仍可能引发性能问题。Linear 的解决方案是细粒度的 observable 订阅。
在 Linear 的 MobX 架构中,每个模型的每个属性都是独立的 observable,每个读取这些属性的组件都用 observer() 包裹。当服务器返回一个变更 delta 时,只有真正依赖该字段的组件会重渲染。一个包含 50 个 issue 的列表更新,触发的是 50 个 cell 的独立重渲染,而非整个列表的级联更新。
这种设计在多人协作场景下尤为重要。当十个用户同时编辑不同 issue 时,每个客户端接收到的更新只影响对应的 UI 单元,不会因为他人的操作导致整个界面卡顿。成本与变更范围成正比,而非与屏幕内容总量相关。
设计层面的速度:减少路径,控制动画
性能不仅是工程问题,也是设计问题。Linear 在交互设计层面的优化同样关键。
键盘优先的输入模型
Linear 为几乎每个常用操作提供了快捷键:单字母编辑当前 issue,双字母组合导航,修饰键触发全局操作。命令面板(⌘K)始终一键可达,搜索的是本地 MobX 对象池而非服务器,响应瞬时完成。这种设计将 "思考→操作" 的路径压缩到最短。
动画的克制使用
Linear 的动画策略极其克制:仅使用 transform 和 opacity 这两个合成层属性,完全避免 width、height、margin 等会触发 layout 的属性。动画时长也控制在 100ms 以内,进入瞬间完成,退出时 150ms 淡出 —— 这种非对称设计符合 "召唤即时,解散从容" 的认知预期。
可落地的优化参数清单
基于 Linear 的实践,以下是可立即应用的性能优化参数:
构建配置
- Bundler target:
esnext(放弃 ES5 和 polyfill) - Code splitting: 按 npm 包级别分割,独立缓存
- CSS minify: 使用 lightningcss 替代传统压缩器
加载优化
- 关键 CSS 内联到 HTML
<head> - 字体 preload 必须匹配 CSS 中的
crossorigin设置 - 使用
modulepreload并行加载依赖 chunk - Service Worker 预缓存路由级 chunk
运行时架构
- 乐观更新:本地状态先变,后台同步验证
- 细粒度订阅:避免级联重渲染
- IndexedDB 作为本地状态持久层
动画约束
- 仅动画
transform和opacity - 动画时长控制在 100ms 以内
- 避免
all过渡,明确指定过渡属性
Linear 的性能不是银弹的结果,而是数百个正确决策的累积。从第一天就将 sync engine 作为核心架构,在构建阶段极致优化代码体积,在运行时通过细粒度订阅控制渲染成本,在交互设计中减少操作路径 —— 这些决策共同构成了 "几毫秒响应" 的技术基础。
参考来源
- Dennis Brotzky, "How's Linear so fast? A technical breakdown", performance.dev, 2026-05-03
- Tuomas Artman, Linear 联合创始人会议演讲,2024
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。