最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue源碼學(xué)習(xí)記錄之手寫vm.$mount方法

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

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

一、概述

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

<div id="mount-point"></div>
<!-- 創(chuàng)建構(gòu)造器 -->
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),沒有關(guān)聯(lián)的DOM元素。

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

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

4、這個方法返回實例自身,因而可以鏈?zhǔn)秸{(diào)用其他實例方法。

(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、在不同的構(gòu)建版本中,vm.$mount的表現(xiàn)都不一樣。其差異主要體現(xiàn)在完整版(vue.js)和只包含運行時版本(vue.runtime.js)之間。

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

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

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

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

三、完整版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方法被一個新的方法覆蓋了。新方法中會調(diào)用原始的方法,這種做法通常被稱為函數(shù)劫持。(看源碼的同學(xué)可能發(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不是字符串,那么認(rèn)為它是元素類型,直接返回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è)置的一些參數(shù),例如tempalte和render。

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

4、Vue.js在官方文檔的template選項中也給出了相應(yīng)的提示。如果沒有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){
  <!-- 新增獲取模板相關(guān)邏輯 -->
  let template = options.template;
  if(template){
  
  }else if(el){
   template = getOuterHTML(el);
  }
 }
    return mount.call(this,el);
}

5、從選項中取出template選項,也就是取出用戶實例化Vue.js時設(shè)置的模板。如果沒有取到,說明用戶沒有設(shè)置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選項設(shè)置模板,那么會從el選項中獲取HTML字符串當(dāng)作模板。如果用戶提供了template選項,那么需要對它進一步解析,因為這個選項支持很多種使用方式。template選項可以直接設(shè)置成字符串模板,也可以設(shè)置為以#開頭的選擇符,還可以設(shè)置成DOM元素。

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

const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
 el = el && query(el);
 const options = this.$options;
 if(!options.render){
  <!-- 新增獲取模板相關(guān)邏輯 -->
  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是用戶設(shè)置的模板,不需要進行任何處理,直接使用即可。

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

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

14、獲取模板之后,下一步是將模板編譯成渲染函數(shù),通過執(zhí)行compileToFunctions函數(shù)可以將模板編譯成渲染函數(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){
  <!-- 新增獲取模板相關(guān)邏輯 -->
  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);
  }
  <!-- 新增編譯相關(guān)邏輯 -->
  if(tempalte){
   const { render } = compileToFunctions(
    template,
    {...},
    this
   )
   options.render = render;
  }
 }
    return mount.call(this,el);
}

15、將模板編譯成代碼字符串并將代碼字符串轉(zhuǎn)換成渲染函數(shù)的過程是在compileToFunctions函數(shù)中完成的,其內(nèi)部實現(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);
 <!-- 將代碼字符串轉(zhuǎn)換為函數(shù) -->
 const res = {};
 res.render = createFunction(compiled.render);
 return (cache[key] = res)
}
function createFunction(code){
 return new Function(code);
}

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

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

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

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

5)在代碼字符串被new Function(code)轉(zhuǎn)換成函數(shù)之后,當(dāng)調(diào)用函數(shù)時,代碼字符串會被執(zhí)行。例如

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

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

