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 相关文章推荐
详解Vue3 Teleport 的实践及原理
Dec 02 Vue.js
Vue router传递参数并解决刷新页面参数丢失问题
Dec 02 Vue.js
vue实现两个区域滚动条同步滚动
Dec 13 Vue.js
vue3中轻松实现switch功能组件的全过程
Jan 07 Vue.js
Vue实现todo应用的示例
Feb 20 Vue.js
Vue实现tab导航栏并支持左右滑动功能
Jun 28 Vue.js
Axios代理配置及封装响应拦截处理方式
Apr 07 Vue.js
vue如何实现关闭对话框后刷新列表
Apr 08 Vue.js
VUE之图片Base64编码使用ElementUI组件上传
Apr 09 Vue.js
vue的项目如何打包上线
Apr 13 Vue.js
vue3语法糖内的defineProps及defineEmits
Apr 14 Vue.js
vue 数字翻牌器动态加载数据
Apr 20 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
《雄兵连》《烈阳天道》真的来了
2020/07/13 国漫
PHPStrom中实用的功能和快捷键大全
2015/09/23 PHP
UPUPW 更新 64 位 Apache 系列 PHP 7.0 正式版
2015/12/08 PHP
Document 对象的常用方法
2009/07/31 Javascript
正则表达式搭配js轻松处理json文本方便而老古
2013/02/17 Javascript
解析ScrollPic在ie8下只滚动一遍,然后变为空白 ie6,ie7,chrome,firefox正常
2013/06/26 Javascript
Textarea根据内容自适应高度
2013/10/28 Javascript
css样式标签和js语法属性区别
2013/11/06 Javascript
JS+CSS实现带小三角指引的滑动门效果
2015/09/22 Javascript
JS清除文本框内容离开在恢复及鼠标离开文本框时触发js的方法
2016/01/12 Javascript
浅析在javascript中创建对象的各种模式
2016/05/06 Javascript
js将json格式的对象拼接成复杂的url参数方法
2016/05/25 Javascript
canvas绘图不清晰的解决方案
2017/02/28 Javascript
在Vue中使用highCharts绘制3d饼图的方法
2018/02/08 Javascript
关于vue中 $emit的用法详解
2018/04/12 Javascript
Angular动画实现的2种方式以及添加购物车动画实例代码
2018/08/09 Javascript
详解如何在Angular优雅编写HTTP请求
2018/12/05 Javascript
详解使用React制作一个模态框
2019/03/14 Javascript
vue实现文字加密功能
2019/09/27 Javascript
vue项目创建步骤及路由router
2020/01/14 Javascript
python应用程序在windows下不出现cmd窗口的办法
2014/05/29 Python
python基于urllib实现按照百度音乐分类下载mp3的方法
2015/05/25 Python
Java多线程实现四种方式原理详解
2020/06/02 Python
canvas实现有递增动画的环形进度条的实现方法
2019/07/10 HTML / CSS
Belle Maison倍美丛官网:日本千趣会旗下邮购网站
2016/07/22 全球购物
TUMI澳大利亚网站:美国旅行箱包品牌
2017/03/27 全球购物
会计专业毕业生自我评价
2013/09/25 职场文书
应聘编辑自荐信范文
2014/03/12 职场文书
演讲比赛的活动方案
2014/08/28 职场文书
美国旅游签证工作证明
2014/10/14 职场文书
银行党员批评与自我批评
2014/10/15 职场文书
交心谈心活动总结
2015/05/11 职场文书
初中政教处工作总结
2015/08/12 职场文书
Matlab求解数组中的最大值及它所在的具体位置
2021/04/16 Python
Jupyter notebook 输出部分显示不全的解决方案
2021/04/24 Python
Java 数组内置函数toArray详解
2021/06/28 Java/Android