美文网首页前端开发那些事儿
Vue 组件创建的流程源码分析

Vue 组件创建的流程源码分析

作者: vivianXIa | 来源:发表于2021-06-29 18:57 被阅读0次

    创建组件的时候都会调用 Vue.extend

    注册组件第二个参数默认会调用extend,Vue.extend 使用Vue基础构造器 产生子类
    Vue.extend() 中data必须是一个函数,继承与Vue,可以new和挂载
    Vue.component("",Vue.extend({})) //传入的是对象

    • 子类可以继承父类 init
    • 每个组件都有自己的数据,希望每个组件都是独立的所以希望new创造一个实例

    为啥data必须是函数,而不是对象

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

    这样复用了同一个对象所以改写为,这样new的时候可以拿到全新的对象

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

    重写Sub.prototype.constructor 指向(源码中有写)

    • 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 会产生一个新的实例作为子类的原型,此时constructor会指向错误
    

    因为new Fn的constructor指向的是Parent,所以Sub.prototype需要重写

    ———————————————— 分割线 ————————————————————

    组件初始化流程

    1:调用initGlobalAPI里边有2个重要的方法

    • Vue.extend:产生Vue的子类(实例),其中mergeOptions合并了全局属性和子组件的属性到,组件实例的options上
    • Vue.component:将产生的组件是放到 Vue.options.components[组件名称]全局上
    export function initGlobalAPI(Vue) {
        Vue.options = {};//全局属性 每个组件初始化的时候 将这些属性放到每个组件桑
    
        Vue.mixin = function(options){
            this.options = mergeOptions(this.options,options);
            console.log(this.options);
            return this;
        }
        //vue.component -> Vue.extend
        Vue.options._base = Vue;
        //等会我通知Vue.extend方法可以产生一个子类 new子类的时候会执行代码初始化流程(组件的初始化)
        Vue.extend = function (opt) {//会产生一个子类
            const Super = this;
            const Sub = function (options) {//创造一个组件 其实就是new这个组件的类(组件的初始化)
                this._init(options)
            }
            Sub.prototype = Object.create(Super.prototype);//继承原型方法
            Sub.prototype.constructor = Sub;//Object.create 会产生一个新的实例作为子类的原型,此时constructior会指向错误 constructor就这个问题
            
            //父类的属性要和子类的属性进行合并
            Sub.options = mergeOptions(Super.options,opt);//需要让子类 能拿到我们Vue定义的全局组件
            //Sub.mixin = Vue.mixin; nextTixk等等
            return Sub;//产生Vue的子类
        }
        Vue.options.components = {};//存放全局组件的
        Vue.component = function name(id, definition) {//definition 可以传入对象或者函数
            let name = definition.name || id;
            definition.name = name;
            if(isObject(definition)){
                definition = Vue.extend(definition);
            }
            Vue.options.components[name] = definition;//缓存组件到全局上(维护关系)
            console.log(Vue.options.components);
        }
    
    }
    

    在renderMixin的时候会调用

     Vue.prototype._c = function () { // createElement 创建元素型的节点
            const vm = this;
            return createElement(vm, ...arguments)
        }
    

    所以在createElement,需要对组件进行处理(要区分组件和普通元素去创建虚拟节点)

    function createComponent(vm,tag,data,children,key,Ctor) {
      if(isObject(Ctor)){//组件的定义一定是通过Vue.extend进行包裹的
        Ctor = vm.$options_base.extend(Ctor)
      }
      //组件的hook 源码里是进行遍历挂载hook
      data.hook = {
        //组件的生命周期
        init(){
        },
        //组件的更新流程
        prepatch(){
        },
       //......
      }
      //每个组件 默认的名字内部都会给你拼接一下 vue-component-1-my-button(数组是一个序号 每new一个+1)
      let componentOptions = vnode(vm,tag,data,undefined,key,undefined,{Ctor,children,tag,propsData,listeners});
      return componentOptions;//componentOptions(非常重要包含了Ctor children等) 存放了一个重要的属性Ctor
    }
    //tag 有可能是组件 createElement({}) createElement(function(){})
    export function createElement(vm, tag, data = {}, ...children) { // 返回虚拟节点 _c('',{}....)
    
       //这个时候是虚拟节点 还没有nodeType; 这里不考虑复杂的就是string类型 看看是不是普通标签 
      if (!isReservedTag(tag)) {
        //Ctor 是组件最重要的属性
        let Ctor = vm.$options.components[tag]; // 组件的初始化 就是new 组件的构造函数 tag标签
        //创建组件的虚拟节点  如果是组件要拿到组件的定义
        return createComponent(vm, tag, data, children, data.key, Ctor);
      }
    
      return vnode(vm, tag, data, children, data.key, undefined)
    }
    
    export function createText(vm, text) { // 返回虚拟节点
      return vnode(vm, undefined, undefined, undefined, undefined, text)
    }
    
    //options 可能是对象(自己定义的组件) 所以不放Ctor
    function vnode(vm,tag,data,children,key,text,options) {
      return {
        vm, tag, data, children,key,text,componentOptions:options
      }
    }
    

    判断是不是原始的标签 还是组件的方法

    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'
    )
    
    

    相关文章

      网友评论

        本文标题:Vue 组件创建的流程源码分析

        本文链接:https://www.haomeiwen.com/subject/nxpkultx.html