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

Vue 組件渲染詳情

 更新時間:2022年08月17日 09:54:46   作者:夏日  
這篇文章主要介紹了Vue 組件渲染詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

Vue中組件分為全局組件和局部組件:

  • 全局組件:通過Vue.component(id,definition)方法進行注冊,并且可以在任何組件中被訪問
  • 局部組件:在組件內(nèi)的components屬性中定義,只能在組件內(nèi)訪問

下面是一個例子:

<div id="app">
  {{ name }}
  <my-button></my-button>
  <aa></aa>
</div>
Vue.components('my-button', {
  template: `<button>my button</button>`
});
Vue.components('aa', {
  template: `<button>global aa</button>`
});
const vm = new Vue({
  el: '#app',
  components: {
    aa: {
      template: `<button>scoped aa</button>`
    },
    bb: {
      template: `<button>bb</button>`
    }
  },
  data () {
    return {
      name: 'ss'
    };
  }
});

頁面中會渲染全局定義的my-button組件和局部定義的aa組件:

接下來筆者會詳細講解全局組件和局部組件到底是如何渲染到頁面上的,并實現(xiàn)相關(guān)代碼。

全局組件

Vue.component是定義在Vue構(gòu)造函數(shù)上的一個函數(shù),它接收iddefinition作為參數(shù):

  • id: 組件的唯一標識
  • definition: 組件的配置項

src/global-api/index.js中定義Vue.component方法:

export function initGlobalApi (Vue) {
  Vue.options = {};
  // 最終會合并到實例上,可以通過vm.$options._base直接使用
  Vue.options._base = Vue;
  // 定義全局組件
  Vue.options.components = {};
  initExtend(Vue);
  Vue.mixin = function (mixin) {
    this.options = mergeOptions(this.options, mixin);
  };
  // 通過Vue.components來注冊全局組件
  Vue.components = function (id, definition) {
    const name = definition.name = definition.name || id;
    // 通過Vue.extend來創(chuàng)建Vue的子類
    definition = this.options._base.extend(definition);
    // 將Vue子類添加到Vue.options.components對象中,key為name
    this.options.components[name] = definition;
  };
}

Vue.component幫我們做了倆件事:

  • 通過Vue.extend利用傳入的definition生成Vue子類
  • Vue子類放到全局Vue.options.components

那么Vue.extend是如何創(chuàng)建出Vue的子類呢?下面我們來實現(xiàn)Vue.extend函數(shù)

Vue.extend

Vue.extend利用JavaScript原型鏈實現(xiàn)繼承,我們會將Vue.prototype指向Sub.prototype.__proto__,這樣就可以在Sub的實例上調(diào)用Vue原型上定義的方法了:

Vue.extend = function (extendOptions) {
  const Super = this;
  const Sub = function VueComponent () {
    // 會根據(jù)原型鏈進行查找,找到Super.prototype.init方法
    this._init();
  };
  Sub.cid = cid++;
  // Object.create將Sub.prototype的原型指向了Super.prototype
  Sub.prototype = Object.create(Super.prototype);
  // 此時prototype為一個對象,會失去原來的值
  Sub.prototype.constructor = Sub;
  Sub.options = mergeOptions(Super.options, extendOptions);
  Sub.component = Super.component;
  return Sub;
};

如果有小伙伴對JavaScript原型鏈不太了解的話,可以看筆者的這篇文章: 一文徹底理解JavaScript原型與原型鏈

核心的繼承代碼如下:

const Super = Vue
const Sub = function VueComponent () {
  // some code ...
};
// Object.create將Sub.prototype的原型指向了Super.prototype
Sub.prototype = Object.create(Super.prototype);
// 此時prototype為一個對象,會失去原來的值
Sub.prototype.constructor = Sub;

Object.create會創(chuàng)建一個新對象,使用一個已經(jīng)存在的對象作為新對象的原型。這里將創(chuàng)建的新對象賦值給了Sub.prototype,相當于做了如下倆件事:

  • Sub.prototype = {}
  • Sub.prototype.__proto__ = Super.prototype

