欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue源碼學習記錄之手寫vm.$mount方法

 更新時間:2022年11月06日 11:24:23   作者:林恒  
在我們開發(fā)中,經常要用到Vue.extend創(chuàng)建出Vue的子類來構造函數(shù),通過new 得到子類的實例,然后通過$mount掛載到節(jié)點,今天通過本文給大家講解手寫vm.$mount方法 ,感興趣的朋友一起看看吧

這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

一、概述

在我們開發(fā)中,經常要用到Vue.extend創(chuàng)建出Vue的子類來構造函數(shù),通過new 得到子類的實例,然后通過$mount掛載到節(jié)點,如代碼:

<div id="mount-point"></div>
<!-- 創(chuàng)建構造器 -->
var Profile = Vue.extend({
 template:'<p>{{firstName}} {{lastName}} aka{{alias}}</p>',
 data:function(){
  return{
   firstName:'Walter',
   lastName:'White',
   alias:'Heisenberg'
  }
 }
})
<!-- 創(chuàng)建Profile實例,并掛載到一個元素上 -->
new Profile().$mount('#mount-point');

$mount方法是怎么實現(xiàn)的,篇文章就來講一下

二、使用方式

vm.$mount( [elementOrSelector] )

(1)參數(shù)

{ Element | string } [elementOrSelector]

(2)返回值

  vm,即實例本身。

(3)用法

1、如果Vue.js實例在實例化時沒有收到el選項,則它處于“未掛載”狀態(tài),沒有關聯(lián)的DOM元素。

2、可以使用vm.$mount手動掛載一個未掛載的實例。

3、如果沒有提供elementOrSelector參數(shù),模板將被渲染為文檔之外的元素,并且必須使用原生DOM的API把它插入文檔中。

4、這個方法返回實例自身,因而可以鏈式調用其他實例方法。

(4)例子

var MyComponent = Vue.extend({
 template:'<div>Hello!</div>',
})
<!-- 創(chuàng)建并掛載到#app(會替換#app) -->
new MyComponent().$mount('#app');
<!-- 創(chuàng)建并掛載到#app(會替換#app) -->
new MyComponent().$mount({el:'#app'});
<!-- 創(chuàng)建并掛載到#app(會替換#app) -->
var component = new MyComponent().$mount();
document.getElementById('app').appendChild(component.$el);

1、在不同的構建版本中,vm.$mount的表現(xiàn)都不一樣。其差異主要體現(xiàn)在完整版(vue.js)和只包含運行時版本(vue.runtime.js)之間。

2、完整版和只包含運行時版本之間的差異在于是否有編譯器,而是否有編譯器的差異主要在于vm.$mount方法的表現(xiàn)形式。

3、在只包含運行時的構建版本中,vm.mount的作用會稍有不同,它首先會檢查template或el選項所提供的模板是否已經轉換成渲染函數(shù)(render函數(shù))。如果沒有,則立即進入編譯過程,將模板編譯成渲染函數(shù),完成之后再進入掛載與渲染的流程中。

4、只包含運行時版本的vm.$mount沒有編譯步驟,它會默認實例上已經存在渲染函數(shù),如果不存在,則會設置一個。并且,這個渲染函數(shù)在執(zhí)行時會返回一個空節(jié)點的VNode,以保證執(zhí)行時不會因為函數(shù)不存在而報錯。同時如果是開發(fā)環(huán)境下運行,Vue.js會觸發(fā)警告,提示我們當前使用的是只包含運行時的版本,會讓我們提供渲染函數(shù),或者去使用完整的構建版本。

5、從原理的角度來講,完整版和只包含運行時版本之間是包含關系,完整版包含只包含運行時版本。

三、完整版vm.$mount的實現(xiàn)原理

(1)實現(xiàn)代碼

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 <!-- 做些什么 -->
 return mount.call(this,el);
}

1、將Vue原型上的$mount方法保存在mount中,以便后續(xù)使用。

