Vue手写实现组件初渲染

目录
  • 前言
  • 生成虚拟节点
  • 将虚拟节点处理为真实节点
  • 总结

前言

Vue进行文本编译之后,会得到代码字符串生成的render函数。本文会基于render函数介绍以下内容:

  • 执行render函数生成虚拟节点
  • 通过vm._update方法,将虚拟节点渲染为真实DOM

vm.$mount方法中,文本编译完成后,要进行组件的挂载,代码如下:

Vue.prototype.$mount = function (el) {
  // text compile code ....
  mountComponent(vm);
};

// src/lifecycle.js
export function mountComponent (vm) {
  vm._update(vm._render());
}

下面详细介绍vm._render()vm._update()中到底做了什么

生成虚拟节点

原生DOM节点拥有大量的属性和方法,操作DOM比较耗费性能。在Vue中通过一个对象来描述DOM中的节点,这个对象就是虚拟节点,Vue组件树构建的整个虚拟节点树就是虚拟DOM

这是一段html

<div id="app">
  <span>hello world {{name}}</span>
</div>
<script>
  new Vue({
    el: '#app',
    data () {
      return {
        name: 'zs'
      }
    }
  })
</script>

其对应的虚拟节点如下:

const vNode = {
  tag: 'div',
  props: { id: 'app' },
  key: undefined,
  children: [
    {
      tag: 'span',
      props: {},
      key: undefined,
      children: undefined,
      text: 'helloworldzs'
    }
  ],
  text: undefined
}

Vue.prototype._render函数中,通过执行文本编译后生成的render方法,会得到虚拟节点:

// src/vdom/index.js
Vue.prototype._render = function () {
  const vm = this;
  // 执行选项中的render方法,指定this为Vue实例
  const { render } = vm.$options;
  return render.call(vm);
};

render函数中用到了_c,_v,_s这些方法,需要在Vue.prototype上添加这些方法,在render函数内就可以通过实例调用它们:

// 创建虚拟节点
function vNode (tag, props, key, children, text) {
  return {
    tag,
    props,
    key,
    children,
    text
  };
}

// 创建虚拟元素节点
function createVElement (tag, props = {}, ...children) {
  const { key } = props;
  delete props.key;
  return vNode(tag, props, key, children);
}

// 创建虚拟文本节点
function createTextVNode (text) {
  return vNode(undefined, undefined, undefined, undefined, text);
}

// 将实例中data里的值转换为字符串
function stringify (value) {
  if (value == null) {
    return '';
  } else if (typeof value === 'object') {
    return JSON.stringify(value);
  } else {
    return value;
  }
}

export function renderMixin (Vue) {
  Vue.prototype._c = createVElement;
  Vue.prototype._v = createTextVNode;
  Vue.prototype._s = stringify;
  // some code ...
}

_render函数最终会递归的调用这些函数来得到虚拟节点,并将其返回:

const vNode = vm.createVElement('div', { id: 'app' },
  vm.createVElement('span', undefined,
    vm.createTextVNode('hello') + vm.createTextVNode('world') + vm.stringify(vm.name)
  )
)

在生成虚拟节点的过程中,会从组件实例vm中取值,从而触发对应属性的get/set方法。

将虚拟节点处理为真实节点

在通过Vue.prototype._render函数生成虚拟节点后,在Vue.prototype._update方法中会利用虚拟节点,替换当前页面上渲染的元素app

其代码如下:

// src/lifecycle.js
export function lifecycleMixin (Vue) {
  Vue.prototype._update = function (vNode) {
    const vm = this;
    patch(vm.$el, vNode);
  };
}

patch方法中,会通过虚拟节点创建真实节点,并将真实节点插入页面中:

// src/vdom/patch.js
export function patch (oldVNode, vNode) {
  // 将虚拟节点创建为真实节点,并插入到dom中
  const el = createElement(vNode);
  // 获取到老节点的父节点
  const parentNode = oldVNode.parentNode;
  // 将新节点插入到老节点之后
  parentNode.insertBefore(el, oldVNode.nextSibling);
  // 删除老节点
  parentNode.removeChild(oldVNode);
}

createElement中是用虚拟节点生成真实节点的逻辑:

  • 通过document.createElement来创建元素节点
  • 元素节点通过updateProperties方法来设置它的属性
  • 通过document.createTextNode来创建文本节点
function createElement (vNode) {
  if (typeof vNode.tag === 'string') {
    vNode.el = document.createElement(vNode.tag);
    updateProperties(vNode);
    for (let i = 0; i < vNode.children.length; i++) {
      const child = vNode.children[i];
      vNode.el.appendChild(createElement(child));
    }
  } else {
    vNode.el = document.createTextNode(vNode.text);
  }
  return vNode.el;
}

createElement会生成的真实DOM元素el并返回,内部会对子虚拟节点再次调用createElement来继续生成真实元素,然后将生成的真实元素通过appendChild方法插入到父节点中。

执行createElement最后得到的el是将所有子节点都插入到内部的元素,但其实el此时还是脱离真实DOM存在的,最后将它插入到真实DOM中便完成了整个真实节点的渲染。

