我放棄了 Vue/React,選擇自研框架

一、框架選型背景分析

1.1 前端生態的現況

當前前端開發領域呈現出「框架為王」的格局。Vue 和 React 作為兩大主流框架,佔據了市場的絕大部分份額,圍繞它們形成了龐大的生態系統:狀態管理、路由、UI 元件庫、建置工具、測試框架…… 每一層都有數十個選擇。

這種繁榮的背後,是開發者需要持續學習和維護的認知負擔。根據 npm 統計,前端領域每週發布的新套件超過 10000 個,而框架及其周邊生態的更新頻率更是令人目不暇給。

1.2 商業專案中的框架依賴困境

在企業級開發中,框架選擇往往意味著長期的技術綁定:

  • 升級成本高:從 Vue 2 遷移到 Vue 3,或從 React 17 升級到 React 18+,都需要大量的重構工作
  • 生態鎖定:一旦選擇某個框架,配套的狀態管理、路由、UI 庫等都傾向於選擇該生態下的方案
  • 客製化能力受限:當業務需求超出框架提供的能力範圍時,往往需要進行大量的 hack 或等待官方支援

1.3 決策的觸發點

促使我做出自研框架決定的直接原因,是一個實際專案中的技術挑戰:我們需要實現一個複雜的視覺化編輯器,要求極高的渲染效能和靈活的自訂渲染能力。在嘗試了 Vue、React 和 Solid 之後,發現沒有一個框架能夠同時滿足以下需求:

  1. 高頻狀態更新下的流暢渲染
  2. 可替換的渲染後端(DOM/Canvas/WebGL)
  3. 對渲染過程的細粒度控制

二、主流框架使用痛點闡述

2.1 Vue 的侷限性

Vue 3 以其優雅的響應式系統和漸進式設計著稱,但在實際使用中仍存在一些痛點:

黑盒內建元件TeleportTransition 等元件雖然功能強大,但實作細節對開發者完全不透明。當需要客製化動畫邏輯或擴充傳送門功能時,只能透過 hack 方式實現。

2.2 React 的挑戰

React 以其靈活的 JSX 語法和龐大的生態系統聞名,但同樣存在問題:

Hooks 規則的複雜性useEffectuseMemouseCallback 的依賴陣列管理是一個永恆的痛點,容易引發難以排查的 Bug。

tsx 代碼解讀複製代碼// React Hooks 的依賴管理陷阱
useEffect(() => {
  fetchData(id).then(setData)
}, [id]) // 漏寫依賴會導致閉包問題

虛擬 DOM 的效能開銷:雖然 React 18 引入了並發渲染和 Fiber 架構,但在高頻更新場景下,虛擬 DOM 的 diff 開銷依然不可忽視。

2.3 信號框架的不足

Solid、Svelte 等信號驅動框架在效能上表現出色,但它們的編譯時最佳化策略帶來了新的問題:

編譯產物不透明:編譯後的程式碼與原始碼差異巨大,除錯困難。 執行時能力受限:由於大量邏輯在編譯時完成,執行時的動態性和靈活性受到限制。

三、自研框架的技術架構設計

3.1 核心設計理念

基於對主流框架的分析,我確定了自研框架 Vitarx 的三大核心設計理念:

設計原則具體含義優勢信號級精確更新資料變化時僅更新關聯的 DOM 節點無 diff 開銷,高頻更新場景效能優異執行時視圖樹保留完整的執行時結構,支援動態操作靈活的渲染編排能力萬物皆元件所有內建元件用公開 API 構建無黑盒,可客製化,可擴充### 3.2 分層架構

Vitarx 採用分層架構設計,各層職責清晰、依賴單向:

 代碼解讀複製代碼┌─────────────────────────────────────────────┐
│                   vitarx                    │  ← 聚合包,統一導出
├────────────┬─────────────┬──────────────────┤
│runtime-dom │ runtime-ssr │                  │  ← 平台適配層
├────────────┴─────────────┼──────────────────┤
│        runtime-core      │     utils        │  ← 執行時核心 & 工具
├──────────────────────────┼──────────────────┤
│        responsive        │                  │  ← 響應式系統
└──────────────────────────┴──────────────────┘

3.3 響應式系統設計