2、然后Vue原型上的$mount方法被一個新的方法覆蓋了。新方法中會調用原始的方法,這種做法通常被稱為函數(shù)劫持。(看源碼的同學可能發(fā)現(xiàn)了,vue多處用了函數(shù)劫持的做法,例如:對數(shù)組實現(xiàn)監(jiān)聽的時候...)

3、通過函數(shù)劫持,可以在原始功能上新增一些其他功能。上面代碼中,vm.$mount的原始方法就是mount的核心功能,而在完整版中需要將編譯功能新增到核心功能上去。

(2)由于el參數(shù)支持元素類型或者字符串類型的選擇器,所以第一步是通過el獲取DOM元素。

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
    el = el && query(el);
    return mount.call(this,el);
}

使用query獲取DOM元素

function query(el){
    if(typeof el === 'string'){
        const selected = document.querySelector(el);
        if(!selected){
            return document.createElement('div');
        }
        return selected;
    }else{
        return el;
    }
}

1、如果el是字符串,則使用doucment.querySelector獲取DOM元素,如果獲取不到,則創(chuàng)建一個空的div元素。

2、如果el不是字符串,那么認為它是元素類型,直接返回el(如果執(zhí)行vm.$mount方法時沒有傳遞el參數(shù),則返回undefined)

(3)編譯器

1、首先判斷Vue.js實例中是否存在渲染函數(shù),只有不存在時,才會將模板編譯成渲染函數(shù)。

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 el = el && query(el);
 const options = this.$options;
 if(!options.render){
  <!-- 將模板編譯成渲染函數(shù)并賦值給options.render -->
 }
    return mount.call(this,el);
}

2、在實例化Vue.js時,會有一個初始化流程,其中會向Vue.js實例上新增一些方法,這里的this.$options就是其中之一,它可以訪問到實例化Vue.js時用戶設置的一些參數(shù),例如tempalte和render。

3、如果在實例化Vue.js時給出了render選項,那么template其實是無效的,因為不會進入模板編譯的流程,而是直接使用render選項中提供的渲染函數(shù)。

4、Vue.js在官方文檔的template選項中也給出了相應的提示。如果沒有render選項,那么需要獲取模板并將模板編譯成渲染函數(shù)(render函數(shù))賦值給render選項。

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 el = el && query(el);
 const options = this.$options;
 if(!options.render){
  <!-- 新增獲取模板相關邏輯 -->
  let template = options.template;
  if(template){
  
  }else if(el){
   template = getOuterHTML(el);
  }
 }
    return mount.call(this,el);
}

5、從選項中取出template選項,也就是取出用戶實例化Vue.js時設置的模板。如果沒有取到,說明用戶沒有設置tempalte選項。那么使用getOuterHTML方法從用戶提供的el選項中獲取模板。

function getOuterHTML(el){
 if(el.outerHTML){
  return el.outerHTML;
 }else{
  const container = document.createElement('div');
  container.appendChild(el.cloneNode(true));
  return container.innerHTML;
 }
}

6、getOuterHTML方法會返回參數(shù)中提供的DOM元素的HTML字符串。

7、整體邏輯

如果用戶沒有通過template選項設置模板,那么會從el選項中獲取HTML字符串當作模板。如果用戶提供了template選項,那么需要對它進一步解析,因為這個選項支持很多種使用方式。template選項可以直接設置成字符串模板,也可以設置為以#開頭的選擇符,還可以設置成DOM元素。

8、從不同的格式中將模板解析出來

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 el = el && query(el);
 const options = this.$options;
 if(!options.render){
  <!-- 新增獲取模板相關邏輯 -->
  let template = options.template;
  if(template){
   if(typeof tempalte === 'string'){
    if(tempalte.charAt(0) === "#"){
     template = idToTemplate(tempalte);
    }
   }else if(tempalte.nodeType){
    template = template.innerHTML;
   }else{
    if(process.env.NODE_ENV !== 'production'){
     warn('invalid template option:'+tempalte,this);
    }
    return this;
   }
  }else if(el){
   template = getOuterHTML(el);
  }
 }
    return mount.call(this,el);
}

9、如果tempalte是字符串并且以#開頭,則它將被用作選擇符。通過選擇符獲取DOM元素后,會使用innerHTML作為模板。

