這篇文章主要介紹“如何用Vue實(shí)現(xiàn)一個(gè)渲染引擎”,在日常操作中,相信很多人在如何用Vue實(shí)現(xiàn)一個(gè)渲染引擎問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”如何用Vue實(shí)現(xiàn)一個(gè)渲染引擎”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
成都創(chuàng)新互聯(lián)長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為閻良企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、成都做網(wǎng)站,閻良網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
當(dāng)我們得到 render 函數(shù)之后,接下來就該進(jìn)入到真正的掛載階段了:
掛載 -> 實(shí)例化渲染 Watcher -> 執(zhí)行 updateComponent 方法 -> 執(zhí)行 render 函數(shù)生成 VNode -> 執(zhí)行 patch 進(jìn)行首次渲染 -> 遞歸遍歷 VNode 創(chuàng)建各個(gè)節(jié)點(diǎn)并處理節(jié)點(diǎn)上的普通屬性和指令 -> 如果節(jié)點(diǎn)是自定義組件則創(chuàng)建組件實(shí)例 -> 進(jìn)行組件的初始化、掛載 -> 最終所有 VNode 變成真實(shí)的 DOM 節(jié)點(diǎn)并替換掉頁面上的模版內(nèi)容 -> 完成初始渲染
所以,本篇文章目標(biāo)就是實(shí)現(xiàn)上面描述的整個(gè)過成,完成初始渲染。整個(gè)過程中涉及如下知識(shí)點(diǎn):
render helper
VNode
patch 初始渲染
指令(v-model、v-bind、v-on)的處理
實(shí)例化子組件
插槽的處理
接下來就正式進(jìn)入代碼實(shí)現(xiàn)過程,一步步實(shí)現(xiàn)上述所有內(nèi)容,完成頁面的初始渲染。
/src/compiler/index.js
/** * 編譯器 */ export default function mount(vm) { if (!vm.$options.render) { // 沒有提供 render 選項(xiàng),則編譯生成 render 函數(shù) // ... } mountComponent(vm) } 復(fù)制代碼
/src/compiler/mountComponent.js
/** * @param {*} vm Vue 實(shí)例 */ export default function mountComponent(vm) { // 更新組件的的函數(shù) const updateComponent = () => { vm._update(vm._render()) } // 實(shí)例化一個(gè)渲染 Watcher,當(dāng)響應(yīng)式數(shù)據(jù)更新時(shí),這個(gè)更新函數(shù)會(huì)被執(zhí)行 new Watcher(updateComponent) } 復(fù)制代碼
/src/compiler/mountComponent.js
/** * 負(fù)責(zé)執(zhí)行 vm.$options.render 函數(shù) */ Vue.prototype._render = function () { // 給 render 函數(shù)綁定 this 上下文為 Vue 實(shí)例 return this.$options.render.apply(this) } 復(fù)制代碼
/src/compiler/renderHelper.js
/** * 在 Vue 實(shí)例上安裝運(yùn)行時(shí)的渲染幫助函數(shù),比如 _c、_v,這些函數(shù)會(huì)生成 Vnode * @param {VueContructor} target Vue 實(shí)例 */ export default function renderHelper(target) { target._c = createElement target._v = createTextNode } 復(fù)制代碼
/src/compiler/renderHelper.js
/** * 根據(jù)標(biāo)簽信息創(chuàng)建 Vnode * @param {string} tag 標(biāo)簽名 * @param {Map} attr 標(biāo)簽的屬性 Map 對(duì)象 * @param {Array<Render>} children 所有的子節(jié)點(diǎn)的渲染函數(shù) */ function createElement(tag, attr, children) { return VNode(tag, attr, children, this) } 復(fù)制代碼
/src/compiler/renderHelper.js
/** * 生成文本節(jié)點(diǎn)的 VNode * @param {*} textAst 文本節(jié)點(diǎn)的 AST 對(duì)象 */ function createTextNode(textAst) { return VNode(null, null, null, this, textAst) } 復(fù)制代碼
/src/compiler/vnode.js
/** * VNode * @param {*} tag 標(biāo)簽名 * @param {*} attr 屬性 Map 對(duì)象 * @param {*} children 子節(jié)點(diǎn)組成的 VNode * @param {*} text 文本節(jié)點(diǎn)的 ast 對(duì)象 * @param {*} context Vue 實(shí)例 * @returns VNode */ export default function VNode(tag, attr, children, context, text = null) { return { // 標(biāo)簽 tag, // 屬性 Map 對(duì)象 attr, // 父節(jié)點(diǎn) parent: null, // 子節(jié)點(diǎn)組成的 Vnode 數(shù)組 children, // 文本節(jié)點(diǎn)的 Ast 對(duì)象 text, // Vnode 的真實(shí)節(jié)點(diǎn) elm: null, // Vue 實(shí)例 context } } 復(fù)制代碼
/src/compiler/mountComponent.js
Vue.prototype._update = function (vnode) { // 老的 VNode const prevVNode = this._vnode // 新的 VNode this._vnode = vnode if (!prevVNode) { // 老的 VNode 不存在,則說明時(shí)首次渲染根組件 this.$el = this.__patch__(this.$el, vnode) } else { // 后續(xù)更新組件或者首次渲染子組件,都會(huì)走這里 this.$el = this.__patch__(prevVNode, vnode) } } 復(fù)制代碼
/src/index.js
/** * 初始化配置對(duì)象 * @param {*} options */ Vue.prototype._init = function (options) { // ... initData(this) // 安裝運(yùn)行時(shí)的渲染工具函數(shù) renderHelper(this) // 在實(shí)例上安裝 patch 函數(shù) this.__patch__ = patch // 如果存在 el 配置項(xiàng),則調(diào)用 $mount 方法編譯模版 if (this.$options.el) { this.$mount() } } 復(fù)制代碼
/src/compiler/patch.js
/** * 初始渲染和后續(xù)更新的入口 * @param {VNode} oldVnode 老的 VNode * @param {VNode} vnode 新的 VNode * @returns VNode 的真實(shí) DOM 節(jié)點(diǎn) */ export default function patch(oldVnode, vnode) { if (oldVnode && !vnode) { // 老節(jié)點(diǎn)存在,新節(jié)點(diǎn)不存在,則銷毀組件 return } if (!oldVnode) { // oldVnode 不存在,說明是子組件首次渲染 createElm(vnode) } else { if (oldVnode.nodeType) { // 真實(shí)節(jié)點(diǎn),則表示首次渲染根組件 // 父節(jié)點(diǎn),即 body const parent = oldVnode.parentNode // 參考節(jié)點(diǎn),即老的 vnode 的下一個(gè)節(jié)點(diǎn) —— script,新節(jié)點(diǎn)要插在 script 的前面 const referNode = oldVnode.nextSibling // 創(chuàng)建元素 createElm(vnode, parent, referNode) // 移除老的 vnode parent.removeChild(oldVnode) } else { console.log('update') } } return vnode.elm } 復(fù)制代碼
/src/compiler/patch.js
/** * 創(chuàng)建元素 * @param {*} vnode VNode * @param {*} parent VNode 的父節(jié)點(diǎn),真實(shí)節(jié)點(diǎn) * @returns */ function createElm(vnode, parent, referNode) { // 記錄節(jié)點(diǎn)的父節(jié)點(diǎn) vnode.parent = parent // 創(chuàng)建自定義組件,如果是非組件,則會(huì)繼續(xù)后面的流程 if (createComponent(vnode)) return const { attr, children, text } = vnode if (text) { // 文本節(jié)點(diǎn) // 創(chuàng)建文本節(jié)點(diǎn),并插入到父節(jié)點(diǎn)內(nèi) vnode.elm = createTextNode(vnode) } else { // 元素節(jié)點(diǎn) // 創(chuàng)建元素,在 vnode 上記錄對(duì)應(yīng)的 dom 節(jié)點(diǎn) vnode.elm = document.createElement(vnode.tag) // 給元素設(shè)置屬性 setAttribute(attr, vnode) // 遞歸創(chuàng)建子節(jié)點(diǎn) for (let i = 0, len = children.length; i < len; i++) { createElm(children[i], vnode.elm) } } // 如果存在 parent,則將創(chuàng)建的節(jié)點(diǎn)插入到父節(jié)點(diǎn)內(nèi) if (parent) { const elm = vnode.elm if (referNode) { parent.insertBefore(elm, referNode) } else { parent.appendChild(elm) } } }
到此,關(guān)于“如何用Vue實(shí)現(xiàn)一個(gè)渲染引擎”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
當(dāng)前題目:如何用Vue實(shí)現(xiàn)一個(gè)渲染引擎
URL標(biāo)題:http://chinadenli.net/article44/gshsee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化、標(biāo)簽優(yōu)化、云服務(wù)器、網(wǎng)頁設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)