Vue?Vapor的實現(xiàn)原理舉例詳解(原來這么簡單)
前言
我們了解了 SolidJS 的基礎(chǔ)實現(xiàn)原理之后,再去探索實現(xiàn) Vue Vapor 就比較容易了,在 Vue3 中數(shù)據(jù)響應(yīng)式部分是獨立成了一個庫,我們可以直接使用 @vue/reactivity 這個庫響應(yīng)式,然后再去實現(xiàn)運行時部分。為了方便開發(fā),我們這里先搭建一個 Vite 的開發(fā)環(huán)境。
手動搭建一個 vite 開發(fā)環(huán)境
首先創(chuàng)建一個項目目錄 vue-vapor-cobyte-test,然后進到目錄里面進行 npm 項目的初始化:
npm init -y
然后安裝 vite 和 @vue/reactivity:
pnpm add vite @vue/reactivity
然后修改 package.json 文件的 scripts 選項設(shè)置 vite 的啟動命令:
"scripts": {
+ "dev": "vite"
},在開發(fā)期間 Vite 是一個服務(wù)器,所以我們需要創(chuàng)建 vite 的啟動入口文件 index.html,內(nèi)容如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Vapor</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>然后我們創(chuàng)建一個 src 目錄,在 src 根目錄創(chuàng)建一個 main.js 文件,內(nèi)容如下:
const root = document.getElementById('app')
root.textContent = '掘金簽約作者:Cobyte'
到此我們基礎(chǔ)的 Vite 開發(fā)環(huán)境就搭建完成了,我們通過命令來啟動它:
pnpm dev
頁面渲染結(jié)果如下:

我們可以看到已經(jīng)成功啟動了我們的項目,接下來我們就在這個 Vite 的開發(fā)環(huán)境中探索 Vue Vapor 的實現(xiàn)原理吧。
Vue Vapor 的核心原理實現(xiàn)
任何復(fù)雜的框架,只有通過最簡單的代碼去實現(xiàn)它的基本運行邏輯,才算是真正掌握了它實現(xiàn)原理。所謂高端的技術(shù)往往采用最原始的編程方式,還有那一句名言:Atwood 定律:Any application that can be written in JavaScript, will eventually be written in JavaScript. (翻譯過來即是:凡是能用 JavaScript 寫出來的,最終都會用 JavaScript 寫出來)。
我們通過前面對 SolidJS 的實現(xiàn)原理的探索,在此基礎(chǔ)之上,我們再去實現(xiàn) Vue Vapor 則比較容易了,就像我們了解了 Vue 的數(shù)據(jù)響應(yīng)式原理之后,再去了解 Mobx 的實現(xiàn)原理也變得簡單一樣,因為從宏觀角度來看它們背后的實現(xiàn)原理是相似的,甚至可以說是一樣的。
首先我們可以把 SolidJS 中的模板生成函數(shù)直接拿過來。
function template(html) {
let node
const create = () => {
const t = document.createElement("template")
t.innerHTML = html
return t.content.firstChild
}
const fn = () => (node || (node = create())).cloneNode(true)
return fn
}
我們之前寫的 SolidJS 的組件如下:

接著我們像 SolidJS 那樣寫一個組件,同時響應(yīng)式變量我們則通過 @vue/reactivity 來生成:
// 生成創(chuàng)建 button 標(biāo)簽的函數(shù)
const _tmpl$ = template('<button></button>')
const App = () => {
const count = ref(0)
// 真正進行創(chuàng)建模板內(nèi)容的地方
const el = _tmpl$()
el.addEventListener('click', () => {
count.value ++
})
insert(el, count)
return el
}
接著我們把 render 和 insert 方法也拿過來,我們之前寫的如下:

現(xiàn)在我們需要對 insert 方法進行改造一下,使用 @vue/reactivity 的 effect 函數(shù)實現(xiàn)依賴收集。
function render(component, parent) {
parent.appendChild(component)
}
function insert(parent, accessor) {
effect(() => {
parent.textContent = accessor.value
})
}
這樣我們就可以在實現(xiàn)無虛擬DOM的應(yīng)用了,并且可以自動更新。
const root = document.getElementById('app')
render(App(), root)
渲染結(jié)果如下:

