带你一行一行手写Vue2.0源码系列(六) -组件渲染

前言

在此之前页面渲染的流程,以及更新渲染的流程已经讲完了。我们在使用Vue时用到最多的其实还是组件,那么我们创建的组件又是如何渲染在页面当中的呢?

我个人推荐的看源码的方式最好是通过视频文章自己能手写一边然后再去看,否则就会造成这样的结果。

真是卷...

组件注册

ini 复制代码
// core/global-api/index.js

const ASSETS_TYPE = ["component", "directive", "filter"]; 
function initGlobalApi(Vue) {
    Vue.options = {};
    ASSETS_TYPE.forEach(type => {
        Vue.options[type + "s"] = {};
    });
}

initGlobalApi方法主要就是在Vue上挂载了静态对象options,用于存储后续注册的component, directive, filter

ini 复制代码
const ASSETS_TYPE = ["component", "directive", "filter"];
export default function initAssetRegisters(Vue) {
    ASSETS_TYPE.forEach(type => {
        Vue[type] = function(id, definition) {
            if(id === 'component') {
                definition = Vue.extend(definition);
            }
            this.options[type + "s"][id] = definition;
        }
    })
}

这里主要将组件实例通过全局Vue.extend注册在component上,后续寻找组件就options.components上找。

Vue.extend

javascript 复制代码
export function initExtend(Vue) {

  Vue.extend = function (options = {}) {
    function Sub(options = {}) { // 组件构造函数,并且调用init初始化方法
      this._init(options);
    }

    Sub.prototype = Object.create(Vue.prototype); // 组件原型指向Vue原型
    Sub.prototype.constructor = Sub; // 组件构造函数指向自己
    Sub.options = mergeOptions(Vue.options, options);

    return Sub; // 返回组件构造函数
  }

}

extend主要就是创建了子类的构造函数,并且进行初始化。通过mergeOptions将当前组件传入的options和我们全局的options进行合并,并且返回子类。

创建组件Vnode

javascript 复制代码
export const isReservedTag = (tag) => { // 是否是普通标签  这里只是举例了几个
  return ['a', 'div', 'h1', 'h2', 'h3', 'span', 'p', 'button', 'body', 'html'].includes(tag);
}

export function createElement(vm, tag, data = {}, ...children) {
  if (!data) {
    data = {};
  }
  let key = data.key;
  if (key) {
    delete data.key;
  }
  if (isReservedTag(tag)) { // 判断是不是普通元素标签
    return vnode(vm, tag, key, data, children);
  } else {
    let Ctor = vm.$options.components[tag];
    return createComponentVnode(vm, tag, key, data, children, Ctor);
  }
}

export function createComponentVnode(vm, tag, key, data, children, Ctor) { // 创建组件虚拟dom
  if (typeof Ctor === 'object') { // 如果传入的是个对象,就需要extend来转为组件构造函数
    Ctor = vm.constructor.extend(Ctor);
  }
  data.hook = { // 在属性上定义了组件钩子函数
    init(vnode) { // 创建真实节点所调用, 组件初始化钩子
      let instance = vnode.componentInstance = new vnode.componentOptions.Ctor();
      instance.$mount();
    }
  }
  
  // 返回组件虚拟Dom
  return vnode(vm, tag, key, data, children, null, { 
    Ctor
  });
}

在调用createElement创建Vnode时,它会判断当前标签名是否是普通标签,还是组件标签。如果时组件就需要从全局options.components中获取对应的组件构造函数,也有可能时组件实例对象,所以这里需要判断下,如果不是组件构造函数,就需要调用extend转为构造函数。并且在属性data上定义了init钩子函数,进行组件的挂载。

Vnode转为真实Dom

ini 复制代码
function createElm(vnode) {
  let { tag, data, children, text } = vnode;
  if (typeof tag === 'string') {
    // 创建真实元素需要区分是不是组件
    if (createComponentVnode(vnode)) {
      return vnode.componentInstance.$el;
    }
    vnode.el = document.createElement(tag);
    patchProps(vnode.el, {}, data);
    children.forEach(child => {
      vnode.el.appendChild(createElm(child));
    })
  } else {
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}

function createComponentVnode(vnode) {
  let i = vnode.data;
  if ((i = i.hook) && (i = i.init)) {
    i(vnode); // 初始化组件
  }
  if(vnode.componentInstance) { // 说明是组件
    return true;
  }
}

在将vnode转为真实Dom中,判断data上是否存在钩子函数,存在钩子函数的便是组件,直接调用钩子函数initvnode上定义componentInstance,直接返回vnode.componentInstance.el进行挂载

总结

组件定义就是将我们写的options对象通过extend转为子类Vue构造函数,并执行初始化方法,将组件定义在全局options.components上,以方便我们后续取组件。在创建组件Vnode时,判断当前标签是否时普通元素标签,如果是组件标签即会在data属性上定义init钩子函数,钩子即调用$mount进行挂载渲染。将虚拟dom转为真实dom其实就是判断当前data是否存在钩子函数,存在就是执行init进行挂载即可。

如果觉得本文有帮助 记得点赞三连哦 十分感谢!

vue2.0 和 vue3.0系列文章(后续更新)

相关推荐
_Feliz5 分钟前
vue2实现excel文件预览
vue.js·elementui·excel
玩具工匠7 分钟前
字玩FontPlayer开发笔记3 性能优化 大量canvas渲染卡顿问题
前端·javascript·vue.js·笔记·elementui·typescript
CodeClimb15 分钟前
【华为OD-E卷 - 服务失效判断 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
CodeClimb17 分钟前
【华为OD-E卷 - 九宫格按键输入 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
m0_7482487724 分钟前
YOLOv5部署到web端(flask+js简单易懂)
前端·yolo·flask
qwaesrdt320230 分钟前
【如何使用大语言模型(LLMs)高效总结多文档内容】
前端
Ace_31750887761 小时前
淘宝平台通过关键字搜索获取商品列表技术贴
前端
卸任1 小时前
国产 Dev/Ops 工具 Jpom 的前端项目自动化部署实践
运维·前端
一个处女座的程序猿O(∩_∩)O1 小时前
vue 如何实现复制和粘贴操作
前端·javascript·vue.js
赔罪1 小时前
HTML-列表标签
服务器·前端·javascript·vscode·html·webstorm