Sub.prototype賦值后,其之前擁有的constructor屬性便會被覆蓋,這里需要再手動指定一下Sub.prototype.constructor = Sub

最終Vue.extend會將生成的子類返回,當用戶實例化這個子類時,便會通過this._init執(zhí)行子類的初始化方法創(chuàng)建組件

組件渲染流程

在用戶執(zhí)行new Vue創(chuàng)建組件的時候,會執(zhí)行this._init方法。在該方法中,會將用戶傳入的配置項和Vue.options中定義的配置項進行合并,最終放到vm.$options中:

function initMixin (Vue) {
  Vue.prototype._init = function (options = {}) {
    const vm = this;
    // 組件選項和Vue.options或者 Sub.options進行合并
    vm.$options = mergeOptions(vm.constructor.options, options);
    // ...
  };
  // ...
}

執(zhí)行到這里時,mergeOptoins會將用戶傳入options中的componentsVue.options.components中通過Vue.component定義的組件進行合并。

merge-options.js中,我們?yōu)?code>strategies添加合并components的策略:

strategies.components = function (parentVal, childVal) {
  const result = Object.create(parentVal); // 合并后的原型鏈為parentVal
  for (const key in childVal) { // childVal中的值都設(shè)置為自身私有屬性,會優(yōu)先獲取
    if (childVal.hasOwnProperty(key)) {
      result[key] = childVal[key];
    }
  }
  return result;
};

components的合并利用了JavaScript的原型鏈,將Vue.options.components中的全局組件放到了合并后對象的原型上,而將optionscomponents 屬性定義的局部組件放到了自身的屬性上。這樣當取值時,首先會從自身屬性上查找,然后再到原型鏈上查找,也就是優(yōu)先渲染局部組件,如果沒有局部組件就會去渲染全局組件。

合并完components之后,接下來要創(chuàng)建組件對應(yīng)的虛擬節(jié)點:

function createVComponent (vm, tag, props, key, children) {
  const baseCtor = vm.$options._base;
  // 在生成父虛擬節(jié)點的過程中,遇到了子組件的自定義標簽。它的定義放到了父組件的components中,所有通過父組件的$options來進行獲取
  // 這里包括全局組件和自定義組件,內(nèi)部通過原型鏈進行了合并
  let Ctor = vm.$options.components[tag];
  // 全局組件:Vue子類構(gòu)造函數(shù),局部組件:對象,合并后的components中既有對象又有構(gòu)造函數(shù),這里要利用Vue.extend統(tǒng)一處理為構(gòu)造函數(shù)
  if (typeof Ctor === 'object') {
    Ctor = baseCtor.extend(Ctor);
  }
  props.hook = { // 在渲染真實節(jié)點時會調(diào)用init鉤子函數(shù)
    init (vNode) {
      const child = vNode.componentInstance = new Ctor();
      child.$mount();
    }
  };
  return vNode(`vue-component-${Ctor.id}-${tag}`, props, key, undefined, undefined, { Ctor, children });
}

function createVElement (tag, props = {}, ...children) {
  const vm = this;
  const { key } = props;
  delete props.key;
  if (isReservedTag(tag)) { // 是否為html的原生標簽
    return vNode(tag, props, key, children);
  } else {
    // 創(chuàng)建組件虛擬節(jié)點
    return createVComponent(vm, tag, props, key, children);
  }
}

在創(chuàng)建虛擬節(jié)點時,如果tag不是html中定義的標簽,便需要創(chuàng)建組件對應(yīng)的虛擬節(jié)點。

組件虛擬節(jié)點中做了下面幾件事:

  • 通過vm.$options拿到合并后的components
  • Vue.extendcomponents中的對象轉(zhuǎn)換為Vue子類構(gòu)造函數(shù)
  • 在虛擬節(jié)點上的props上添加鉤子函數(shù),方便在之后調(diào)用
  • 執(zhí)行vNode函數(shù)創(chuàng)建組件虛擬節(jié)點,組件虛擬節(jié)點會新增componentOptions屬性來存放組件的一些選項