完整代碼如下:
import { ref, effect } from '@vue/reactivity'
function template(html) {
let node
const create = () => {
const t = document.createElement("template")
t.innerHTML = html
return t.content.firstChild
}
const fn = () => (node || (node = create())).cloneNode(true)
return fn
}
function render(component, parent) {
parent.appendChild(component)
}
function insert(parent, accessor) {
effect(() => {
parent.textContent = accessor.value
})
}
// 生成創(chuàng)建 button 標(biāo)簽的函數(shù)
const _tmpl$ = template('<button></button>')
const App = () => {
const count = ref(0)
// 真正進行創(chuàng)建模板內(nèi)容的地方
const el = _tmpl$()
el.addEventListener('click', () => {
count.value++
})
insert(el, count)
return el
}
const root = document.getElementById('app')
render(App(), root)
Vue Vapor 設(shè)計原理
我們知道 Vue Vapor 是在 Vue3 的設(shè)計基礎(chǔ)上實現(xiàn)的一個無虛擬 DOM 的版本,那么也就是說它應(yīng)該是需要兼容目前 Vue3 的語法的,不然就又是一個新框架了。我們在上述實現(xiàn)的小 demo 是在 SolidJS 加 Vue3 的響應(yīng)式庫實現(xiàn)的,總體風(fēng)格是 SolidJS 的設(shè)計風(fēng)格,很明顯上述的實現(xiàn)是不兼容目前 Vue3 的語法的,所以我們還需要進行迭代我們的功能。
那么我們要兼容 Vue3 的語法,首先需要修改的就是我們的 render 函數(shù)的調(diào)用方式,我們目前所實現(xiàn)的 App 組件是一個函數(shù)組件,在我們原來 Vue3 中,render 函數(shù)第一個參數(shù)則是一個對象或者一個函數(shù),因為 Vue3 中的組件在數(shù)據(jù)類型上進行區(qū)分,就只有兩種,一種是虛擬DOM 對象(也就是我們平時所寫的 .vue 文件最后會被編譯成一個對象),一種則是函數(shù)(也就是組件函數(shù))。
調(diào)用方式修改如下:
- render(App(), root) + render(App, root)
接著我們修改 render 函數(shù):
function render(comp, container) {
const render = typeof comp === 'function' ? comp : comp.render
const block = render()
insert(block, container)
}
如果是組件函數(shù)則組件函數(shù)本身就是 render 渲染函數(shù),否則就是組件對象上的 render 函數(shù)。得到 render 渲染函數(shù)后,執(zhí)行渲染函數(shù)得到真實 DOM,然后掛載到對應(yīng)的根節(jié)點上。
insert 函數(shù)的修改則如下:
function insert(block, parent, anchor = null) {
parent.insertBefore(block, anchor)
}
在 SolidJS 中,依賴收集的副作用函數(shù)是放在 insert 函數(shù)中,我們也可以放在編譯后的組件函數(shù)中,所以我們的對編譯后的組件修改如下:
const App = () => {
// 生成創(chuàng)建 button 標(biāo)簽的函數(shù)
const _tmpl$ = template('<button></button>')
const count = ref(0)
// 真正進行創(chuàng)建模板內(nèi)容的地方
const el = _tmpl$()
el.addEventListener('click', () => {
count.value ++
})
effect(() => {
el.textContent = count.value
})
return el
}
渲染結(jié)果如下:

