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 获取到数据但却渲染不到页面上的解决方法
Nov 19 Vue.js
Vue实现购物小球抛物线的方法实例
Nov 22 Vue.js
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
Dec 04 Vue.js
浅析vue中的nextTick
Dec 28 Vue.js
vue-quill-editor插入图片路径太长问题解决方法
Jan 08 Vue.js
vue监听键盘事件的相关总结
Jan 29 Vue.js
关于Vue Router的10条高级技巧总结
May 06 Vue.js
vue响应式原理与双向数据的深入解析
Jun 04 Vue.js
vue中控制mock在开发环境使用,在生产环境禁用方式
Apr 06 Vue.js
vue数据字典取键值项目的字典问题
Apr 12 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学习之PHP表达式
2006/10/09 PHP
FireFox浏览器使用Javascript上传大文件
2013/10/30 PHP
详解php比较操作符的安全问题
2015/12/03 PHP
php判断是否连接上网络的方法实例详解
2016/12/14 PHP
[原创]PHP实现SQL语句格式化功能的方法
2017/07/28 PHP
PHP递归遍历文件夹去除注释并压缩php源代码的方法示例
2018/05/23 PHP
基于PHP实现解密或加密Cloudflar邮箱保护
2020/06/24 PHP
Jquery实现鼠标移上弹出提示框、移出消失思路及代码
2013/05/19 Javascript
JS实现匀速运动的代码实例
2013/11/29 Javascript
JS字符串截取函数实例
2013/12/27 Javascript
jQuery中bind与live的用法及区别小结
2014/01/27 Javascript
jQuery Dialog对话框事件用法实例分析
2016/05/10 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
2016/08/01 Javascript
Jquery组件easyUi实现手风琴(折叠面板)示例
2016/08/23 Javascript
Vue 理解之白话 getter/setter详解
2019/04/16 Javascript
详解JavaScript中关于this指向的4种情况
2019/04/18 Javascript
JS学习笔记之贪吃蛇小游戏demo实例详解
2019/05/29 Javascript
wxPython框架类和面板类的使用实例
2014/09/28 Python
跟老齐学Python之坑爹的字符编码
2014/09/28 Python
Python 序列的方法总结
2016/10/18 Python
django1.8使用表单上传文件的实现方法
2016/11/04 Python
python+selenium开发环境搭建图文教程
2017/08/11 Python
使用11行Python代码盗取了室友的U盘内容
2018/10/23 Python
Python中super函数用法实例分析
2019/03/18 Python
如何用Python制作微信好友个性签名词云图
2019/06/28 Python
python如何删除文件中重复的字段
2019/07/16 Python
解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available
2019/07/31 Python
python:目标检测模型预测准确度计算方式(基于IoU)
2020/01/18 Python
Jupyter Notebook 实现正常显示中文和负号
2020/04/24 Python
毕业生精彩的自我评价分享
2013/10/06 职场文书
物流合作计划书
2014/01/10 职场文书
学年自我鉴定
2014/01/16 职场文书
工作疏忽检讨书
2014/01/25 职场文书
转学证明范本
2015/06/19 职场文书
2016年党员公开承诺书格式范文
2016/03/24 职场文书
postgresql中如何执行sql文件
2023/05/08 PostgreSQL