Vue 組件創(chuàng)建的流程源碼分析

創(chuàng)建組件的時(shí)候都會(huì)調(diào)用 Vue.extend

注冊(cè)組件第二個(gè)參數(shù)默認(rèn)會(huì)調(diào)用extend,Vue.extend 使用Vue基礎(chǔ)構(gòu)造器 產(chǎn)生子類(lèi)
Vue.extend() 中data必須是一個(gè)函數(shù),繼承與Vue,可以new和掛載
Vue.component("",Vue.extend({})) //傳入的是對(duì)象

  • 子類(lèi)可以繼承父類(lèi) init
  • 每個(gè)組件都有自己的數(shù)據(jù),希望每個(gè)組件都是獨(dú)立的所以希望new創(chuàng)造一個(gè)實(shí)例

為啥data必須是函數(shù),而不是對(duì)象

class Component{
    constructor(){
        this.data = obj;
    }
}
new Component().data
new Component().data

這樣復(fù)用了同一個(gè)對(duì)象所以改寫(xiě)為,這樣new的時(shí)候可以拿到全新的對(duì)象

let fn = function(){
    return {
    }
}
class Component{
    constructor(){
        this.data = fn;
    }
}

重寫(xiě)Sub.prototype.constructor 指向(源碼中有寫(xiě))

  • Sub.prototype = Object.create(Super.prototype);
  • Object.setPrototypeOf()
  • Object.create 原理
function create(parentProtptype){
    const Fn = function(){}
    Fn.prototype = parentProtptype
    return new Fn;
}
Sun.prototype = Object.create(Super.prototype);//繼承原型方法
Sub.prototype.constructor = Sub;//Object.create 會(huì)產(chǎn)生一個(gè)新的實(shí)例作為子類(lèi)的原型,此時(shí)constructor會(huì)指向錯(cuò)誤

因?yàn)閚ew Fn的constructor指向的是Parent,所以Sub.prototype需要重寫(xiě)

———————————————— 分割線 ————————————————————

組件初始化流程

1:調(diào)用initGlobalAPI里邊有2個(gè)重要的方法

  • Vue.extend:產(chǎn)生Vue的子類(lèi)(實(shí)例),其中mergeOptions合并了全局屬性和子組件的屬性到,組件實(shí)例的options上
  • Vue.component:將產(chǎn)生的組件是放到 Vue.options.components[組件名稱(chēng)]全局上
export function initGlobalAPI(Vue) {
    Vue.options = {};//全局屬性 每個(gè)組件初始化的時(shí)候 將這些屬性放到每個(gè)組件桑

    Vue.mixin = function(options){
        this.options = mergeOptions(this.options,options);
        console.log(this.options);
        return this;
    }
    //vue.component -> Vue.extend
    Vue.options._base = Vue;
    //等會(huì)我通知Vue.extend方法可以產(chǎn)生一個(gè)子類(lèi) new子類(lèi)的時(shí)候會(huì)執(zhí)行代碼初始化流程(組件的初始化)
    Vue.extend = function (opt) {//會(huì)產(chǎn)生一個(gè)子類(lèi)
        const Super = this;
        const Sub = function (options) {//創(chuàng)造一個(gè)組件 其實(shí)就是new這個(gè)組件的類(lèi)(組件的初始化)
            this._init(options)
        }
        Sub.prototype = Object.create(Super.prototype);//繼承原型方法
        Sub.prototype.constructor = Sub;//Object.create 會(huì)產(chǎn)生一個(gè)新的實(shí)例作為子類(lèi)的原型,此時(shí)constructior會(huì)指向錯(cuò)誤 constructor就這個(gè)問(wèn)題
        
        //父類(lèi)的屬性要和子類(lèi)的屬性進(jìn)行合并
        Sub.options = mergeOptions(Super.options,opt);//需要讓子類(lèi) 能拿到我們Vue定義的全局組件
        //Sub.mixin = Vue.mixin; nextTixk等等
        return Sub;//產(chǎn)生Vue的子類(lèi)
    }
    Vue.options.components = {};//存放全局組件的
    Vue.component = function name(id, definition) {//definition 可以傳入對(duì)象或者函數(shù)
        let name = definition.name || id;
        definition.name = name;
        if(isObject(definition)){
            definition = Vue.extend(definition);
        }
        Vue.options.components[name] = definition;//緩存組件到全局上(維護(hù)關(guān)系)
        console.log(Vue.options.components);
    }

}

在renderMixin的時(shí)候會(huì)調(diào)用

 Vue.prototype._c = function () { // createElement 創(chuàng)建元素型的節(jié)點(diǎn)
        const vm = this;
        return createElement(vm, ...arguments)
    }

所以在createElement,需要對(duì)組件進(jìn)行處理(要區(qū)分組件和普通元素去創(chuàng)建虛擬節(jié)點(diǎn))

function createComponent(vm,tag,data,children,key,Ctor) {
  if(isObject(Ctor)){//組件的定義一定是通過(guò)Vue.extend進(jìn)行包裹的
    Ctor = vm.$options_base.extend(Ctor)
  }
  //組件的hook 源碼里是進(jìn)行遍歷掛載hook
  data.hook = {
    //組件的生命周期
    init(){
    },
    //組件的更新流程
    prepatch(){
    },
   //......
  }
  //每個(gè)組件 默認(rèn)的名字內(nèi)部都會(huì)給你拼接一下 vue-component-1-my-button(數(shù)組是一個(gè)序號(hào) 每new一個(gè)+1)
  let componentOptions = vnode(vm,tag,data,undefined,key,undefined,{Ctor,children,tag,propsData,listeners});
  return componentOptions;//componentOptions(非常重要包含了Ctor children等) 存放了一個(gè)重要的屬性Ctor
}
//tag 有可能是組件 createElement({}) createElement(function(){})
export function createElement(vm, tag, data = {}, ...children) { // 返回虛擬節(jié)點(diǎn) _c('',{}....)

   //這個(gè)時(shí)候是虛擬節(jié)點(diǎn) 還沒(méi)有nodeType; 這里不考慮復(fù)雜的就是string類(lèi)型 看看是不是普通標(biāo)簽 
  if (!isReservedTag(tag)) {
    //Ctor 是組件最重要的屬性
    let Ctor = vm.$options.components[tag]; // 組件的初始化 就是new 組件的構(gòu)造函數(shù) tag標(biāo)簽
    //創(chuàng)建組件的虛擬節(jié)點(diǎn)  如果是組件要拿到組件的定義
    return createComponent(vm, tag, data, children, data.key, Ctor);
  }

  return vnode(vm, tag, data, children, data.key, undefined)
}

export function createText(vm, text) { // 返回虛擬節(jié)點(diǎn)
  return vnode(vm, undefined, undefined, undefined, undefined, text)
}

//options 可能是對(duì)象(自己定義的組件) 所以不放Ctor
function vnode(vm,tag,data,children,key,text,options) {
  return {
    vm, tag, data, children,key,text,componentOptions:options
  }
}

判斷是不是原始的標(biāo)簽 還是組件的方法

function makeMap(str) {
  let tagList = str.split(',');
  return function (tagName) {
    return tagList.includes(tagName)
  }
}

//這里的源碼在platform/web/util/element.js
export const isReservedTag = makeMap(
  'html,body,base,head,link,meta,style,title,' +
  'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
  'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
  'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
  's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
  'embed,object,param,source,canvas,script,noscript,del,ins,' +
  'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
  'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
  'output,progress,select,textarea,' +
  'details,dialog,menu,menuitem,summary,' +
  'content,element,shadow,template,blockquote,iframe,tfoot'
)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容