10、使用idToTemplate方法從選擇符中獲取模板。idToTemplate使用選擇符獲取DOM元素之后,將它的innerHTML作為模板。

function idToTemplate(id){
 const el = query(id);
 return el && el.innerHTML;
}

11、如果template是字符串,但不是以#開頭,就說明template是用戶設置的模板,不需要進行任何處理,直接使用即可。

12、如果template選項的類型不是字符串,則判斷它是否是一個DOM元素,如果是,則使用DOM元素的innerHTML作為模板。如果不是,只需要判斷它是否具備nodeType屬性即可。

13、如果tempalte選項既不是字符串,也不是DOM元素,那么Vue.js會觸發(fā)警告,提示用戶template選項是無效的。

14、獲取模板之后,下一步是將模板編譯成渲染函數(shù),通過執(zhí)行compileToFunctions函數(shù)可以將模板編譯成渲染函數(shù)并設置到this.options上。

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 el = el && query(el);
 const options = this.$options;
 if(!options.render){
  <!-- 新增獲取模板相關邏輯 -->
  let template = options.template;
  if(template){
   if(typeof tempalte === 'string'){
    if(tempalte.charAt(0) === "#"){
     template = idToTemplate(tempalte);
    }
   }else if(tempalte.nodeType){
    template = template.innerHTML;
   }else{
    if(process.env.NODE_ENV !== 'production'){
     warn('invalid template option:'+tempalte,this);
    }
    return this;
   }
  }else if(el){
   template = getOuterHTML(el);
  }
  <!-- 新增編譯相關邏輯 -->
  if(tempalte){
   const { render } = compileToFunctions(
    template,
    {...},
    this
   )
   options.render = render;
  }
 }
    return mount.call(this,el);
}

15、將模板編譯成代碼字符串并將代碼字符串轉換成渲染函數(shù)的過程是在compileToFunctions函數(shù)中完成的,其內部實現(xiàn)如下

function compileToFunctions(template,options,vm){
 options = extend({},options);
 <!-- 檢查緩存 -->
 const key = options.delimiters
 ? String(options.delimiters)+tempalte
 :template;
 if(cache[key]){
  return cache[key];
 }
 <!-- 編譯 -->
 const compiled = compile(template,options);
 <!-- 將代碼字符串轉換為函數(shù) -->
 const res = {};
 res.render = createFunction(compiled.render);
 return (cache[key] = res)
}
function createFunction(code){
 return new Function(code);
}

1)首先,將options屬性混合到空對象中,其目的是讓options稱為可選參數(shù)。

2)檢查緩存中是否已經存在編譯后的模板。如果模板已經被編譯,就會直接返回緩存中的結果,不會重復編譯,保證不做無用功來提升性能。

3)調用compile函數(shù)來編譯模板,將模板編譯成代碼字符串并存儲在compiled中的render屬性中。

4)調用createFunction函數(shù)將代碼字符串轉換成函數(shù)。其實現(xiàn)原理箱單簡單,使用new Function(code)就可以完成。

5)在代碼字符串被new Function(code)轉換成函數(shù)之后,當調用函數(shù)時,代碼字符串會被執(zhí)行。例如

const code = 'console.log("Hello Berwin")';
const render = new Function(code);
render();//Hello Berwin

6)最后,將渲染函數(shù)返回給調用方。

16、當通過compileToFunctions函數(shù)得到渲染函數(shù)之后,將渲染函數(shù)設置到this.$options上。

四、只包含運行時版本的vm.$mount的實現(xiàn)原理

(1)只包含運行時版本的vm.mount方法的核心功能。實現(xiàn)如下

Vue.prototype.$mount = function(el){
 el = el && inBrower ? query(el) : undefined;
 return mountComponent(this,el);
}

1、$mount方法將ID轉換為DOM元素后,使用mountComponent函數(shù)將Vue.js實例掛載到DOM元素上。

2、將實例掛載到DOM元素上指的是將模板渲染到指定的DOM元素中,而且是持續(xù)性的,以后當數(shù)據(jù)(狀態(tài))發(fā)生變化時,依然可以渲染到指定的DOM元素中。

