vue组件是如何解析及渲染的?


Posted in Vue.js onJanuary 13, 2021

前言

本文将对vue组件如何解析以及渲染做一个讲解。

我们可以通过Vue.component注册全局组件,之后可以在模板中进行使用

<div id="app">
  <my-button></my-button>
</div>
<script>
 Vue.component("my-button", {
    template: "<button> 按钮组件</button>",
   });
let vm = new Vue({
	el:'#app'
});
</script>

全局组件解析原理

为了保证组件的隔离,每个组件通过extend方法产生一个新的类,去继承父类。并把用户通过Vue.component方法传入的 opts 合并到 vue.options.components,再vue初始化时合并Vue.options.components 和 vm.$options.components 。

1.Vue.component 方法

Vue.options._base = Vue; //可以通过\_base 找到 vue
Vue.options.components = {}; 

Vue.component = function (id, definition) {
  //每个组件产生一个新的类去继承父亲
  definition = this.options._base.extend(definition);

  console.log("1.给组件创造一个构造函数,基于Vue", definition);

  this.options.components[id] = definition;
 };

2.Vue.extend 方法

extend 方法就是产生一个继承于 Vue 的类,并且他身上应该有父类的所有功能。

import {mergeOptions} from '../util/index'
Vue.extend = function (definition) {
  const Vue = this;
  const Sub = function VueComponent(options) {
   this._init(options);
  };
  Sub.prototype = Object.create(Vue.prototype);
  Sub.prototype.constructor = Sub;
  Sub.options = mergeOptions(Vue.options, definition); 
  return Sub;
 };

3.属性合并

合并Vue.options 和 Vue.component(definition)传入的 definition

strats.components = function (parentVal, childVal) {
 let options = Object.create(parentVal);
 if (childVal) {
  for (let key in childVal) {
   options[key] = childVal[key];
  }
 }
 return options;
};

4.初始化合并

合并Vue.options.components 和 vm.$options.components

Vue.prototype._init = function (options) {
  const vm = this;
 ++ vm.$options = mergeOptions(vm.constructor.options, options); 
  //...
  initState(vm);
  if (vm.$options.el) {
   //将数据挂载到这个模版上
   vm.$mount(vm.$options.el);
  }
 };

好哒,到这里我们就实现了全局组件的解析。

下面我们再来康康组件如何渲染的吧?

组件的渲染原理

在创建虚拟节点时我们要通过isReservedTag 判断当前这个标签是否是组件,普通标签的虚拟节点和组件的虚拟节点有所不同,如果 tag 是组件 应该渲染一个组件的 vnode。

export function isReservedTag(str) {
 let reservedTag = "a,div,span,p,img,button,ul,li";
 return reservedTag.includes(str);
}

1.创建组件虚拟节点

createComponent 创建组件的虚拟节点,通过data上有无hook来区分是否为组件

export function createElement(vm, tag, data = {}, ...children) {
  // 如果tag是组件 应该渲染一个组件的vnode
  if (isReservedTag(tag)) {
    return vnode(vm, tag, data, data.key, children, undefined);
  } else {
    const Ctor = vm.$options.components[tag]
    return createComponent(vm, tag, data, data.key, children, Ctor);
  }
}
// 创建组件的虚拟节点, 为了区分组件和元素 data.hook 
function createComponent(vm, tag, data, key, children, Ctor) {
  // 组件的构造函数
  if(isObject(Ctor)){
    Ctor = vm.$options._base.extend(Ctor); // Vue.extend 
  }
  data.hook = { // 等会渲染组件时 需要调用此初始化方法
    init(vnode){
      let vm = vnode.componentInstance = new Ctor({_isComponent:true}); // new Sub 会用此选项和组件的配置进行合并
      vm.$mount(); // 组件挂载完成后 会在 vnode.componentInstance.$el 
    }
  }
  return vnode(vm,`vue-component-${tag}`,data,key,undefined,undefined,{Ctor,children})
}

2.创建组件的真实节点

typeof tag === "string",有可能是组件的虚拟节点,则调用createComponent。

export function patch(oldVnode,vnode){
  // 1.判断是更新还是要渲染
  if(!oldVnode){
    return createElm(vnode);
  }else{
    // ...
  }
}

function createElm(vnode) {
 let { tag, data, children, text, vm } = vnode;
 if (typeof tag === "string") {
  if (createComponent(vnode)) {
   //返回组件对应的真实节点
   return vnode.componentInstance.$el;
  }
  vnode.el = document.createElement(tag); // 虚拟节点会有一个el属性,对应真实节点
  children.forEach((child) => {
   vnode.el.appendChild(createElm(child));
  });
 } else {
  vnode.el = document.createTextNode(text);
 }
 return vnode.el;
}

createComponent 通过 data上是否有hook.init方法,判断是否组件虚拟节点

是的话则调用组件上 data.hook.init

