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 相关文章推荐
快速解决vue2+vue-cli3项目ie兼容的问题
Nov 17 Vue.js
vue祖孙组件之间的数据传递案例
Dec 07 Vue.js
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
Dec 25 Vue.js
Vue实现摇一摇功能(兼容ios13.3以上)
Jan 26 Vue.js
vue3.0 自适应不同分辨率电脑的操作
Feb 06 Vue.js
如何在 Vue 中使用 JSX
Feb 14 Vue.js
vue3.0 项目搭建和使用流程
Mar 04 Vue.js
Vue全家桶入门基础教程
May 14 Vue.js
详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
Nov 27 Vue.js
Vue+Flask实现图片传输功能
Apr 01 Vue.js
教你部署vue项目到docker
Apr 05 Vue.js
使用vue判断当前环境是安卓还是IOS
Apr 12 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
编写PHP的安全策略
2006/10/09 PHP
8个必备的PHP功能实例代码
2013/10/27 PHP
php绘制一条弧线的方法
2015/01/24 PHP
PHP中将一个字符串部分字符用星号*替代隐藏的实现代码
2019/09/08 PHP
Swoole扩展的6种模式深入详解
2021/03/04 PHP
深入理解JavaScript系列(2) 揭秘命名函数表达式
2012/01/15 Javascript
js中通过split函数分割字符串成数组小例子
2013/09/21 Javascript
JS OffsetParent属性深入解析
2014/01/13 Javascript
JavaScript操作DOM元素的childNodes和children区别
2015/04/01 Javascript
JQuery动态添加Select的Option元素实现方法
2016/08/29 Javascript
JS实现间歇滚动的运动效果实例
2016/12/22 Javascript
基于AGS JS开发自定义贴图图层
2017/03/31 Javascript
JavaScript实现三级联动菜单效果
2017/08/16 Javascript
基于Axios 常用的请求方法别名(详解)
2018/03/13 Javascript
Angular父组件调用子组件的方法
2018/04/02 Javascript
Layui实现主窗口和Iframe层参数传递
2019/11/14 Javascript
[00:34]DOTA2上海特级锦标赛 VG战队宣传片
2016/03/04 DOTA
Python常用随机数与随机字符串方法实例
2015/04/09 Python
用yum安装MySQLdb模块的步骤方法
2016/12/15 Python
对python:print打印时加u的含义详解
2018/12/15 Python
Python3 单行多行万能正则匹配方法
2019/01/07 Python
python 图片二值化处理(处理后为纯黑白的图片)
2019/11/01 Python
基于python修改srt字幕的时间轴
2020/02/03 Python
在python中利用dict转json按输入顺序输出内容方式
2020/02/27 Python
pyecharts调整图例与各板块的位置间距实例
2020/05/16 Python
Python实现爬取网页中动态加载的数据
2020/08/17 Python
无畏的旅行:Intrepid Travel
2017/12/20 全球购物
Ancheer官方户外和运动商店:销售电动自行车
2019/08/07 全球购物
2013年学期结束动员演讲稿
2014/01/07 职场文书
5.1手机促销活动
2014/01/17 职场文书
《小动物过冬》教学反思
2014/04/17 职场文书
2014年党的群众路线整改措施思想汇报
2014/10/12 职场文书
2014年全国法制宣传日宣传活动方案
2014/11/02 职场文书
初中毕业生感言
2015/07/31 职场文书
史上最全书信经典范文大全(建议收藏)
2019/07/10 职场文书
如何使用pdb进行Python调试
2021/06/30 Python