vue3模塊創(chuàng)建runtime-dom源碼解析
前言
runtime-dom
是針對瀏覽器的運(yùn)行時,包括 DOM 操作、props
(例如class
、事件、樣式以及其它attributes
)的更新等內(nèi)容;本小節(jié)我們開啟 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)?shù)诙€參數(shù)為null時,插入到末尾; parent.insertBefore(child, anchor || null); }, // 3. 移除元素 remove(child) { const parent = child.parentNode; if (parent) { parent.removeChild(child); } }, // 4. 查詢元素 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
再來實(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
屬性;分別用四種方法來處理這四種 prop
。
patchClass
更新 class
屬性:
value
為空時,使用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
沒有新值時,去掉style
屬性;style
有新值時才進(jìn)行更新;- 將新的樣式添加到
style
上,如果老的style
中有重復(fù)的,則直接將老的樣式覆蓋 - 對于老的
style
中有、新的style
中沒有的樣式,需要將其移除
// 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. 老的有,新的沒有,要移除掉 if (next[key] == null) { style[key] = null; } } } else { el.removeAttribute("style"); } };
patchEvent
更新事件(事件是以 onXxx
的形式綁定的):
- 通過一個調(diào)用器
invoker
來存儲事件的回調(diào)函數(shù)(存儲到invoker.value
上),以及執(zhí)行回調(diào)(執(zhí)行invoker.value()
) - 需要將老事件緩存起來,緩存到
el._vei
屬性上(緩存的是invoker
) - 情況一:如果存在新的事件回調(diào)函數(shù),且在
el._vei
中存在該事件的緩存,則是更新事件;直接更新invoker.value
即可 - 情況二:如果存在新的事件回調(diào)函數(shù),但緩存中不存在,則是綁定新的事件;先創(chuàng)建
invoker
,并將invoker
緩存到el._vei
中,然后通過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)的時候,只需更新 invoker.value 即可 invoker.value = initialValue; return invoker; } export const patchEvent = (el, key, nextValue) => { const invokers = el._vei || (el._vei = {}); // _vei 是 vue event invoker 的縮寫 const name = key.slice(2).toLowerCase(); // 獲取事件名 const existingInvoker = invokers[name]; // 取緩存 // 1. 如果是更新事件的回調(diào) if (nextValue && existingInvoker) { // 事件是存儲到 invoker.value 上的,更新該屬性即可 existingInvoker.value = nextValue; } else { // 2. 如果是綁定新的事件 if (nextValue) { // 2.1 創(chuàng)建 invoker(事件的回調(diào)函數(shù)),并緩存起來(本質(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)建的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue 監(jiān)聽鍵盤回車事件詳解 @keyup.enter || @keyup.enter.native
今天小編就為大家分享一篇vue 監(jiān)聽鍵盤回車事件詳解 @keyup.enter || @keyup.enter.native,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08vue3+ts實(shí)現(xiàn)一個表單組件的詳細(xì)代碼
這篇文章主要介紹了vue3+ts實(shí)現(xiàn)一個表單組件的詳細(xì)代碼,確保通過axios調(diào)用后端接口來獲取省市區(qū)和街道數(shù)據(jù),并在選擇省市區(qū)時加載相應(yīng)的街道數(shù)據(jù),需要的朋友可以參考下2024-07-07Vue使用js-audio-recorder實(shí)現(xiàn)錄制,播放與下載音頻功能
這篇文章主要為大家詳細(xì)介紹了Vue如何使用js-audio-recorder實(shí)現(xiàn)錄制,播放與下載音頻功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2023-12-12