Vue 2源碼解讀$mount函數(shù)原理
1. $mount 函數(shù)來源
上一節(jié)雖然直接從 core 目錄下找到了 Vue 的構(gòu)造函數(shù)定義,但是缺少 $mount 方法。所以直接從開發(fā)過程中使用的 vue.esm.js 找到對(duì)應(yīng)的源碼入口。
根據(jù) scripts/config.js 中可以找到 vue.esm.js 的構(gòu)建入口是 entry-runtime-with-compiler-esm.ts,這個(gè)文件沒有最終也是依賴的 runtime-with-compiler.ts。
這里放一下依賴關(guān)系:
// src/platforms/web/entry-runtime-with-compiler-esm.ts import Vue from './runtime-with-compiler' export default Vue // src/platforms/web/runtime-with-compiler.ts import Vue from './runtime/index' const mount = Vue.prototype.$mount Vue.prototype.$mount = function(el?: string | Element, hydrating?: boolean): Component { // ... const options = this.$options // ... return mount.call(this, el, hydrating) } Vue.compile = compileToFunctions export default Vue // src/platforms/web/runtime/index.ts import Vue from 'core/index' import { mountComponent } from 'core/instance/lifecycle' import { patch } from './patch' // ... 處理 Vue.config // ... 處理 Vue.options Vue.prototype.__patch__ = inBrowser ? patch : noop Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean ): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating) } export default Vue
在 src/platforms/web/runtime/index.ts 中對(duì) Vue 構(gòu)造函數(shù)還做了其他處理,這里就先不看了。
2. runtime 運(yùn)行時(shí)的 $mount 函數(shù)
在 src/platforms/web/runtime/index.ts 中定義的 $mount 函數(shù)就是調(diào)用了 mountComponent 方法并返回其結(jié)果,所以核心依然在 mountComponent 函數(shù)
2.1 mountComponent 函數(shù)
export function mountComponent(vm: Component, el: Element | null | undefined, hydrating?: boolean ): Component { vm.$el = el if (!vm.$options.render) { vm.$options.render = createEmptyVNode if (__DEV__) { // 這里是判斷開發(fā)環(huán)境,如果有配置模板或者el屬性,則警告需要將模板編譯成渲染函數(shù),或者使用包含編譯器的部分。 } } callHook(vm, 'beforeMount') let updateComponent if (__DEV__ && config.performance && mark) { // 處理開發(fā)環(huán)境中,配置了 性能分析時(shí)的處理,會(huì)在組件中創(chuàng)建對(duì)應(yīng)的dom標(biāo)簽(class) } else { updateComponent = () => { vm._update(vm._render(), hydrating) } } // 渲染watcher,在執(zhí)行前觸發(fā) beforeUpdate 調(diào)用對(duì)應(yīng)鉤子函數(shù) const watcherOptions: WatcherOptions = { before() { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } } new Watcher( vm, updateComponent, noop, watcherOptions, true) hydrating = false // 這個(gè)是配合 2.7 添加的 setup 處理 const preWatchers = vm._preWatchers if (preWatchers) { for (let i = 0; i < preWatchers.length; i++) { preWatchers[i].run() } } // 手動(dòng)掛載實(shí)例,并且在首次掛載($vnode為空)時(shí)觸發(fā) mounted if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') } return vm }
這里的邏輯也比較簡(jiǎn)單(省略掉了性能分析)
- 首先判斷 render 渲染函數(shù),沒有則將 render 屬性配置為一個(gè)創(chuàng)建空節(jié)點(diǎn)的函數(shù)
- 調(diào)用在 lifecycleMixin(Vue) 中定義的 _update 方法來對(duì)比和更新 VNode,并渲染到 dom 節(jié)點(diǎn)上
- 實(shí)例化一個(gè) Render Watcher ,并在每次更新 dom 之前觸發(fā) beforeUpdate
- 在2.7+版本,根據(jù) setup 中定義的預(yù)執(zhí)行的 watcher 函數(shù)分別調(diào)用 watcher.run 執(zhí)行一次
- 最后修改實(shí)例狀態(tài) _isMounted,并觸發(fā) mounted,返回組件實(shí)例對(duì)象
2.2 _update 函數(shù)(首次渲染)
函數(shù)大致代碼如下
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { const vm: Component = this const prevEl = vm.$el const prevVnode = vm._vnode const restoreActiveInstance = setActiveInstance(vm) vm._vnode = vnode if (!prevVnode) { // 首次渲染 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */) } else { // 更新 vm.$el = vm.__patch__(prevVnode, vnode) } restoreActiveInstance() prevEl && (prevEl.__vue__ = null) vm.$el && (vm.$el.__vue__ = vm) // if parent is an HOC, update its $el as well if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { vm.$parent.$el = vm.$el } }
這里主要是記錄組件實(shí)例在更新前的 el** 和 **_vnode** 兩個(gè)屬性,用來在后面的 `__path__` 方法中進(jìn)行比較和更新(也就是常說的 diff),最后將實(shí)例的 **el 屬性的 __vue__
指向當(dāng)前實(shí)例,并處理高階函數(shù)組件的正確dom。
???? 這里通過 setActiveInstance(vm) 函數(shù),用閉包的形式將 當(dāng)前組件實(shí)例 導(dǎo)出到了外部,后面的 patch 使用的實(shí)例也就是該實(shí)例(translation 組件也會(huì)使用該實(shí)例)
3. runtime-with-compiler 的 $mount 函數(shù)
上文說到了 runtime-with-compiler.ts 中對(duì) Vue 構(gòu)造函數(shù)上的 mount∗∗函數(shù)進(jìn)行了重寫,并且原始的∗∗mount** 函數(shù)進(jìn)行了重寫,并且原始的 **mount∗∗函數(shù)進(jìn)行了重寫,并且原始的∗∗mount 函數(shù)也會(huì)在執(zhí)行時(shí)進(jìn)行相關(guān)檢查。
const mount = Vue.prototype.$mount Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) if (el === document.body || el === document.documentElement) { __DEV__ && warn(`Do not mount Vue to <html> or <body> - mount to normal elements instead.`) return this } const options = this.$options if (!options.render) { let template = options.template if (template) { // 區(qū)分template的類型并驗(yàn)證,最后轉(zhuǎn)成字符串 } else if (el) { template = getOuterHTML(el) } // 根據(jù) template 字符串生成一個(gè) render 函數(shù) if (template) { const { render, staticRenderFns } = compileToFunctions( template, { outputSourceRange: __DEV__, shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this ) options.render = render options.staticRenderFns = staticRenderFns } } return mount.call(this, el, hydrating) }
這里的作用其實(shí)就是編譯 template 模板的過程,并且在函數(shù)執(zhí)行時(shí)會(huì)檢查掛載的dom節(jié)點(diǎn)類型,不能掛載到 body 或者 html 上。
因?yàn)楸旧淼?mount 方法(也就是 mountComponent)只能使用 vm.render() 生成的 VNode 來進(jìn)行 patch 和生成真實(shí) dom。
4. runtime 對(duì) Vue 構(gòu)造函數(shù)的其他修改
上面說的 runtime/index.ts 中第一次定義了 Vue 上的 _mount 方法和 __patch__
方法;同時(shí),這里還在 Vue 構(gòu)造函數(shù)上增加了其他方法。
// 定義 web 端特定的工具函數(shù) Vue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement // 定義相關(guān)指令和組件 extend(Vue.options.directives, platformDirectives) extend(Vue.options.components, platformComponents)
上面的幾個(gè)方法主要有以下作用:
mustUseProp: 定義哪些dom元素必須使用 props 綁定參數(shù)(默認(rèn)有 input,option,select 等)
isReservedTag:判斷標(biāo)簽是不是默認(rèn)保留的標(biāo)簽
isReservedAttr:判斷是不是標(biāo)簽保留屬性,這里只有 class 和 style
getTagNamespace:判斷標(biāo)簽的類別,這里只區(qū)分 SVG 元素和 math 標(biāo)簽
isUnknownElement:判斷未知元素
然后定義了以下指令和方法:
注冊(cè) v-model 和 v-show 指令
注冊(cè) Transition 和 TransitionGroup 動(dòng)畫組件
以上就是Vue 2源碼解讀$mount函數(shù)原理的詳細(xì)內(nèi)容,更多關(guān)于Vue 2 $mount函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue實(shí)現(xiàn)Base64轉(zhuǎn)png、jpg圖片格式
這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)Base64轉(zhuǎn)png、jpg圖片格式的相關(guān)資料,前段獲取生成的是base64圖片,需要轉(zhuǎn)化為jpg,png,需要的朋友可以參考下2023-09-09vue-quill-editor+plupload富文本編輯器實(shí)例詳解
這篇文章主要介紹了vue-quill-editor+plupload富文本編輯器實(shí)例詳解,需要的朋友可以參考下2018-10-10vue3中defineProps傳值使用ref響應(yīng)式失效詳解
這篇文章主要給大家介紹了關(guān)于vue3中defineProps傳值使用ref響應(yīng)式失效的相關(guān)資料,文章通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03vue踩坑記錄之echarts動(dòng)態(tài)數(shù)據(jù)刷新問題
這篇文章主要介紹了vue踩坑記錄之echarts動(dòng)態(tài)數(shù)據(jù)刷新問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Vue實(shí)現(xiàn)圖片預(yù)覽效果實(shí)例(放大、縮小、拖拽)
現(xiàn)在項(xiàng)目中有這樣的一個(gè)需求,對(duì)上傳的圖片可以點(diǎn)擊之后在線預(yù)覽,這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)圖片預(yù)覽效果實(shí)例(放大、縮小、拖拽)的相關(guān)資料,需要的朋友可以參考下2021-05-05vue使用video.js實(shí)現(xiàn)播放m3u8格式的視頻
這篇文章主要為大家詳細(xì)介紹了vue如何使用video.js實(shí)現(xiàn)播放m3u8格式的視頻,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12vue+Element-ui實(shí)現(xiàn)分頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了vue+Element-ui實(shí)現(xiàn)分頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11基于VUE.JS的移動(dòng)端框架Mint UI的使用
本篇文章主要介紹了基于VUE.JS的移動(dòng)端框架Mint UI的使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10