在生成虛擬節(jié)點之后,便會通過虛擬節(jié)點來創(chuàng)建真實節(jié)點,如果是組件虛擬節(jié)點要單獨處理:

// 處理組件虛擬節(jié)點
function createComponent (vNode) {
  let init = vNode.props?.hook?.init;
  init?.(vNode);
  if (vNode.componentInstance) {
    return true;
  }
}

// 將虛擬節(jié)點處理為真實節(jié)點
function createElement (vNode) {
  if (typeof vNode.tag === 'string') {
    if (createComponent(vNode)) {
      return vNode.componentInstance.$el;
    }
    vNode.el = document.createElement(vNode.tag);
    updateProperties(vNode);
    for (let i = 0; i < vNode.children.length; i++) {
      const child = vNode.children[i];
      vNode.el.appendChild(createElement(child));
    }
  } else {
    vNode.el = document.createTextNode(vNode.text);
  }
  return vNode.el;
}

在處理虛擬節(jié)點時,我們會獲取到在創(chuàng)建組件虛擬節(jié)點時為props添加的init鉤子函數(shù),將vNode傳入執(zhí)行init函數(shù):

props.hook = { // 在渲染真實節(jié)點時會調(diào)用init鉤子函數(shù)
  init (vNode) {
    const child = vNode.componentInstance = new Ctor();
    child.$mount();
  }
};

此時便會通過new Ctor()來進行子組件的一系列初始化工作:

  • this._init
  • initState
  • ...

Ctor是通過Vue.extend來生成的,而在執(zhí)行Vue.extend的時候,我們已經(jīng)將組件對應(yīng)的配置項傳入。但是由于配置項中缺少el選項,所以要手動執(zhí)行$mount方法來掛載組件。

在執(zhí)行$mount之后,會將組件template創(chuàng)建為真實DOM并設(shè)置到vm.$el選項上。執(zhí)行props.hook.init方法時,將組件實例放到了vNodecomponentInstance 屬性上,最終在createComponent中會判斷如果有該屬性則為組件虛擬節(jié)點,并將其對應(yīng)的DOM(vNode.componentInstance.$el)返回,最終掛載到父節(jié)點上,渲染到頁面中。

整個渲染流程畫圖總結(jié)一下:

總結(jié)

明白了組件渲染流程之后,最后我們來看一下父子組件的生命周期函數(shù)的執(zhí)行過程:

<div id="app">
  {{ name }}
  <aa></aa>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    components: {
      aa: {
        template: `<button>aa</button>`,
        beforeCreate () {
          console.log('child beforeCreate');
        },
        created () {
          console.log('child created');
        },
        beforeMount () {
          console.log('child beforeMount');
        },
        mounted () {
          console.log('child mounted');
        }
      },
    },
    data () {
      return {
        name: 'ss'
      };
    },
    beforeCreate () {
      console.log('parent beforeCreate');
    },
    created () {
      console.log('parent created');
    },
    beforeMount () {
      console.log('parent beforeMount');
    },
    mounted () {
      console.log('parent mounted');
    }
  });
</script>

在理解了Vue的組件渲染流程后,便可以很輕易的解釋這個打印結(jié)果了:

  • 首先會初始化父組件,執(zhí)行父組件的beforeCreate,created鉤子
  • 接下來會掛載父組件,在掛載之前會先執(zhí)行beforeMount鉤子
  • 當父組件開始掛載時,首先會生成組件虛擬節(jié)點,之后在創(chuàng)建真實及節(jié)點時,要new SubComponent來創(chuàng)建子組件,得到子組件掛載后的真實DOM:vm.$el
  • 而在實例化子組件的過程中,會執(zhí)行子組件的beforeCreate,created,beforeMount,mounted鉤子
  • 在子組件掛載完畢后,繼續(xù)完成父組件的掛載,執(zhí)行父組件的mounted鉤子