创建组件实例,并赋值给vnode.componentInstance

vnode.componentInstance 有值说明对应组件的真实 dom 已经生成

function createComponent(vnode) {
  let i = vnode.data;
  if((i = i.hook) && (i = i.init)){
    i(vnode);
  }
  if(vnode.componentInstance){
    return true;
  }
}

调用init方法,创造组件的实例并该进行挂载

data.hook = {
  init(vnode){
    let child = vnode.componentInstance = new Ctor({});
    child.$mount(); // 组件的挂载
  }
}

小结

vue组件是如何解析及渲染的?

对组件进行 new 组件().$mount() => vm.$el

将组件的$el 插入到父容器中 (父组件)

就完成啦~

以上就是vue组件是如何解析及渲染的?的详细内容,更多关于vue 组件解析和渲染的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue 实现基础组件的自动化全局注册
Dec 25 Vue.js
vue实现防抖的实例代码
Jan 11 Vue.js
Vue看了就会的8个小技巧
Jan 21 Vue.js
如何在Vue项目中添加接口监听遮罩
Jan 25 Vue.js
Vue 3自定义指令开发的相关总结
Jan 29 Vue.js
Vue实现圆环进度条的示例
Feb 06 Vue.js
vue3.0 项目搭建和使用流程
Mar 04 Vue.js
手写Vue2.0 数据劫持的示例
Mar 04 Vue.js
关于Vue中的options选项
Mar 22 Vue.js
vue3语法糖内的defineProps及defineEmits
Apr 14 Vue.js
vue生命周期钩子函数以及触发时机
Apr 26 Vue.js
vue递归实现树形组件
Jul 15 Vue.js
vue实现一个获取按键展示快捷键效果的Input组件
Jan 13 #Vue.js
vue使用vue-quill-editor富文本编辑器且将图片上传到服务器的功能
Jan 13 #Vue.js
基于VUE实现简单的学生信息管理系统
Jan 13 #Vue.js
详解为什么Vue中的v-if和v-for不建议一起用
Jan 13 #Vue.js
vue自定义组件实现双向绑定
Jan 13 #Vue.js
Vue页面渲染中key的应用实例教程
Jan 12 #Vue.js
Vue项目中使用mock.js的完整步骤
Jan 12 #Vue.js
You might like
农民C键的运用技巧
2020/03/04 星际争霸
Optimizer与Debugger兼容性问题的解决方法
2008/12/01 PHP
PHP数据类型之布尔型的介绍
2013/04/28 PHP
php实现兼容2038年后Unix时间戳转换函数
2015/03/18 PHP
php结合正则批量抓取网页中邮箱地址
2015/05/19 PHP
PHP实现登陆表单提交CSRF及验证码
2017/01/24 PHP
lib.utf.js
2007/08/21 Javascript
javascript 面向对象编程 聊聊对象的事
2009/09/17 Javascript
JavaScript 高效运行代码分析
2010/03/18 Javascript
为jQuery增加join方法的实现代码
2010/11/28 Javascript
javascript自执行函数之伪命名空间封装法
2010/12/25 Javascript
javascript (用setTimeout而非setInterval)
2011/12/28 Javascript
js不能获取隐藏的div的宽度只能先显示后获取
2014/09/04 Javascript
Bootstrap Paginator分页插件使用方法详解
2016/05/30 Javascript
JavaScript 继承详解(六)
2016/10/11 Javascript
纯js的右下角弹窗实例
2017/03/12 Javascript
微信小程序request出现400的问题解决办法
2017/05/23 Javascript
微信小程序实现下拉刷新和轮播图效果
2017/11/21 Javascript
微信小程序表单验证功能完整实例
2017/12/01 Javascript
在vue-cli的组件模板里使用font-awesome的两种方法
2018/09/28 Javascript
JavaScript逻辑运算符相关总结
2020/09/04 Javascript
python 定时修改数据库的示例代码
2018/04/08 Python
PyQt5实现简单数据标注工具
2019/03/18 Python
python实现随机漫步方法和原理
2019/06/10 Python
python多线程扫描端口(线程池)
2019/09/04 Python
python爬虫 Pyppeteer使用方法解析
2019/09/28 Python
Django choices下拉列表绑定实例
2020/03/13 Python
CSS3实现渐变背景兼容问题
2020/05/06 HTML / CSS
Html5应用程序缓存(Cache manifest)
2018/06/04 HTML / CSS
巾帼文明岗申报材料
2014/05/01 职场文书
党的群众路线教育学习材料
2014/05/12 职场文书
学生安全承诺书
2014/05/22 职场文书
普通党员对照检查材料
2014/09/24 职场文书
项目投资意向书范本
2015/05/09 职场文书
css中z-index: 0和z-index: auto的区别
2021/08/23 HTML / CSS
SpringBoot整合Redis入门之缓存数据的方法
2021/11/17 Redis