響應式系統是 Vitarx 的核心。與 Vue 的實作不同,Vitarx 採用雙向鏈結串列依賴管理,實現了更高效的依賴追蹤和觸發:

typescript 代碼解讀複製代碼/**
 * DepLink — 信號與副作用之間的雙向鏈結串列節點
 * 每個節點同時存在於兩條鏈結串列中:
 * - Signal 鏈結串列:該信號被哪些 effect 依賴
 * - Effect 鏈結串列:該 effect 依賴了哪些信號
 */
class DepLink {
  sigPrev?: DepLink // Signal 鏈結串列中的前驅
  sigNext?: DepLink // Signal 鏈結串列中的後繼
  ePrev?: DepLink // Effect 鏈結串列中的前驅
  eNext?: DepLink // Effect 鏈結串列中的後繼

  constructor(
    public signal: Signal,
    public effect: EffectRunner
  ) {}
}

Track(依賴收集):當副作用函式執行並存取響應式資料時,建立或重用鏈結串列節點,同時掛到兩條鏈結串列上。

Trigger(觸發更新):當資料變化時,沿 Signal 鏈結串列遍歷通知所有依賴者——不需要 diff,不需要重新渲染。

3.4 視圖系統設計

Vitarx 的視圖系統基於 View 抽象層,將 JSX 轉換為輕量的視圖物件,而非虛擬 DOM:

typescript 代碼解讀複製代碼interface View {
  init(ctx: ViewContext): void // 初始化
  mount(container: HostContainer): void // 掛載到容器
  dispose(): void // 銷毀
}

這種設計使得渲染器可以完全替換——只需實作 ViewRenderer 介面:

typescript 代碼解讀複製代碼interface ViewRenderer {
  createElement(tag: string, parent: HostContainer): HostElement
  createText(text: string): HostText
  createComment(text: string): HostComment
  append(child: HostNode, parent: HostContainer): void
  remove(node: HostNode): void
  setAttribute(el: HostElement, key: string, next: unknown, prev: unknown): void
  // ... 其他方法
}

四、開發過程中的關鍵挑戰與解決方案

4.1 挑戰一:響應式系統的記憶體管理

問題:信號與 effect 之間的依賴關係如果不及時清理,會導致記憶體洩漏。

解決方案:採用版本號增量標記機制。每次 effect 重新執行時,先給所有舊依賴打上過期標記,然後重新收集依賴並更新標記,最後清理過期的依賴節點。

typescript 代碼解讀複製代碼export function trackSignal(signal: Signal): void {
  const activeEffect = currentActiveEffect
  if (!activeEffect) return

  let link = activeEffect[DEP_INDEX_MAP]?.get(signal)
  if (!link) {
    link = createDepLink(activeEffect, signal)
    activeEffect[DEP_INDEX_MAP]?.set(signal, link)
  }
  link[DEP_VERSION] = activeEffect[DEP_VERSION] // 標記為"本次仍存取"
}

4.2 挑戰二:視圖樹的高效更新

問題:在執行時維護完整的視圖樹結構,如何保證更新操作的高效性?

解決方案:引入視圖切換交易(ViewSwitchTransaction),將視圖切換操作封裝為可中斷、可回滾的交易。這使得 Transition 等元件可以在視圖切換過程中插入動畫邏輯。

typescript 代碼解讀複製代碼onViewSwitch((tx) => {
  const { prev, next } = tx

  tx.stopPropagation()

  // 先執行離開動畫
  runTransition(prev.node, 'leave', props, () => {
    tx.commit() // 提交切換
    // 再執行進入動畫
    runTransition(next.node, 'enter', props)
  })
})

4.3 挑戰三:JSX 執行時的自訂

問題:如何讓 Vitarx 使用自己的 JSX 編譯鏈路,而不依賴 React 的 jsx-runtime?

解決方案:透過 @vitarx/plugin-vite 插件配合 Vite 實現自訂 JSX 編譯。該插件的核心工作流程是:

  1. 保留 JSX 不轉譯:設定 esbuild/oxc 的 jsx: 'preserve',讓建置工具不處理 JSX
  2. 自訂轉換管道:在 transform 鉤子中實作 JSX → createView 的轉換邏輯
  3. 編譯巨集支援:內建對 v-ifv-elsev-modelv-show 等指令的處理
  4. 巨集元件支援:內建對 SwitchIfBlock 等元件的編譯最佳化
  5. HMR 注入:開發模式下自動注入熱更新相關程式碼