到此這篇關(guān)于Vue 組件渲染詳情的文章就介紹到這了,更多相關(guān)Vue 組件渲染內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaScript使用SpreadJS創(chuàng)建Excel查看器

    JavaScript使用SpreadJS創(chuàng)建Excel查看器

    在現(xiàn)代的Web應(yīng)用開發(fā)中,Excel文件的處理和展示是一項常見的需求,小編今天將為大家展示如何借助SpreadJS來創(chuàng)建一個Excel查看器,感興趣的小伙伴可以了解下
    2023-12-12
  • moment.js 時間日期處理詳解

    moment.js 時間日期處理詳解

    這篇文章主要介紹了moment.js 時間日期處理詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • ComboBox(下拉列表框)通過url加載調(diào)用遠程數(shù)據(jù)的方法

    ComboBox(下拉列表框)通過url加載調(diào)用遠程數(shù)據(jù)的方法

    這篇文章主要介紹了ComboBox(下拉列表框)通過url加載調(diào)用遠程數(shù)據(jù)的方法 ,需要的朋友可以參考下
    2017-08-08
  • js change,propertychange,input事件小議

    js change,propertychange,input事件小議

    github上關(guān)于mootools一個issue的討論很有意思,所以就想測試記錄下。感興趣的可以點擊原頁面看看
    2011-12-12
  • 基于JSONP原理解析(推薦)

    基于JSONP原理解析(推薦)

    下面小編就為大家推薦一篇基于JSONP原理解析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • 微信小程序?qū)崿F(xiàn)簡單吸頂效果

    微信小程序?qū)崿F(xiàn)簡單吸頂效果

    這篇文章主要為大家詳細介紹了微信小程序?qū)崿F(xiàn)簡單吸頂效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 微信小程序輸入多行文本的實戰(zhàn)記錄

    微信小程序輸入多行文本的實戰(zhàn)記錄

    多行輸入框組件是原生組件,這篇文章主要給大家介紹了關(guān)于微信小程序輸入多行文本的相關(guān)資料,文中通過實例代碼介紹的介紹的非常詳細,需要的朋友可以參考下
    2022-03-03
  • JSONP基礎(chǔ)知識詳解

    JSONP基礎(chǔ)知識詳解

    JSONP是JSON with padding(填充式JSON或參數(shù)式JSON)的簡寫,是應(yīng)用JSON的一種新方法,常用于務(wù)器與客戶端跨源通信,在后來的Web服務(wù)中非常流行。本文將詳細介紹JSONP,下面跟著小編一起來看下吧
    2017-03-03
  • JavaScript實現(xiàn)滑塊驗證解鎖

    JavaScript實現(xiàn)滑塊驗證解鎖

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)滑塊驗證解鎖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • 微信小程序獲取手機網(wǎng)絡(luò)狀態(tài)的方法【附源碼下載】

    微信小程序獲取手機網(wǎng)絡(luò)狀態(tài)的方法【附源碼下載】

    這篇文章主要介紹了微信小程序獲取手機網(wǎng)絡(luò)狀態(tài)的方法,涉及微信小程序wx.getNetworkType函數(shù)檢查網(wǎng)絡(luò)連接狀態(tài)的相關(guān)使用技巧,并附帶源碼供讀者下載參考,需要的朋友可以參考下
    2017-12-12

最新評論

灵石县| 罗平县| 西宁市| 湖北省| 民乐县| 安义县| 黄冈市| 万宁市| 广丰县| 绥阳县| 玉树县| 台北县| 丰宁| 涿鹿县| 章丘市| 蒙阴县| 马山县| 博爱县| 长泰县| 新巴尔虎左旗| 阿鲁科尔沁旗| 铜山县| 马鞍山市| 博兴县| 清水河县| 军事| 高雄县| 石渠县| 安阳县| 尚志市| 九台市| 区。| 潍坊市| 疏勒县| 汤阴县| 筠连县| 连城县| 浪卡子县| 威海市| 博客| 宁国市|