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 使用class创建和清除水印的示例代码
Dec 25 Vue.js
为什么推荐使用JSX开发Vue3
Dec 28 Vue.js
Vue过滤器,生命周期函数和vue-resource简单介绍
Jan 12 Vue.js
vue 递归组件的简单使用示例
Jan 14 Vue.js
vue实现轮播图帧率播放
Jan 26 Vue.js
vue使用echarts画组织结构图
Feb 06 Vue.js
Vue中使用wangeditor富文本编辑的问题
Feb 07 Vue.js
Vue自定义铃声提示音组件的实现
Jan 22 Vue.js
Vue的生命周期一起来看看
Feb 24 Vue.js
vue3引入highlight.js进行代码高亮的方法实例
Apr 08 Vue.js
vue如何在data中引入图片的正确路径
Jun 05 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
php获取本地图片文件并生成xml文件输出具体思路
2013/04/27 PHP
php数组去重复数据示例
2014/02/25 PHP
php进程间通讯实例分析
2016/07/11 PHP
kindeditor 加入七牛云上传的实例讲解
2017/11/12 PHP
php/JS实现的生成随机密码(验证码)功能示例
2019/06/06 PHP
使用swoole 定时器变更超时未支付订单状态的解决方案
2019/07/24 PHP
CL vs ForZe BO5 第一场 2.13
2021/03/10 DOTA
JavaScript CSS修改学习第五章 给“上传”添加样式
2010/02/19 Javascript
JavaScript中函数声明优先于变量声明的实例分析
2012/03/01 Javascript
JQueryiframe页面操作父页面中的元素与方法(实例讲解)
2013/11/19 Javascript
Jquery对select的增、删、改、查操作
2015/02/06 Javascript
在JavaScript中使用JSON数据
2016/02/15 Javascript
JavaScript必知必会(九)function 说起 闭包问题
2016/06/08 Javascript
浅谈javascript中的constructor
2016/06/08 Javascript
整理一下常见的IE错误
2016/11/18 Javascript
jquery应用实例分享_实现手风琴特效
2018/02/01 jQuery
vue bus全局事件中心简单Demo详解
2018/02/26 Javascript
理解 JavaScript EventEmitter
2018/03/29 Javascript
关于Vue源码vm.$watch()内部原理详解
2019/04/26 Javascript
实现elementUI表单的全局验证的方法步骤
2019/04/29 Javascript
Element-ui中元素滚动时el-option超出元素区域的问题
2019/05/30 Javascript
Python的Flask开发框架简单上手笔记
2015/11/16 Python
Python设计模式编程中Adapter适配器模式的使用实例
2016/03/02 Python
mac安装scrapy并创建项目的实例讲解
2018/06/13 Python
Python使用psutil获取进程信息的例子
2019/12/17 Python
浅谈python print(xx, flush = True) 全网最清晰的解释
2020/02/21 Python
Python 输出详细的异常信息(traceback)方式
2020/04/08 Python
CSS3径向渐变之大鱼吃小鱼之孤单的大鱼
2016/04/26 HTML / CSS
Belle Maison倍美丛官网:日本千趣会旗下邮购网站
2016/07/22 全球购物
MySQL面试题目集锦
2016/04/14 面试题
机电专业个人自荐信格式模板
2013/09/23 职场文书
2014年圣诞节促销方案
2014/03/14 职场文书
谢师宴答谢词
2015/01/05 职场文书
学习保证书怎么写
2015/02/26 职场文书
2016年校园植树节广播稿
2015/12/17 职场文书
解决Maven项目中 Invalid bound statement 无效的绑定问题
2021/06/15 Java/Android