一,前言

上篇,主要介绍了数组数据变化的观测情况,涉及以下几个点:

  • 实现了数组数据变化被劫持后,已重写原型方法的具体逻辑;
  • 数组各种数据变化时的观测情况分析;

到目前为止,已经完成了数据初始化操作,分析并实现了各种情况下的数据观测;接下来,需要将数据渲染到页面上;

本篇,主要介绍 Vue 的数据渲染流程;


二,如何将数据渲染到页面上

1,Vue 初始化流程回顾

目前,在 Vue 初始化流程中,会做两件事:

  • 状态初始化:实现数据响应式;(已实现)
  • 将数据挂载到页面上;(未实现)
// src/state.js
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    vm.$options = options;
    
    // 1,状态初始化:实现数据劫持
    initState(vm);
    
    // 2,将数据挂载到页面上
    if (vm.$options.el) {
      // todo...
    }
  }
}

Vue数据初始化完成后,就进入了Vue的数据渲染流程,el元素即为dom的挂载点:

<body>
  <div id=app>{{message}}</div>
  <script>
    let vm = new Vue({
      el: '#app',
      data() {
        return { message: 'Brave' } 
      }
    });
  </script>
</body>

2,渲染问题分析

在 Vue 中,由于响应式数据特性,当数据变化时,会触发视图更新;

在编写 Vue 页面时,会大量使用到{{xxx}}插值表达式、Vue 指令,比如:

<ul>
    <li v-for="(item, index) in arr">{{item}} -- {{index}}</li>
</ul>

当数据发生变化时,Vue 并不会对整个视图全部重新渲染一遍,毕竟那样做性能太低了,也太笨了;

Vue 会对新老节点进行比对,根据比对结果,尽可能复用已经存在的节点,只更新那些变化的节点;

那么,这里就会有以下几个疑问:

  • Vue 如何解析到模板中的插值表达式和指令?
  • Vue 如何进行新老节点的比对?
  • Vue 如何复用老节点,只更新变化的节点?

猜测一下:如果 Vue 使用更新后的数据和字符串模板,重新渲染出新 dom,再用新 dom 和老 dom 进行对比?

  • 首先,使用更新后的数据和字符串模板,渲染出新 dom?
  • 每次更新,都从字符串中解析出指令并完成值的更新?这些是非常困难且消耗性能的;
  • 其次,新 dom 与老 dom 如何做比对?
  • 用字符串做全量比对吗?这似乎不是一个好的方案,性能上也是不能接受的;
  • 再有,尽可能复用老节点,只能更新变化的节点?
  • 只更新需要更新的节点,目前好像也做不到…;(diff是没办法实现的)

为了实现以上几点特性,Vue 就“创造”了一套template模板(类似于 React 的 jsx 语法)

3,templatejsx对比

Vue同时支持templatejsx写法:

  • 在日常开发中,大多都会采用template模板进行开发,template即简单又方便;
  • jsx相比template而言,更加灵活但语法稍显复杂;

在 vue3 中,由于template内部做了大量优化,性能其实会比jsx更高一些,所以在 Vue3 中使用jsx反而会丢掉一些性能优化;


三,Vue 的渲染流程

1,渲染流程分析

Vue会对template模板进行解析,将template模板语法转变为javascript语法;

在转化过程中,使用ast语法树对html语法结构进行描述,通过ast的树形结构将代码重组成为js语法;

ast语法树:用于描述 css、js、html 等语法;

虚拟dom:用于描述 dom 节点;

这样,后续对html模板的操作,都可以直接通过js来完成,不必再去解析字符串了;

那么,这里就涉及到了 “Vue 模板编译原理”

2,Vue 模板编译原理

  1. template模板编译为render函数;
  2. 通过rander函数处理后,返回虚拟dom(老);
    3,更新时,再次通过rander函数生成虚拟dom(新),使用diff算法做新旧比对,根据比对结果更新真实 dom

Vue 模板编译流程:template模板 -> render函数 -> 虚拟dom -> diff比对;


四,render函数简介

vue2 模板解析工具:https://template-explorer.vuejs.org/

vue3aXios如何拿后端数据并渲染至前端_javascript

左侧div标签,最终将被编译成为右侧render函数:

  • _c函数:相当于createElement,创建div元素,内部包含属性id值为app
  • _s函数:相当于JSON.stringify,如果模板的参数msg为对象类型,_s函数会将其转换为string
  • _v函数:创建文本,将_s(msg)创建为一个文本;

五,Vue 数据渲染的核心流程

Vue 数据渲染,分为初渲染更新渲染两部分;

1,初次渲染时

  1. template模板被编译为ast语法树;
  2. 通过ast语法树生成render函数;
  3. 通过render函数返回vnode虚拟节点;
  4. 使用vnode虚拟节点生成真实dom并进行渲染;

2,视图更新时

  1. 调用render函数生成新的vnode虚拟节点;
  2. 通过diff算法对新老vnode虚拟节点进行比对;
  3. 根据新老虚拟节点的比对结果,最终更新真实dom

综上分析,实现 Vue 数据渲染流程,需要实现以下功能点:

  • 通过template模板,生成ast语法树;
  • 根据ast语法树,生成render函数;
  • 根据render函数,生成vnode虚拟节点;
  • 根据vnode虚拟节点,创建真实节点;
  • 使用真实节点替换原始节点;

六,结尾

本篇,主要介绍了 vue 数据渲染的核心流程,初渲染与更新渲染;

  • 通过template模板,生成ast语法树;
  • 根据ast语法树,生成render函数;
  • 根据render函数,生成vnode虚拟节点;
  • 根据vnode虚拟节点,创建真实节点;
  • 使用真实节点替换原始节点;

下一篇,通过template模板,生成ast语法树;


维护日志

  • 20230113: 重新梳理文章内容,调整目录结构,补充大量文字说明并优化部分内容语义,添加描述内容中的代码显示高亮,添加数据渲染核心流程的任务拆分;