最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【Vue2.x 源码学习】第四十一篇 - 组件部分 - 生成组件的真实节点

    正文概述 掘金(BraveWang)   2021-08-15   605

    这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战


    一,前言

    上篇,介绍了组件部分-组件的生命周期,主要涉及以下几部分:

    本篇,组件部分-生成组件的真实节点;


    二,生成组件的真实节点

    1,前文回顾

    前篇,在 createElement 方法中,扩展了对组件的处理 createComponent:生成组件的虚拟节点;

    按照模板渲染流程,接下来会进入 patch 方法,其中的 createElm 方法:将虚拟节点转化成为真实节点;

    // todo 后续需详细描述相关流程,对 patch 和 createElm 方法进行必要的介绍和说明;

    2,createElm 方法

    在 patch 方法中,createElm 方法会将虚拟节点生成为真实节点: 通过vnode.el = document.createElement(tag)直接创建出真实节点

    export function createElm(vnode) {
      // vnode.el:绑定真实节点与虚拟节点的映射关系,便于后续的节点更新操作
    
      // 虚拟节点必备的三个:标签,数据,孩子
      let { tag, data, children, text, vm } = vnode;
      if (typeof tag === 'string') { // 处理元素节点
        // 创建真实节点
        vnode.el = document.createElement(tag) 
        updateProperties(vnode, data)  // 处理元素的 data 属性
        // 处理当前元素节点的儿子:递归创建儿子的真实节点,并添加到对应的父亲中
        children.forEach(child => { // 若不存在儿子,children为空数组
          vnode.el.appendChild(createElm(child))
        });
      } else { // 文本:文本中 tag 是 undefined
        vnode.el = document.createTextNode(text)  // 创建文本的真实节点
      }
      return vnode.el;
    } 
    

    现在由于组件的加入,createElm 方法中可能会存在 componentOptions:

    打印createElm 查看: 第一次:真实节点:id=app 【Vue2.x 源码学习】第四十一篇 - 组件部分 - 生成组件的真实节点 第二次:组件:my-button 【Vue2.x 源码学习】第四十一篇 - 组件部分 - 生成组件的真实节点

    添加对组件的处理

    /**
     * 创造组件的真实节点
     * @param {*} vnode 
     */
    function createComponent(vnode) {
      console.log(vnode) // my-button
    }
    
    export function createElm(vnode) {
      let { tag, data, children, text, vm } = vnode;
      if (typeof tag === 'string') {// 元素 or 组件
        // 添加对组件的处理
        if(createComponent(vnode)){ // 将组件的虚拟节点,创建成为组件的真实节点
    
        }
        // 创建真实节点
        vnode.el = document.createElement(tag) 
        updateProperties(vnode, data)
        children.forEach(child => {
          vnode.el.appendChild(createElm(child))
        });
      } else { // 文本
        vnode.el = document.createTextNode(text)
      }
      return vnode.el;
    } 
    

    3,组件的初始化 Hook

    之前在组件的 data 属性上,扩展出了生命周期 hook;

    在 createComponent 中获取 hook,如果有 hook 说明就是组件;

    拿到 hook中的 init 方法,并使用 init 方法处理 vnode:

    /**
     * 创造组件的真实节点
     * @param {*} vnode 
     */
    function createComponent(vnode) {
      console.log(vnode);
      let i = vnode.data;
      // 先确定有 hook;再拿到 init 方法;
      if((i = i.hook)&&(i = i.init)){ // 最后 i 为 init 方法
        i(vnode); // 使用 init 方法处理 vnode
      }
    }
    
    备注:
    先将 hook 赋值给 i:看是否有 hook,如果有 hook 就是组件;
    再将 hook 中的 init 方法赋值给 i;
    最终 i 就是 hook 上的init 方法;
    

    使用 hook 上的init 方法处理 vnode,在 hook 中进行中组件的初始化:

    /**
     * 创造组件的虚拟节点 componentVnode
     */
    function createComponent(vm, tag, data, children, key, Ctor) {
    
      if(isObject(Ctor)){
        Ctor = vm.$options._base.extend(Ctor)
      }
    
      data.hook = {
        init(){
          // 创建组件的实例并挂载
          let child = new Ctor({});
          child.$mount();
        },
        prepatch(){},
        postpatch(){}
      }
      
      let componentVnode = vnode(vm, tag, data, undefined, key, undefined, {Ctor, children, tag});
      return componentVnode;
    }
    

    在 new Ctor 时,会执行 _init 进行组件的初始化: // 调用子类的初始化 _init 方法

      Vue.prototype._init = function (options) {
        const vm = this;
        vm.$options = mergeOptions(vm.constructor.options, options);
        initState(vm);
        // 由于 el 不存在,所以不会执行 vm.$mount
        if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
      }
    

    通过child.$mount();进行挂在,但没有传参 el = null,所以不会挂载:

      Vue.prototype.$mount = function (el) {
        const vm = this;
        const opts = vm.$options;
        el = document.querySelector(el); // 获取真实的元素
        vm.$el = el;// $el:页面上的真实元素
    
        if (!opts.render) {
          let template = opts.template;
          if (!template) {
            template = el.outerHTML;
          }
    
          let render = compileToFunction(template);
          opts.render = render;
        }
    
        mountComponent(vm);
      }
    

    如果组件的 render 函数不存在,使用组件的 template 编译为 render函数,并保存起来,之后 mountComponent 进行组件的挂载:

    export function mountComponent(vm) {
    
      let updateComponent = ()=>{
        vm._update(vm._render());  
      }
    
      callHook(vm, 'beforeCreate');
      // 生成渲染 watcher :每个组件都具有独立的渲染 watcher
      new Watcher(vm, updateComponent, ()=>{
        callHook(vm, 'created');
      },true)
       callHook(vm, 'mounted');
    }
    

    updateComponent 会调用 _render 方法根据子组件创造虚拟节点:

      Vue.prototype._render = function () {
        const vm = this;
        let { render } = vm.$options;
        let vnode = render.call(vm);
        return vnode
      }
    

    通过 render.call 产生虚拟节点,这个 vnode 就是模板的 button


    三,结尾

    本篇,介绍了组件部分-生成组件的真实节点;

    下篇,待定


    下载网 » 【Vue2.x 源码学习】第四十一篇 - 组件部分 - 生成组件的真实节点

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元