vue生命周期 —— 模板编译

2023-11-07

Vue 的 template 是如何编译成真正的 HTML 并做到双向绑定等等特殊功能的呢?

在这张图中,我们可以看到 Vue 的模板编译是在 $mount 的过程中进行的,在 $mount 的时候执行了 compile 这个方法来将 template 里的内容转换成真正的 HTML 代码。complie 之后执行的事情也蛮重要的,这个我们留到最后再说。complie 最终生成 render 函数,等待调用。这个方法分为三步:

  • parse 函数解析 template
  • optimize 函数优化静态内容
  • generate 函数创建 render 函数字符串

parse 解析

在了解 parse 的过程之前,我们需要了解 AST,AST 的全称是 Abstract Syntax Tree,也就是所谓抽象语法树,用来表示代码的数据结构。在 Vue 中我把它理解为嵌套的、携带标签名、属性和父子关系的 JS 对象,以树来表现 DOM 结构。

下面是 Vue 里的 AST 的定义:

我们可以看到 AST 有三种类型,并且通过 children 这个字段层层嵌套形成了树状的结构。而每一个 AST 节点存放的就是我们的 HTML 元素、插值表达式或文本内容。AST 正是 parse 函数生成和返回的。
parse 函数里定义了许多的正则表达式,通过对标签名开头、标签名结尾、属性字段、文本内容等等的递归匹配。把字符串类型的 template 转化成了树状结构的 AST。

// parse 里定义的一些正则
export const onRE = /^@|^v-on:/ //匹配 v-on
export const dirRE = /^v-|^@|^:/ //匹配 v-on 和 v-bind
export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/ //匹配 v-for 属性
export const forIteratorRE = /\((\{[^}]*\}|[^,]*),([^,]*)(?:,([^,]*))?\)/ //匹配 v-for 的多种形式

我们可以把这个过程理解为一个截取的过程,它把 template 字符串里的元素、属性和文本一个个地截取出来,其中的细节十分琐碎,涉及到各种不同情况(比如不同类型的 v-for,各种 vue 指令、空白节点以及父子关系等等)。

假设我们有一个元素<div id="test">texttext</div>,在 parse 完之后会变成如下的结构并返回:

  ele1 = {
    type: 1,
    tag: "div",
    attrsList: [{name: "id", value: "test"}],
    attrsMap: {id: "test"},
    parent: undefined,
    children: [{
        type: 3,
        text: 'texttext'
      }
    ],
    plain: true,
    attrs: [{name: "id", value: "'test'"}]
  }

optimize 优化

在第二步中,会对 parse 生成的 AST 进行静态内容的优化。静态内容指的是和数据没有关系,不需要每次都刷新的内容。标记静态节点的作用是为了在后面做 Vnode 的 diff 时起作用,用来确认一个节点是否应该做 patch 还是直接跳过。optimize 的过程分为两步:

  • 标记所有的静态和非静态结点
  • 标记静态根节点

标记所有的静态和非静态结点

关于这一段我们可以直接看源码:

function markStatic (node: ASTNode) {
  // 标记 static 属性
  node.static = isStatic(node)
  if (node.type === 1) {
    // 注意这个判断逻辑
    if (
      !isPlatformReservedTag(node.tag) &&
      node.tag !== 'slot' &&
      node.attrsMap['inline-template'] == null
    ) {
      return
    }
    for (let i = 0, l = node.children.length; i < l; i++) {
      const child = node.children[i]
      markStatic(child)
      if (!child.static) {
        node.static = false
      }
    }
  }
}

上面的代码中有几个需要注意的地方:

  • isStatic 函数

isStatic 函数顾名思义是判断该节点是否 static 的函数,符合如下内容的节点就会被认为是 static 的节点:

1. 如果是表达式AST节点,直接返回 false
2. 如果是文本AST节点,直接返回 true
3. 如果元素是元素节点,阶段有 v-pre 指令 ||
  1. 没有任何指令、数据绑定、事件绑定等 &&
  2. 没有 v-if 和 v-for &&
  3. 不是 slot 和 component &&
  4. 是 HTML 保留标签 &&
  5. 不是 template 标签的直接子元素并且没有包含在 for 循环中
  则返回 true
  • if 判断条件
!isPlatformReservedTag(node.tag):node.tag 不是 HTML 保留标签时返回true。
node.tag !== 'slot':标签不是slot。
node.attrsMap['inline-template'] == null:node不是一个内联模板容器。