下面是其执行逻辑示意图:

总结

Vue的组件挂载vm.$mount(el)过程如下:

  • template编译为render函数
  • 使用render函数生成虚拟节点,函数中需要的变量和方法会去vm的自身和原型链中查找
  • 将虚拟节点创建为真实节点,并递归的插入到页面中
  • 使用真实节点替换之前老的节点

到目前为止,我们已经实现了Vue组件初渲染的整个过程,下面用一张图来总结一下:

到此这篇关于Vue手写实现组件初渲染的文章就介绍到这了,更多相关Vue组件初渲染内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

源码地址: 传送门

(0)

相关推荐

  • vue实现动态表单动态渲染组件的方式(1)

    vue 实现动态表单/动态渲染组件的方式(一),供大家参考,具体内容如下 思路 先写好各个可能会出现的表单或者自定义的组件,引入. 此时后端可能会给到一个对象型数组,每个对象有要渲染组件的一个类型标识 利用component is 动态组件,根据不同的组件类型标识,渲染展示不同的组件 在利用组件的数据通信去收集各个子组件的数据 实现demo 三个表单组件,用了element-ui 此处用了自定义组件的v-model来收集子组件的数据 //InputComponent.vue  要渲染的子组件 <

  • vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

    早就实现了功能,但是发现点击的时候,选中的菜单项背景色会变白,周五时候仔细观察了一下,发现并不是调整样式的问题,而是选项没有被选中,于是好好研究了一下组件递归这块,总结记录一下心路历程 一.概念 递归:递归其实说白了,就是自己调用自己,样子就像是套娃一个套一个的,小时候玩过一个游戏汉诺塔就是利用的递归原理: 函数递归:函数利用函数名还调用自己 组件递归:所以组件递归利用的是vue组件中的name属性来实现的 二.需求 实现可折叠动态渲染多级侧边栏导航 三.分析 1.观察到侧边栏导航是一级一级的,

  • Vue中强制组件重新渲染的正确方法

    有时候,依赖 vue 响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据.或者,我们可能只想抛开当前的DOM,重新开始.那么,如何让vue以正确的方式重新呈现组件呢? 强制 Vue 重新渲染组件的最佳方法是在组件上设置:key. 当我们需要重新渲染组件时,只需更 key 的值,Vue 就会重新渲染组件. 这是一个非常简单的解决方案. 当然,你可能会对其他方式会更感兴趣: 简单粗暴的方式:重新加载整个页面 不妥的方式:使用 v-if 较好的方法:使用Vue的内置forceUpda

  • vuex中遇到的坑,vuex数据改变,组件中页面不渲染操作

    vuex数据改变,组件中页面不渲染 相信许多vuex新手都会遇到这样的问题: vuex数据更新后,插件中使用数据的地方没有更新 这样的代码 data() { return { tableData: this.$store.state.AdminInfo }; } 然后在 template 中使用 tableData <el-table :data="tableData" class="tablePst"> <el-table-column labe

  • Vue组件的渲染流程详细讲解

    目录 引言与例子 举一个工作中能用到的例子: 实现 extend 执行流程 1. 注册流程(以Vue.component()祖册为例子): 2. 执行流程 3. 渲染流程 总结 注: 本文目的是通过源码方向来讲component组件的渲染流程 引言与例子 在我们创建Vue实例时,是通过new Vue.this._init(options)方法来进行初始化,然后再执行$mount等,那么在组件渲染时,可不可以让组件使用同一套逻辑去处理呢? 答:当然是可以的,需要使用到Vue.extend方法来实现

  • vue实现动态表单动态渲染组件的方式(2)

    本文实例为大家分享了vue实现动态表单动态渲染组件的方式,供大家参考,具体内容如下 思路 先把所有可能出现的表单/组件写在主页面 每个表单/组件的slot 属性值要与后端返回的表单/组件类型匹配 根据后端返回的数据,动态生成一个slot列表,slot的name属性要与数据的类型匹配,此列表放入一个子组件 在主页面引入子组件,把之前主页面写好的各个表单/组件放入子组件标签中,通过匹配slot插槽去渲染组件,没有匹配到插槽的则不会渲染 案例 //主页面 <template>     <el-

  • Vue手写实现组件初渲染

    目录 前言 生成虚拟节点 将虚拟节点处理为真实节点 总结 前言 在Vue进行文本编译之后,会得到代码字符串生成的render函数.本文会基于render函数介绍以下内容: 执行render函数生成虚拟节点 通过vm._update方法,将虚拟节点渲染为真实DOM 在vm.$mount方法中,文本编译完成后,要进行组件的挂载,代码如下: Vue.prototype.$mount = function (el) { // text compile code .... mountComponent(v

  • 解决vue同一slot在组件中渲染多次的问题

    Q:今天写公共组件的时候碰到一个奇葩的情况,slot内部需要再次调用slot,这就会导致同一slot在组件中渲染多次引发的bug A:把把父组件需要传入的dom结构作为props属性传递给slotRender,保证出来的dom结构不相同(尽管它们都是同一vnode渲染出来的) 定义一个渲染slot的组件 子公共组件调用 父公共组件调用 以上这篇解决vue同一slot在组件中渲染多次的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Vue手写横向轮播图的实例

    目录 Vue手写横向轮播图 Vue常见的轮播图 Vue手写横向轮播图 前提:自己封装的轮播图,暂时没测出bug~ 效果如下图,一行三个,点击上一张/下一张 向前或向后移动一格,窗口缩放会适当变形,不影响切换 <template> <div class="swiper-template"> <div class="my-swiper-page"> <div class="page-left"> <

  • 微信小程序实现简单手写签名组件的方法实例

    目录 背景: 需求: 效果 一.思路 二.实现 1. 页面与样式 2. 初始化 3. 点击时 4. 签名时 三.总结 背景: 在做项目过程中,需要在微信小程序中实现手写签名组件.在网上找了微信小程序手写签名实现,但是都是不太理想.在实际运用中,会因为实时计算较多的贝塞尔曲线而产生卡顿.效果不理想.所以退一步,不需要笔锋以及笔迹模拟效果.只需要简单的手写签名实现. 需求: 可以实现用户在微信小程序上手写签名. 需要组件化. 效果 一.思路 在微信小程序中,我们使用canvas组件实现.将用户的输入

  • 基于vue手写tree插件的那点事儿

    前言 Tree树形控件在前端开发中必不可少,对于数据的展示现在网站大都采取树形展示.因为大数据全部展示出来对于用户来说是不友好的.今天我们自己手写一个Tree插件. iview提供的控件 iview已经很成熟了,如果说我写的控件和iview提供的控件谁更好,那肯定是选择iview , 手写控件只是为了更好的了解vue父子组件之间的通信的. 请读者还是不要拿我的控件和iview或者其他第三方的去对比.下面我们先来看看iview的Tree控件如何使用 <template> <Tree :da

  • vue手写加载动画项目

    在页面没有响应时,展示加载动画是一种对用户友好的表现,不至于白屏,等响应内容渲染到页面时再移除动画 先放loading效果图 原理:伪类+动画,下面是步骤图,贴上助于理解,加载动画本质是设置一个定宽定高的正方形,border-radius: 50%;使其成为圆形,再给该div3px的边框并设为透明,然后在单独设置上边框为白色,用::before,::after伪类absolute定位并进行同样设置,不同之处是依次增大留出的间隙,以及动画执行时间变长和设置延迟,这样就能形成圆圈不同快慢的旋转 完整

  • vue 使用 canvas 实现手写电子签名

    功能 兼容 PC 和 Mobile: 画布自适应屏幕大小变化(窗口缩放.屏幕旋转时画布无需重置,自动校正坐标偏移): 自定义画布尺寸(导出图尺寸),画笔粗细.颜色,画布背景色: 支持裁剪 (针对需求:有的签字需要裁剪掉四周空白). 导出图片格式为 base64 : 示例demo 安装 npm install vue-esign --save 使用 main.js 中引入 import vueEsign from 'vue-esign' Vue.use(vueEsign) 页面中使用 必须设置 r

  • vue写一个组件

    写一个vue组件 我下面写的是以.vue结尾的单文件组件的写法,是基于webpack构建的项目.如果还不知道怎么用webpack构建一个vue的工程的,可以移步到vue-cli. 一个完整的vue组件会包括一下三个部分: template:模板 js: 逻辑 css : 样式 每个组件都有属于自己的模板,js和样式.如果将一个页面比喻成一间房子的话,组件就是房子里的客厅.卧室.厨房.厕所.如果把厨房单独拿出来的话,组件又可以是刀.油烟机...等等.就是说页面是由组件构成的,而组件也可以是组件构成

  • vue实现简单表格组件实例详解

    本来想这一周做一个关于vuex的总结的,但是由于朋友反应说还不知道如何用vue去写一个组件,所以在此写写一篇文章来说明下如何去写vue页面或者组件.vue的核心思想就是组件,什么是组件呢?按照我的理解组件就是装配页面的零件,比如一辆车有大大小小许多零件组成,那么同样的一个页面,也是有许多组件构成的比如说头部组件 按钮组件等等,vue三大核心组件 路由 状态管理,路由控制页面的渲染,页面由组件组成,数据有vuex进行管理和改变.下面我会以一个简单的案例来说 第一步:构建一个简单的vue项目,老规矩

  • 手写Vue内置组件component的实现示例

    目录 前言 内置组件component的使用 component组件的原理分析 虚拟DOM与原生DOM render函数的使用 尝试手写实现component 总结 最近在复习Vue的源码,今天带大家手写实现一下Vue内置组件component,比较简单,最近面试有被问到. 前言 Vue大家都很熟悉,除了原生的组件,其自己也封装了一下内置组件,比如component,transition,keep-alive等等. component算是用的比较多的了,当我们遇到需要根据不同条件显示不同组件的时

随机推荐