完整代碼如下:
import { ref, effect } from '@vue/reactivity'
function template(html) {
let node
const create = () => {
const t = document.createElement("template")
t.innerHTML = html
return t.content.firstChild
}
const fn = () => (node || (node = create())).cloneNode(true)
return fn
}
function render(comp, container) {
const render = typeof comp === 'function' ? comp : comp.render
const block = render()
insert(block, container)
}
function insert(block, parent, anchor) {
parent.insertBefore(block, anchor)
}
const App = () => {
const _tmpl$ = template('<button></button>')
const count = ref(0)
const el = _tmpl$()
el.addEventListener('click', () => {
count.value++
})
effect(() => {
el.textContent = count.value
})
return el
}
const root = document.getElementById('app')
render(App, root)
我們可以看到 Vue Vapor 核心實現(xiàn)的代碼 50 行都不到。
總結(jié)
本文從零開始搭建了一個基于 Vite 的開發(fā)環(huán)境,并借助 @vue/reactivity 響應(yīng)式庫,成功實現(xiàn)了一個極簡版的 Vue Vapor 運行時核心。通過對比 SolidJS 的實現(xiàn)思路,我們看到了無虛擬 DOM 框架的基本模式:模板編譯生成 DOM 元素、利用響應(yīng)式系統(tǒng)的 effect 自動追蹤依賴并直接更新 DOM 內(nèi)容。整個核心代碼僅 50 行左右,卻完整展現(xiàn)了 Vue Vapor 的設(shè)計精髓——拋棄虛擬 DOM,回歸原生 DOM 操作,同時保持與 Vue3 組件語法的兼容性。
關(guān)鍵實現(xiàn)步驟包括:
- 使用
template函數(shù)靜態(tài)克隆 DOM 節(jié)點,提升創(chuàng)建效率; - 通過
ref定義響應(yīng)式狀態(tài),利用effect建立數(shù)據(jù)與視圖的綁定; - 組件函數(shù)直接返回真實 DOM 節(jié)點,由
render函數(shù)完成掛載; - 事件監(jiān)聽直接綁定在原生元素上,無需額外代理。
這一實現(xiàn)不僅驗證了 “任何能用 JavaScript 寫出的最終都會用 JavaScript 寫出” 的理念,也為深入理解 Vue Vapor 后續(xù)的指令系統(tǒng)、組件化、調(diào)度更新等進階特性奠定了扎實的基礎(chǔ)。無虛擬 DOM 的路徑在性能敏感場景下具有天然優(yōu)勢,Vue Vapor 正是 Vue 生態(tài)對這一方向的積極探索。
到此這篇關(guān)于Vue Vapor的實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue Vapor實現(xiàn)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實現(xiàn)加載頁面自動觸發(fā)函數(shù)(及異步獲取數(shù)據(jù))
這篇文章主要介紹了vue實現(xiàn)加載頁面自動觸發(fā)函數(shù)(及異步獲取數(shù)據(jù)),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
基于vue實現(xiàn)滾動條滾動到指定位置對應(yīng)位置數(shù)字進行tween特效
這篇文章主要介紹了基于vue實現(xiàn)滾動條滾動到指定位置對應(yīng)位置數(shù)字進行tween特效,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
vue文件上傳Required request part ‘file‘ is&n
這篇文章主要介紹了vue文件上傳Required request part ‘file‘ is not present問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
在Vue3中使用CSS Modules實現(xiàn)樣式隔離
隨著構(gòu)建現(xiàn)代前端應(yīng)用的需要,樣式的管理和隔離變得越來越重要,為了解決樣式?jīng)_突和管理困難的問題,CSS Modules 應(yīng)運而生,本文我們將討論如何在 Vue3 中使用 CSS Modules 實現(xiàn)樣式隔離,需要的朋友可以參考下2024-09-09
Vue3+Element?Plus實現(xiàn)動態(tài)標(biāo)簽頁以及右鍵菜單功能
這篇文章主要給大家介紹了關(guān)于Vue3+Element?Plus實現(xiàn)動態(tài)標(biāo)簽頁以及右鍵菜單功能的相關(guān)資料,Vue?3和Element?Plus提供了一種簡單的方法來實現(xiàn)側(cè)邊菜單欄與標(biāo)簽頁之間的聯(lián)動,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09