如果满足上面的所有条件,那么这个节点的 static 就会被置为 false 并且不递归子元素,当不满足上面某一个条件时,递归子元素判断子元素是否 static,只有所有元素都是 static 的时候,该元素才是 static。

标记静态根节点

这部分理解起来很简单,只有当一个节点是 static 并且其不能只拥有一个静态文本节点时才能被称为 static root。因为作者认为这种情况去做优化,其消耗会超过获得的收益。

if (node.static && node.children.length && !(
  node.children.length === 1 &&
  node.children[0].type === 3
)) {
  node.staticRoot = true
  return
} else {
  node.staticRoot = false
}

generate 生成 render

生成 render 的 generate 函数的输入也是 AST,它递归了 AST 树,为不同的 AST 节点创建了不同的内部调用方法,等待后面的调用。生成 render 函数的过程如下:

几种内部方法
_c:对应的是 createElement 方法,顾名思义,它的含义是创建一个元素(Vnode)
_v:创建一个文本结点。
_s:把一个值转换为字符串。(eg: {{data}})
_m:渲染静态内容

假设我们有这么一段 template

<template>
  <div id="test">
    {{val}}
    <img src="http://xx.jpg">
  </div>
</template>

最终会被转换成这样子的函数字符串


{render: "with(this){

  return 
    _c('div',
      {attrs:{"id":"test"}},
       [[_v(_s(val))]),
         _v(" "),
        _m(0)
       ]
     )
   }
"}


整个 Vue 渲染过程,前面我们说了 complie 的过程,在做完 parse、optimize 和 generate 之后,我们得到了一个 render 函数字符串。
那么接下来 Vue 做的事情就是 new watcher,这个时候会对绑定的数据执行监听,render 函数就是数据监听的回调所调用的,其结果便是重新生成 vnode。当这个 render 函数字符串在第一次 mount、或者绑定的数据更新的时候,都会被调用,生成 Vnode。如果是数据的更新,那么 Vnode 会与数据改变之前的 Vnode 做 diff,对内容做改动之后,就会更新到我们真正的 DOM 上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue生命周期 —— 模板编译 的相关文章

  • C++ STL 迭代器方法 之 advance与prev 方法 浅析

    摘要 迭代器是STL中重要的一支 prev和distance是其基本方法 distance方法十分简单 就不在此赘述 现主要对prev方法以及其相关方法 advance方法作简要介绍与使用说明 并在文末附上代码示例 Advance 方法 A

