vue3模塊創(chuàng)建runtime-dom源碼解析
前言
runtime-dom 是針對(duì)瀏覽器的運(yùn)行時(shí),包括 DOM 操作、props(例如class、事件、樣式以及其它attributes)的更新等內(nèi)容;本小節(jié)我們開(kāi)啟 runtime-dom 的篇章。
創(chuàng)建模塊
在 packages/runtime-dom/ 目錄下創(chuàng)建目錄文件:
│ │ └─ src │ │ ├─ index.ts │ │ ├─ modules │ │ │ ├─ attr.ts // attributes 的更新方法 │ │ │ ├─ class.ts // class 的更新 │ │ │ ├─ event.ts // 事件綁定的更新 │ │ │ └─ style.ts // style屬性的更新 │ │ ├─ nodeOps.ts // dom操作方法 │ │ └─ patchProp.ts // 屬性更新操作
創(chuàng)建 runtime-dom/package.json 文件:
{
"name": "@vue/runtime-dom",
"version": "1.0.0",
"main": "index.js",
"module": "dist/runtime-dom.esm-bundler.js",
"unpkg": "dist/runtime-dom.global.js",
"buildOptions": {
"name": "VueRuntimeDOM",
"formats": [
"esm-bundler",
"cjs",
"global"
]
}
}
nodeOptions
先創(chuàng)建一些操作 DOM 的方法,例如元素和文本的增刪改查:
// runtime-dom/src/nodeOps.ts
export const nodeOps = {
// 1. 創(chuàng)建元素
createElement(tagName) {
return document.createElement(tagName);
},
// 創(chuàng)建文本節(jié)點(diǎn)
createText(text) {
return document.createTextNode(text);
},
// 2. 插入元素
insert(child, parent, anchor) {
// 元素移動(dòng);
// 當(dāng)?shù)诙€(gè)參數(shù)為null時(shí),插入到末尾;
parent.insertBefore(child, anchor || null);
},
// 3. 移除元素
remove(child) {
const parent = child.parentNode;
if (parent) {
parent.removeChild(child);
}
},
// 4. 查詢?cè)?
querySelector(selector) {
return document.querySelector(selector);
},
parentNode(node) {
return node.parentNode;
},
nextSibling(node) {
return node.nextSibling;
},
// 5. 設(shè)置文本內(nèi)容
setElementText(el, text) {
el.textContent = text;
},
setText(node, text) {
node.nodeValue = text;
},
};
patchProps
patchProp
再來(lái)實(shí)現(xiàn)一些屬性的更新方法:
// runtime-dom/src/patchProp.ts
import { patchAttr } from "./modules/attr";
import { patchClass } from "./modules/class";
import { patchEvent } from "./modules/event";
import { patchStyle } from "./modules/style";
export const patchProp = (el, key, prevValue, nextValue) => {
if (key === "class") {
// 1. class 類名
patchClass(el, nextValue);
} else if (key === "style") {
// 2. 樣式
patchStyle(el, prevValue, nextValue);
} else if (/^on[^a-z]/.test(key)) {
// 3. onXxx 事件
patchEvent(el, key, nextValue);
} else {
// 4. 其它 attributes 屬性
patchAttr(el, key, nextValue);
}
};
我們將 props 分成四種類型:class、style、onXxx 事件、以及其它 attributes 屬性;分別用四種方法來(lái)處理這四種 prop。
patchClass
更新 class 屬性:
value為空時(shí),使用el.removeAttribute("class")去掉class屬性;- 否則設(shè)置元素的
className屬性。
// runtime-dom/src/modules/class.ts
export const patchClass = (el, value) => {
if (value == null) {
el.removeAttribute("class");
} else {
el.className = value;
}
};
patchStyle
更新 style 屬性:
style沒(méi)有新值時(shí),去掉style屬性;style有新值時(shí)才進(jìn)行更新;- 將新的樣式添加到
style上,如果老的style中有重復(fù)的,則直接將老的樣式覆蓋 - 對(duì)于老的
style中有、新的style中沒(méi)有的樣式,需要將其移除
// runtime-dom/src/modules/style.ts
export const patchStyle = (el, prev, next) => {
if (next) {
const style = el.style;
// 1. 將新的樣式添加到style上,如果有重復(fù)的直接覆蓋
for (let key in next) {
style[key] = next[key];
}
for (let key in prev) {
// 2. 老的有,新的沒(méi)有,要移除掉
if (next[key] == null) {
style[key] = null;
}
}
} else {
el.removeAttribute("style");
}
};
patchEvent
更新事件(事件是以 onXxx 的形式綁定的):
- 通過(guò)一個(gè)調(diào)用器
invoker來(lái)存儲(chǔ)事件的回調(diào)函數(shù)(存儲(chǔ)到invoker.value上),以及執(zhí)行回調(diào)(執(zhí)行invoker.value()) - 需要將老事件緩存起來(lái),緩存到
el._vei屬性上(緩存的是invoker) - 情況一:如果存在新的事件回調(diào)函數(shù),且在
el._vei中存在該事件的緩存,則是更新事件;直接更新invoker.value即可 - 情況二:如果存在新的事件回調(diào)函數(shù),但緩存中不存在,則是綁定新的事件;先創(chuàng)建
invoker,并將invoker緩存到el._vei中,然后通過(guò)el.addEventListener()綁定事件 - 情況三:如果不存在新的事件回調(diào)函數(shù),則是移除事件,直接使用
el.removeEventListener()將該事件移除。
// runtime-dom/src/modules/event.ts
function createInvoker(initialValue) {
const invoker = (e) => invoker.value(e);
// 將事件的回調(diào)綁定到 invoker.value 上,后續(xù)更新事件回調(diào)的時(shí)候,只需更新 invoker.value 即可
invoker.value = initialValue;
return invoker;
}
export const patchEvent = (el, key, nextValue) => {
const invokers = el._vei || (el._vei = {}); // _vei 是 vue event invoker 的縮寫(xiě)
const name = key.slice(2).toLowerCase(); // 獲取事件名
const existingInvoker = invokers[name]; // 取緩存
// 1. 如果是更新事件的回調(diào)
if (nextValue && existingInvoker) {
// 事件是存儲(chǔ)到 invoker.value 上的,更新該屬性即可
existingInvoker.value = nextValue;
} else {
// 2. 如果是綁定新的事件
if (nextValue) {
// 2.1 創(chuàng)建 invoker(事件的回調(diào)函數(shù)),并緩存起來(lái)(本質(zhì)是緩存到el._vei上)
const invoker = (invokers[name] = createInvoker(nextValue));
// 2.2 綁定事件
el.addEventListener(name, invoker);
} else {
// 3. 如果是移除事件
// 3.1 解綁事件
el.removeEventListener(name, existingInvoker);
// 3.2 清除緩存
invokers[name] = null;
}
}
};
patchAttr
更新其它 attributes:
- 使用
el.removeAttribute()移除屬性; - 使用
el.setAttribute()新增和更新屬性
// runtime-dom/src/modules/attr.ts
export const patchAttr = (el, key, value) => {
if (value == null) {
el.removeAttribute(key);
} else {
el.setAttribute(key, value);
}
};
總結(jié)
本小節(jié)我們大致介紹了 runtime-dom 模塊,實(shí)現(xiàn)了一些 DOM 操作和 props 更新方法; 下一小節(jié),我們將介紹 runtime-core 模塊相關(guān)的內(nèi)容。
以上就是vue3模塊創(chuàng)建runtime-dom源碼解析的詳細(xì)內(nèi)容,更多關(guān)于vue3 runtime-dom模塊創(chuàng)建的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Vue完整版和runtime版的區(qū)別詳解
- vue開(kāi)發(fā)runtime core中的虛擬節(jié)點(diǎn)示例詳解
- Vue.js和Vue.runtime.js區(qū)別淺析
- vue-next/runtime-core 源碼閱讀指南詳解
- Vue中使用localStorage存儲(chǔ)token并設(shè)置時(shí)效
- vue中destroyed方法及使用示例講解
- vue中?render?函數(shù)功能與原理分析
- Vue中Number方法將字符串轉(zhuǎn)換為數(shù)字的過(guò)程
- Vue中 Runtime + Compiler 和 Runtime-only 兩種模式含義和區(qū)別詳解
相關(guān)文章
Vue學(xué)習(xí)之a(chǎn)xios的使用方法實(shí)例分析
這篇文章主要介紹了Vue學(xué)習(xí)之a(chǎn)xios的使用方法,結(jié)合實(shí)例形式分析了vue.js axios庫(kù)的功能及網(wǎng)絡(luò)請(qǐng)求相關(guān)操作技巧,需要的朋友可以參考下2020-01-01
vue使用監(jiān)聽(tīng)實(shí)現(xiàn)全選反選功能
最近做的項(xiàng)目用到了全選全不選功能,于是我就自己動(dòng)手寫(xiě)了一個(gè),基于vue使用監(jiān)聽(tīng)實(shí)現(xiàn)全選反選功能,具體實(shí)例代碼大家參考下本文2018-07-07
使用vue3+vite導(dǎo)入圖片路徑錯(cuò)亂問(wèn)題排查及解決
使用vue3+vite開(kāi)發(fā)的時(shí)候,導(dǎo)入svg圖片時(shí),同一個(gè)文件夾下的文件,其中一個(gè)路徑正常解析,另一個(gè)不行,更改文件名之后,該圖片文件就可以正常解析了,本文給大家介紹了使用vue3+vite導(dǎo)入圖片路徑錯(cuò)亂問(wèn)題排查及解決,需要的朋友可以參考下2024-03-03
vue3.x項(xiàng)目降級(jí)到vue2.7的解決方案
Vue2.7是Vue2.x的最終次要版本,下面這篇文章主要給大家介紹了關(guān)于vue3.x項(xiàng)目降級(jí)到vue2.7的解決方案,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
基于Vue實(shí)例對(duì)象的數(shù)據(jù)選項(xiàng)
下面小編就為大家?guī)?lái)一篇基于Vue實(shí)例對(duì)象的數(shù)據(jù)選項(xiàng)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
el-table?樹(shù)形數(shù)據(jù)?tree-props?多層級(jí)使用避坑
本文主要介紹了el-table?樹(shù)形數(shù)據(jù)?tree-props?多層級(jí)使用避坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
Vue滾動(dòng)頁(yè)面到指定位置的實(shí)現(xiàn)及避坑
這篇文章主要介紹了Vue滾動(dòng)頁(yè)面到指定位置的實(shí)現(xiàn)及避坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
Vue封裝localStorage設(shè)置過(guò)期時(shí)間的示例詳解
這篇文章主要介紹了Vue封裝localStorage設(shè)置過(guò)期時(shí)間的相關(guān)資料,在這個(gè)示例中,我們?cè)贛yComponent.vue組件的setup函數(shù)中導(dǎo)入了setItemWithExpiry和getItemWithExpiry函數(shù),并在函數(shù)內(nèi)部使用它們來(lái)設(shè)置和獲取帶有過(guò)期時(shí)間的localStorage數(shù)據(jù),需要的朋友可以參考下2024-06-06
Vue中@click.stop與@click.prevent、@click.native使用
這篇文章主要介紹了Vue中@click.stop與@click.prevent、@click.native使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