使用方式只需在 vite.config.ts 中引入插件:

typescript 代碼解讀複製代碼// vite.config.ts
import vitarx from '@vitarx/plugin-vite'

export default defineConfig({
  plugins: [vitarx()]
})

與 React jsx-runtime 的本質差異:React 的 jsx-runtime 在執行時執行 createElement 呼叫;而 Vitarx 在建置時透過 Vite 插件將 JSX 編譯為 createView 呼叫,執行時無需任何額外的 JSX 轉換開銷。

4.4 挑戰四:伺服器端渲染的實作

問題:如何實作伺服器端渲染和客戶端水合?

解決方案:實作 renderToStringrenderToStream 兩個核心 API,同時在客戶端實作增量水合——只更新變化的部分。

typescript 代碼解讀複製代碼import { renderToString, renderToStream } from '@vitarx/runtime-ssr'

// 字串渲染
const html = await renderToString(App)

// 串流渲染
const stream = await renderToStream(App)

五、效能對比測試資料

5.1 測試環境

  • 測試工具:JS Framework Benchmark
  • 測試環境:Chrome 120, macOS 14.0
  • 測試項目:1000 行資料表格,包含建立、更新、選取、交換、刪除等操作

5.2 測試結果

執行時間對比(毫秒)

測試場景Vue 3.6React 19Vitarx 4.0建立1000行81.982.4112.2更新1000行88.284.8122.4部分更新(每10行)19.111.29.2選取行高亮9.85.84.5交換兩行10.767.610.4刪除一行20.918.117.2#### 記憶體配置對比(MB)

測試場景Vue 3.6React 19Vitarx 4.0初始記憶體0.861.180.83執行記憶體3.774.425.46建立/清理後1.231.971.16#### 傳輸大小對比

項目Vue 3.6React 19Vitarx 4.0未壓縮63.7 KB180.3 KB57.8 KB壓縮後22.9 KB51.4 KB17.0 KB六、未來框架迭代規劃

6.1 短期目標(2026 - 2027年)

目標具體內容優先級生態完善推出官方 UI 元件庫 設計系統高VitaStack全端開發框架高工具鏈開發瀏覽器除錯插件高文件升級完善官方文件和教程中狀態管理開發官方狀態管理庫中### 6.2 中期目標

目標具體內容優先級跨平台支援推出行動端和桌面端支援中效能最佳化進一步最佳化首屏載入和渲染效能高### 6.3 長期目標

目標具體內容優先級AI 整合探索 AI 輔助開發能力低WebAssembly關鍵路徑的 WASM 最佳化低社群建設建立活躍的開發者社群中七、總結與反思

7.1 自研框架的價值

自研框架帶給我的不僅僅是一個可用的工具,更重要的是:

  1. 深度理解:對前端框架的底層原理有了前所未有的理解
  2. 技術掌控:不再被框架的 API 變動綁架,能夠自主掌控技術路線
  3. 創新能力:累積了從 0 到 1 建構複雜系統的經驗

7.2 適合自研框架的場景

自研框架並非適合所有專案。以下場景更適合考慮自研:

  • 對效能有極致要求的專案
  • 需要深度客製化框架行為的專案
  • 追求架構透明度的團隊
  • 有足夠技術能力和時間投入的團隊

7.3 給其他開發者的建議

如果你也在考慮自研框架,我的建議是:

  1. 明確目標:清楚自己想要解決什麼問題,不要為了造輪子而造輪子
  2. 從簡單開始:先實作最小可用版本,再逐步完善
  3. 保持開放:參考優秀框架的設計,但要有自己的思考
  4. 持續迭代:框架是活的,需要不斷最佳化和改進

寫在最後:Vitarx 是我兩年技術探索的結晶,它可能不是最完美的框架,但它是一個「看得見、摸得著」的框架。如果你也對前端框架的底層原理感興趣,歡迎加入我們的交流群一起探討。


原文出處:https://juejin.cn/post/7655009852205187078


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝13   💬2   ❤️1
572
🥈
我愛JS
📝1   ❤️1
71
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登