随机推荐

  • R语言编程 R语言作业

    一 40 分 请使用 nycflight13 回答以下问题 1 请用 flights 表单找出出发时间没有延误 但是到达时间的所有航班 2 在 1 数据的基础上计算到达每个目的地的航班数量 平均飞行距离和平 均到达延误时间 3 在 2 的基
  • 自然语言处理(NLP)学习笔记——RNN模型

    一 RNN架构解析 1 认识RNN模型 1 1 什么是RNN模型 RNN Recurrent Neural Network 中文称作循环神经网络 它一般以序列数据为输入 通过网络内部的结构设计有效捕捉序列之间的关系特征 一般也是以序列形式进
  • J2EE的体系架构——J2EE

    J2EE是Java2平台企业版 Java 2 Platform Enterprise Edition 它的核心是一组技术规范与指南 提供基于组件的方式来设计 开发 组装和部署企业应用 J2EE使用多层分布式的应用模型 J2EE分层 客户层
  • 电脑输入英文字符的时候字体突然变了样

    问题描述 电脑输入英文字符的时候 突然变成了粗粗的还有间隙 如下所示 与正常对比 te bie pang de zi fu 出现原因 可能是因为无意间按到了中文的全半角切换 快捷键 可以把这个快捷键关掉 也可以不关 解决办法 解决这个问题的
  • Linux系统上安装docker

    文章目录 一 Docker的简介 二 Docker的组成部分 三 Docker的安装命令 安装之前先卸载系统上原有的Docker 安装需要的安装包yum utils 设置镜像仓库地址 安装docker相关的引擎 安装docker 启动doc
  • python打包列表文件到一个包

    python打包列表文件到一个包 def tar file tarfile list ret os system tar pzcvf x tar gz s join tarfile list if ret 0 return True els
  • 【机器学习 - 1】:knn算法

    文章目录 机器学习的概念和基础 knn算法的实现过程 封装knn算法 总结 机器学习的概念和基础 机器学习可以两类任务 分类任务和回归任务 以机器学习本身来进行分类可分为 监督学习 非监督学习 半监督学习 增强学习 监督学习 给机器的训练数
  • 绿幕换背景、绿幕视频实时换背景

    PS 陆陆续续做绿幕抠图相关的工作也有2年之久了 一直研究普通摄像头下的绿幕抠图工作 这样的工作要比摄影棚下的难度要高很多 当然现在也出来很多的工具 抠图算法也越来越成熟 本人较懒 后面会一点点的把相关内容补齐 先上图 上面是效果 边缘做的
  • 卷积核(又叫filter,neuron),设计CNN layers的技巧

    loss entropy求导 为0 那么该怎样求导呢 并行计算 视频 https www bilibili com video BV1Ht411g7Ef p 13 CNN的术语 共享参数 第二种版本解释CNN 前一个64个filter 通过
  • freeswitch六、freeswitch会议功能

    freeswitch默认的会议号 FreeSwitch 默认支持会议功能 有如下特点 1 不需要创建一个会议室的操作 只需要通过 conference 拨码计划就可以实现 2 会议室不真正存在 直到有人呼入为止 3 会议功能很强大 能实现灵
  • 【AOSP】Settings应用界面逻辑

    源码参考 AOSPXRef 现象效果 调试UI显示 Settings应用子界面Activity绝大部分都是SubSetting 通过dumpsys指令查看当前活动 adb shell dumpsys activity activities
  • python自动化_检测系统的空文件夹

    一 空文件夹的判断 1 os listdir 函数 2 权限得注意 二 统计检测消耗时间 1 引入datetime日期库 2 扫描开始start time 扫描结束end time 3 因为权限的原因 所以使用了try import os
  • Matlab中使用Mex时遇到的问题及解决方法

    在Matlab命令行使用mex命令时出现错误 error Building MFC application with MD d CRT dll version requires MFC shared dll version Please d
  • 中国姓氏大全(常见508个,罕见740个)

    1 比较靠谱的资料 资料来源 百度百科 中国姓氏 常见姓氏 508个 赵 钱 孙 李 周 吴 郑 王 冯 陈 褚 卫 蒋 沈 韩 杨 朱 秦 尤 许 何 吕 施 张 孔 曹 严 华 金 魏 陶 姜 戚 谢 邹 喻 柏 水 窦 章 云 苏 潘
  • xml报文编写以及解析

    封装电子保单回执报文 Document document org dom4j DocumentHelper createDocument document setXMLEncoding UTF 8 Element root document
  • ChatGPT“保姆级教程”——手把手教你1分钟快速制作思维导图(Markmap/Xmind+Markdown)

    目录 前言 使用ChatGPT生成markdown格式主题 Markmap Markdown 使用Markmap生成思维导图 Xmind Markdown 使用Xmind生成思维导图 建议 其它资料下载 前言 思维导图是一种强大的工具 它可
  • hdu 1003 最大连续子序列和及起始位置 && hdu 1087 最大上升子序列和

    hdu 1003 题意 求最大连续子序列和及起始位置 对于动态规划问题要找出其子问题 考虑到dp的无后效性 dp i 表示以i为结尾的最大值 当dp i 1 gt 0时 以i 1为值对以i为结尾的值有贡献 否则起始位置变为自己 动态地更新最
  • [从零开始学DeepFaceLab-6]: 使用-命令行八大操作步骤-第3步:从目标视频中提取图片

    目录 总体流程 步骤3 从目标视频中提取图片 3 0 目标视频文件和大小的选择 3 1 命令 3 cut video drop video on me bat 可选
  • 三大主流软件负载均衡器对比(LVS、Nginx、HAproxy)

    资料来自网络 做了部分的补充说明 LVS 1 抗负载能力强 性能高 能达到F5的60 对内存和CPU资源消耗比较低 2 工作在网络4层 通过VRRP协议 仅作代理之用 具体的流量是由linux内核来处理 因此没有流量的产生 3 稳定 可靠性
  • vue生命周期 —— 模板编译

    Vue 的 template 是如何编译成真正的 HTML 并做到双向绑定等等特殊功能的呢 在这张图中 我们可以看到 Vue 的模板编译是在 mount 的过程中进行的 在 mount 的时候执行了 compile 这个方法来将 templ