3、實現(xiàn)這個功能需要開啟watcher。

watcher將持續(xù)觀察模板中用到的所有數(shù)據(jù)(狀態(tài)),當這些數(shù)據(jù)(狀態(tài))被修改時它將得到通知,從而進行渲染操作。這個過程回持續(xù)到實例被銷毀。

export function mountComponent(vm,el){
 if(!vm.$options.render){
  vm.$options.render = createEmptyVNode;
  if(process.env.NODE_ENV !== 'production'){
   <!-- 在開發(fā)環(huán)境發(fā)出警告 -->
  }
 }
}

4、mountComponent方法會判斷實例上是否存在渲染函數(shù)。如果不存在,則設置一個默認的渲染函數(shù)createEmptyVNode,該渲染函數(shù)執(zhí)行后,會返回一個注釋類型的VNode節(jié)點。

5、事實上,如果在mountComponent方法中發(fā)現(xiàn)實例上沒有渲染函數(shù),則會將el參數(shù)指定頁面中的元素節(jié)點替換成一個注釋節(jié)點,并且在開發(fā)環(huán)境下在瀏覽器的控制臺中給出警告。

(2)Vue.js實例在不同的階段會觸發(fā)不同的生命周期鉤子,在掛載實例之前會觸發(fā)beforeMount鉤子函數(shù)。

export function mountComponent(vm,el){
 if(!vm.$options.render){
  vm.$options.render = createEmptyVNode;
  if(process.env.NODE_ENV !== 'production'){
   <!-- 在開發(fā)環(huán)境發(fā)出警告 -->
  }
  callHook(vm,'beforeMount')
 }
}

1、鉤子函數(shù)觸發(fā)后,將執(zhí)行真正的掛載操作。掛載操作與渲染類似,不同的是渲染指的是渲染一次,而掛載指的是持續(xù)性渲染。掛載之后,每當狀態(tài)發(fā)生變化時,都會進行渲染操作。

(3)mountComponent具體實現(xiàn)

export function mountComponent(vm,el){
 if(!vm.$options.render){
  vm.$options.render = createEmptyVNode;
  if(process.env.NODE_ENV !== 'production'){
   <!-- 在開發(fā)環(huán)境發(fā)出警告 -->
  }
  <!-- 觸發(fā)生命周期鉤子 -->
  callHook(vm,'beforeMount');
  <!-- 掛載 -->
  vm._watcher = new Watcher(vm,()=>{
   vm._update(vm._render())
  },noop);
  <!-- 觸發(fā)生命周期鉤子 -->
  callHook(vm,'mounted');
  return vm;
 }
}

1、vm._update作用:調用虛擬DOM中的patch方法來執(zhí)行節(jié)點的比對與渲染操作。

2、vm._render作用:執(zhí)行渲染函數(shù),得到一份新的VNode節(jié)點樹。

3、vm._update(vm._render())作用:先調用渲染函數(shù)得到一份最新的VNode節(jié)點樹,然后通過vm._update方法對最新的VNode和上一次渲染用到的舊VNode進行對比并更新DOM節(jié)點。簡單來說,就是執(zhí)行了渲染操作。

(4)掛載是持續(xù)性的,而持續(xù)性的關鍵就在于new Watcher這行代碼。

1、Watcher的第二個參數(shù)支持函數(shù),并且當它是函數(shù)時,會同時觀察函數(shù)中所讀取的所有Vue.js實例上的響應式數(shù)據(jù)。

2、當watcher執(zhí)行函數(shù)時,函數(shù)中所讀取的數(shù)據(jù)都將會觸發(fā)getter去全局找到watcher并將其收集到函數(shù)的依賴列表中。即,函數(shù)中讀取的所有數(shù)據(jù)都將被watcher觀察。這些數(shù)據(jù)中的任何一個發(fā)生變化時,watcher都將得到通知。

3、當數(shù)據(jù)發(fā)生變化時,watcher會一次又一次地執(zhí)行函數(shù)進入渲染流程,如此反復,這個過程會持續(xù)到實例被銷毀。