16、當(dāng)通過compileToFunctions函數(shù)得到渲染函數(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轉(zhuǎn)換為DOM元素后,使用mountComponent函數(shù)將Vue.js實例掛載到DOM元素上。

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

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

watcher將持續(xù)觀察模板中用到的所有數(shù)據(jù)(狀態(tài)),當(dāng)這些數(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è)置一個默認(rèn)的渲染函數(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ù)性渲染。掛載之后,每當(dāng)狀態(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作用:調(diào)用虛擬DOM中的patch方法來執(zhí)行節(jié)點的比對與渲染操作。

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

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

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

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

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

3、當(dāng)數(shù)據(jù)發(fā)生變化時,watcher會一次又一次地執(zhí)行函數(shù)進入渲染流程,如此反復(fù),這個過程會持續(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,一句關(guān)鍵代碼:vm._update(vm._render()),表示通過執(zhí)行vm._render()得到VNode,再把VNode傳入vm._update()vm._update()得功能是 將傳入的VNode 變成 真實Dom渲染到頁面。

簡單地總結(jié)一下:

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

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

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

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

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

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

相關(guān)文章

  • 基于Vue實現(xiàn)可以拖拽的樹形表格實例詳解

    基于Vue實現(xiàn)可以拖拽的樹形表格實例詳解

    因業(yè)務(wù)需求,需要一個樹形表格,并且支持拖拽排序,任意未知插入,github搜了下,真不到合適的,大部分樹形表格都沒有拖拽功能,所以決定自己實現(xiàn)一個。這里分享一下實現(xiàn)過程,需要的朋友可以參考下
    2018-10-10
  • vue?使用addRoutes動態(tài)添加路由及刷新頁面跳轉(zhuǎn)404路由的問題解決方案

    vue?使用addRoutes動態(tài)添加路由及刷新頁面跳轉(zhuǎn)404路由的問題解決方案

    我自己使用addRoutes動態(tài)添加的路由頁面,使用router-link標(biāo)簽可以跳轉(zhuǎn),但是一刷新就會自動跳轉(zhuǎn)到我定義的通配符?*?指向的404路由頁面,這說明沒有找到指定路由才跳到404路由的,這樣的情況如何處理呢,下面小編給大家分享解決方案,一起看看吧
    2023-10-10
  • Vue使用視頻作為網(wǎng)頁背景的實現(xiàn)指南

    Vue使用視頻作為網(wǎng)頁背景的實現(xiàn)指南

    在現(xiàn)代網(wǎng)頁設(shè)計中,視頻背景逐漸成為一種流行的設(shè)計趨勢,它不僅能夠提升網(wǎng)頁的動態(tài)效果,還可以在視覺上抓住用戶的注意力,本文將詳細(xì)講解如何在頁面中使用視頻作為背景,并確保內(nèi)容可見、頁面元素布局合理,需要的朋友可以參考下
    2024-10-10
  • vue利用vant組件實現(xiàn)輪播圖效果

    vue利用vant組件實現(xiàn)輪播圖效果

    vant組件適用于移動端項目,目前項目開源,是市面上做的比較好的開源項目,功能比較強大,本文小編就來為大家介紹一下如何利用vant實現(xiàn)輪播圖效果吧
    2023-10-10
  • Vue編譯優(yōu)化實現(xiàn)流程詳解

    Vue編譯優(yōu)化實現(xiàn)流程詳解

    編譯優(yōu)化指的是編譯器將模板編譯為渲染函數(shù)的過程中,盡可能多的提取關(guān)鍵信息,并以此指導(dǎo)生成最優(yōu)代碼的過程,優(yōu)化的方向主要是區(qū)分動態(tài)內(nèi)容和靜態(tài)內(nèi)容,并針對不同的內(nèi)容采用不同的優(yōu)化策略
    2023-01-01
  • vue實現(xiàn)左右點擊滾動效果

    vue實現(xiàn)左右點擊滾動效果

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)左右點擊滾動效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 解決Vue項目打包后打開index.html頁面顯示空白以及圖片路徑錯誤的問題

    解決Vue項目打包后打開index.html頁面顯示空白以及圖片路徑錯誤的問題

    這篇文章主要介紹了解決Vue項目打包后打開index.html頁面顯示空白以及圖片路徑錯誤的問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • vue項目的訪問端口及其設(shè)置方式

    vue項目的訪問端口及其設(shè)置方式

    這篇文章主要介紹了vue項目的訪問端口及其設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。
    2022-10-10
  • vue-cli中打包圖片路徑錯誤的解決方法

    vue-cli中打包圖片路徑錯誤的解決方法

    這篇文章主要介紹了vue-cli中打包圖片路徑錯誤的解決方法 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • vue使用vue-json-viewer插件展示JSON格式數(shù)據(jù)的方法

    vue使用vue-json-viewer插件展示JSON格式數(shù)據(jù)的方法

    這篇文章主要給大家介紹了關(guān)于vue使用vue-json-viewer插件展示JSON格式數(shù)據(jù)的相關(guān)資料,前端使用這個插件可以方便展現(xiàn)出json格式的數(shù)據(jù),下載引入使用代碼可直接使用,需要的朋友可以參考下
    2024-05-05

最新評論

左权县| 岳阳县| 宣武区| 昆山市| 民权县| 湖北省| 永春县| 志丹县| 石嘴山市| 呼和浩特市| 托克逊县| 洛川县| 昌都县| 夏河县| 岳普湖县| 江安县| 灌阳县| 舞钢市| 新巴尔虎右旗| 朝阳市| 布拖县| 大石桥市| 左云县| 盐津县| 屏南县| 本溪市| 仲巴县| 城步| 涟源市| 利辛县| 西吉县| 黎川县| 玛沁县| 贺兰县| 夏津县| 大英县| 丰台区| 玛沁县| 黎城县| 建瓯市| 大英县|