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 相关文章推荐
springboot+vue实现文件上传下载
Nov 17 Vue.js
vue添加自定义右键菜单的完整实例
Dec 08 Vue.js
vue实现两个区域滚动条同步滚动
Dec 13 Vue.js
vue+element实现动态加载表单
Dec 13 Vue.js
Vue中ref和$refs的介绍以及使用方法示例
Jan 11 Vue.js
vue组件是如何解析及渲染的?
Jan 13 Vue.js
vue+element table表格实现动态列筛选的示例代码
Jan 14 Vue.js
Vue 实例中使用$refs的注意事项
Jan 29 Vue.js
vue浏览器返回监听的具体步骤
Feb 03 Vue.js
详解vue3中组件的非兼容变更
Mar 03 Vue.js
vue+flask实现视频合成功能(拖拽上传)
Mar 04 Vue.js
vue修饰符.capture和.self的区别
Apr 22 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
yii框架中的Url生产问题小结
2012/01/16 PHP
php网站判断用户是否是手机访问的方法
2013/11/01 PHP
PHP的伪随机数与真随机数详解
2015/05/27 PHP
利用PHPExcel读取Excel的数据和导出数据到Excel
2017/05/12 PHP
laravel-admin 在列表页添加自定义按钮的例子
2019/09/30 PHP
Laravel5.1 框架Request请求操作常见用法实例分析
2020/01/04 PHP
使用IE6看老赵的博客 jQuery初探
2010/01/17 Javascript
Javascript字符串浏览器兼容问题分析
2014/12/01 Javascript
javascript实现 百度翻译 可折叠的分享按钮列表
2015/03/12 Javascript
jQuery扩展实现text提示还能输入多少字节的方法
2016/11/28 Javascript
Bootstrap基本组件学习笔记之面板(14)
2016/12/08 Javascript
Vue.js 2.5新特性介绍(推荐)
2017/10/24 Javascript
vue router的基本使用和配置教程
2018/11/05 Javascript
JavaScript函数定义方法实例详解
2019/03/05 Javascript
vue实现微信获取用户信息的方法
2019/03/21 Javascript
layui数据表格跨行自动合并的例子
2019/09/02 Javascript
学习Python selenium自动化网页抓取器
2018/01/20 Python
python全栈要学什么 python全栈学习路线
2019/06/28 Python
python实发邮件实例详解
2019/11/11 Python
Python关键字及可变参数*args,**kw原理解析
2020/04/04 Python
Python闭包装饰器使用方法汇总
2020/06/29 Python
意大利婴儿产品网上商店:Mukako
2018/10/14 全球购物
有机婴儿毛毯和衣服:Monica + Andy
2020/03/01 全球购物
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
职业生涯规划设计步骤
2014/01/12 职场文书
5.1手机促销活动
2014/01/17 职场文书
数控个人求职信范文
2014/02/03 职场文书
人事行政专员岗位职责
2014/07/23 职场文书
庆六一活动总结
2014/08/29 职场文书
研究生就业推荐表导师评语
2014/12/31 职场文书
入党积极分子群众意见
2015/06/01 职场文书
2015年学校总务工作总结
2015/07/20 职场文书
2016年国培心得体会及反思
2016/01/13 职场文书
解析MySQL binlog
2021/06/11 MySQL
浅谈MySQL 亿级数据分页的优化
2021/06/15 MySQL
「租借女友」第2季樱泽墨角色PV&新视觉图公开
2022/03/21 日漫