4、掛載完畢后,會觸發(fā)mounted鉤子函數(shù)。

如果不懂watcher,其實可以去掉看,就簡單很多

export function mountComponent(vm,el){
 if(!vm.$options.render){
  vm.$options.render = createEmptyVNode;
  if(process.env.NODE_ENV !== 'production'){
   <!-- 在開發(fā)環(huán)境發(fā)出警告 -->
  }
  <!-- 觸發(fā)生命周期鉤子 -->
  callHook(vm,'beforeMount');
  <!-- 掛載 -->
   
   vm._update(vm._render())
   
  <!-- 觸發(fā)生命周期鉤子 -->
  callHook(vm,'mounted');
  return vm;
 }
}

這樣,是不是很容易理解了。整個mountComponent,一句關鍵代碼:vm._update(vm._render()),表示通過執(zhí)行vm._render()得到VNode,再把VNode傳入vm._update()vm._update()得功能是 將傳入的VNode 變成 真實Dom渲染到頁面。

簡單地總結一下:

$mount()的思路就是, 判斷 用戶傳入的option有沒有render函數(shù),

1.有的話就走運行時版本,

2.沒有的話就自動生成render函數(shù),然后在執(zhí)行運行時版本(其實這就是編譯時版本,比運行時版本多了異步生成render函數(shù)的步驟)。

執(zhí)行運行時版本的時候,

通過render()獲得Vnode把Vnode傳入_update() 實現(xiàn)渲染

到此這篇關于Vue源碼學習記錄之手寫vm.$mount方法 的文章就介紹到這了,更多相關vue vm.$mount方法 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Vue3 去除 vue warn 及生產環(huán)境去除console.log的方法

    Vue3 去除 vue warn 及生產環(huán)境去除console.log的方法

    這篇文章主要介紹了Vue3 去除 vue warn 及生產環(huán)境去除console.log的方法,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • vue實現(xiàn)簡單加法計算器

    vue實現(xiàn)簡單加法計算器

    這篇文章主要為大家詳細介紹了vue實現(xiàn)簡單加法計算器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • 在 Vue 中編寫 SVG 圖標組件的方法

    在 Vue 中編寫 SVG 圖標組件的方法

    這篇文章主要介紹了在 Vue 中編寫 SVG 圖標組件的方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • vue實現(xiàn)跨頁面定位錨點區(qū)域方式

    vue實現(xiàn)跨頁面定位錨點區(qū)域方式

    這篇文章主要介紹了vue實現(xiàn)跨頁面定位錨點區(qū)域方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Vue Ref全家桶具體用法詳解

    Vue Ref全家桶具體用法詳解

    ref用來輔助我們獲取DOM元素或組件的引用實例對象,每個vue的組件實例上,都包含一個refs對象,里面存儲著對應的DOM元素或組件的引用,默認情況下,組件的refs指向一個空對象
    2023-03-03
  • vue 指定文字高亮的實現(xiàn)示例

    vue 指定文字高亮的實現(xiàn)示例

    在做文字處理的項目時經常會遇到搜索文字并高亮的需求,本文就來介紹vue 指定文字高亮的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • vue src動態(tài)加載請求獲取圖片的方法

    vue src動態(tài)加載請求獲取圖片的方法

    這篇文章主要為大家詳細介紹了vue src動態(tài)加載請求獲取圖片的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 在vue項目中使用Jquery-contextmenu插件的步驟講解

    在vue項目中使用Jquery-contextmenu插件的步驟講解

    今天小編就為大家分享一篇關于在vue項目中使用Jquery-contextmenu插件的步驟講解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • vue一步步實現(xiàn)alert功能

    vue一步步實現(xiàn)alert功能

    本篇文章主要介紹了vue一步步實現(xiàn)alert功能,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 詳解Vue3頁面如何自適應表格滾動高度

    詳解Vue3頁面如何自適應表格滾動高度

    這篇文章主要為大家詳細介紹了Vue3頁面如何自適應表格滾動高度,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02

最新評論