VUE模板编译的实现原理

前言

在Vue.js 2.0中,模板编译是通过将模板转换为渲染函数来实现的。渲染函数是一个函数,它返回虚拟DOM节点,用于渲染实际的DOM。Vue.js的模板编译过程可以分为以下几个步骤:

  • 将模板解析为抽象语法树(AST);
  • 对AST进行静态分析,找出其中的静态节点和动态节点;
  • 生成渲染函数,包括生成静态节点的渲染函数和动态节点的渲染函数。

接下来,我们将重点介绍以上三个步骤。

将模板解析为抽象语法树(AST)

将模板解析为抽象语法树是模板编译的第一步。抽象语法树是一种树形结构,它将模板转换为语法树,便于后续的静态分析和代码生成。Vue.js使用了HTML解析器和指令解析器来解析模板,并生成AST。

HTML解析器的主要任务是将模板解析为标签节点和文本节点,同时记录标签节点之间的嵌套关系。指令解析器的主要任务是解析指令,例如v-bind、v-if、v-for等指令,并将其转换为AST节点。

以下是Vue.js中HTML解析器的相关代码:

// 解析模板,生成AST节点
function parse(template) {const stack = [] // 用于记录标签节点的栈let currentParent // 当前标签节点的父节点let root // AST树的根节点// 调用HTML解析器解析模板parseHTML(template, {// 处理标签节点的开始标记start(tag, attrs, unary) {// 创建标签节点const element = {type: 1, // 节点类型为标签节点tag, // 标签名attrsList: attrs, // 属性列表attrsMap: makeAttrsMap(attrs), // 属性列表转换成属性mapparent: currentParent, // 父节点children: [] // 子节点}// 如果AST树还没有根节点,则将当前标签节点设置为根节点if (!root) {root = element}// 如果存在父节点,则将当前标签节点加入父节点的子节点列表中if (currentParent) {currentParent.children.push(element)}// 如果不是自闭合标签,则将当前标签节点压入栈中if (!unary) {stack.push(element)currentParent = element // 当前标签节点设置为父节点}},// 处理标签节点的结束标记end() {// 弹出栈顶的标签节点,当前标签节点设置为其父节点const element = stack.pop()currentParent = stack[stack.length - 1]},// 处理文本节点chars(text) {// 创建文本节点,并将其加入当前标签节点的子节点列表中const element = {type: 3, // 节点类型为文本节点text,parent: currentParent}if (currentParent) {currentParent.children.push(element)}}})// 返回AST树的根节点return root
}

对AST进行静态分析,找出其中的静态节点和动态节点

 

// 静态节点的类型
const isStaticKey = genStaticKeysCached('staticClass,staticStyle')// 判断一个节点是否为静态节点
function isStatic(node) {if (node.type === 2) { // 表达式节点肯定不是静态节点return false}if (node.type === 3) { // 文本节点只有在它的值是纯文本时才是静态节点return true}return !!(node.pre || ( // 有v-pre指令的节点也是静态节点!node.hasBindings && // 没有绑定数据的节点也是静态节点!isBuiltInTag(node.tag) && // 不是内置标签的节点也是静态节点isStaticKey(node) // 属性只包含静态键的节点也是静态节点))
}// 标记静态节点
function markStatic(node) {node.static = isStatic(node)if (node.type === 1) {// 处理子节点for (let i = 0, l = node.children.length; i < l; i++) {const child = node.children[i]markStatic(child)if (!child.static) {node.static = false}}// 处理属性节点if (node.ifConditions) {for (let i = 1, l = node.ifConditions.length; i < l; i++) {const block = node.ifConditions[i].blockmarkStatic(block)if (!block.static) {node.static = false}}}}
}// 找出AST中的静态节点和动态节点
function optimize(root) {markStatic(root) // 标记静态节点// 优化静态节点function markStaticRoots(node) {if (node.type === 1) {if (node.static && node.children.length && !(node.children.length === 1 && node.children[0].type === 3)) {node.staticRoot = truereturn} else {node.staticRoot = false}}}// 遍历整个ASTfunction dfs(node) {if (node.children) {for (let i = 0, l = node.children.length; i < l; i++) {const child = node.children[i]markStaticRoots(child)dfs(child)}}}dfs(root)return root
}

 在静态分析的过程中,我们需要标记出哪些节点是静态节点,哪些节点是动态节点。静态节点的特点是在渲染过程中不会发生变化,而动态节点则可能发生变化。因此,对于静态节点我们可以采用优化的手段,例如提取静态节点的生成代码,减少渲染过程中的重复计算。

将AST转换为渲染函数

在对AST进行静态分析后,接下来的任务是将AST转换为渲染函数。渲染函数就是一个函数,接收一个上下文对象作为参数,返回一个VNode节点。因此,我们需要将AST转换为一个函数,然后再将这个函数返回的VNode节点渲染出来。

将AST转换为渲染函数的过程是一个比较复杂的过程,涉及到许多细节。在Vue.js的源码中,这个过程是由createCompiler函数来完成的。createCompiler函数接收一个选项对象,包含了编译器的所有配置项,返回一个对象,包含了编译器的所有方法。

在createCompiler函数中,我们首先需要创建一个parse函数,用于将模板字符串解析为AST。在Vue.js中,我们使用了另外一个库——parse5,来解析HTML字符串。解析完成后,我们得到了一个AST,接下来就是对AST进行处理。

在对AST进行处理时,我们需要考虑以下几个问题:

  • 如何处理指令和事件绑定
  • 如何处理插槽
  • 如何处理动态属性和静态属性
  • 如何处理插值表达式
  • 如何处理文本节点和HTML节点

这些问题的处理方式比较复杂,我们在这里不做详细的介绍。在Vue.js的源码中,这些问题的处理都是由不同的函数来完成的,最终将所有的函数组合起来,形成一个完整的编译器。

以下是createCompiler函数的实现:

export function createCompiler(baseOptions: CompilerOptions): Compiler {// 通过createCompiler函数,生成一个编译器Compiler对象function compile(template: string,options?: CompilerOptions): CompiledResult {// 创建一个空的finalOptions对象const finalOptions = Object.create(baseOptions)// 创建一个空数组errors,用于存储编译过程中的错误信息const errors = []// 创建一个空数组tips,用于存储编译过程中的提示信息const tips = []// 定义finalOptions的warn方法,用于处理编译过程中的警告信息finalOptions.warn = (msg, tip) => {(tip ? tips : errors).push(msg)}// 将传入的options对象合并到finalOptions中if (options) {// 合并自定义模块if (options.modules) {finalOptions.modules =(baseOptions.modules || []).concat(options.modules)}// 合并自定义指令if (options.directives) {finalOptions.directives = extend(Object.create(baseOptions.directives || null),options.directives)}// 复制其他选项for (const key in options) {if (key !== 'modules' && key !== 'directives') {finalOptions[key] = options[key]}}}// 调用baseCompile函数进行编译,返回编译结果compiledconst compiled = baseCompile(template, finalOptions)// 将编译过程中的错误信息和提示信息存储到compiled中compiled.errors = errorscompiled.tips = tipsreturn compiled}// 返回一个对象,包含compile和compileToFunctions两个方法return {compile,compileToFunctions: createCompileToFunctionFn(compile)}
}

以上是createCompiler函数的注释说明,我们在注释中解释了createCompiler函数的作用和实现细节,让读者更好地理解该函数的作用和用法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149945.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux CentOS7 vim宏操作

vim的macro就是用来解决重复的问题。在vim寄存器的文章里面已经对macro有所涉及&#xff0c;macro的操作都是以文本的方式存放在寄存器中。 宏是一组命令的集合&#xff0c;应用极其广泛&#xff0c;包括MS Office中的word编辑器&#xff0c;excel编辑器和各种文本编辑器&…

输入电压转化为电流性 5~20mA方案

输入电压转化为电流性 5~20mA方案 方案一方案二方案三 方案一 XTR111是一款精密的电压-电流转换器是最广泛应用之一。原因有二&#xff1a;一是线性度非常好、二是价格便宜。总结成一点&#xff0c;就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式&#xff1a;…

Linux-centos系统安装MySql5.7

1.配置yum仓库 1.1配置yum仓库 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 1.2 安装Mysql yum库 rpm -Uvh http://repo.mysql.com//mysql57-community-release-el7-7.noarch.rpm 2.使用yum安装Msql 说明&#xff1a;下载大约5分钟左右 yum -y install mysq…

2023计算机保研——双非上岸酒吧舞

我大概是从22年10月份开始写博客的&#xff0c;当时因为本校专业的培养方案的原因&#xff0c;课程很多&#xff0c;有些知识纸质记录很不方便&#xff0c;于是选择了打破了自己的成见使用博客来记录学习生活。对于我个人而言&#xff0c;保研生活在前一大半过程中都比较艰难&a…

网络安全行业真的内卷了吗?网络安全就业就业必看

前言 有一个特别流行的词语叫做“内卷”&#xff1a; 城市内卷太严重了&#xff0c;年轻人不好找工作&#xff1b;教育内卷&#xff1b;考研内卷&#xff1b;当然还有计算机行业内卷…… 这里的内卷当然不是这个词原本的意思&#xff0c;而是“过剩”“饱和”的替代词。 按照…

c语言进阶部分详解(详细解析字符串常用函数,并进行模拟实现)

前段时间也是把指针较为详细系统的讲解完毕&#xff0c;接下来介绍一个全新的知识点&#xff0c;就是字符函数和字符串函数 前几期文章可进我主页观看&#xff1a;总之就是非常唔姆_Matlab,经验分享,c语言题目分享-CSDN博客 想要源代码可以去我的github看看&#xff1a;Neros…

5. Mysql卸载

Mysql卸载 已经成功安装mysql&#xff0c;没有必要卸载&#xff0c;卸载之后不一定再次会安装成功。 双击安装包 检查如下三个目录是否有mysql&#xff0c;有的话&#xff0c;删除掉即可&#xff08;前提&#xff0c;电脑只有Mysql8,否则mysql其他版本也会被删除&#xff09;…

【数据结构】排序算法(二)—>冒泡排序、快速排序、归并排序、计数排序

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.冒泡排序 2.快速排序 2.1Hoare版 2.2占…

vue3 中使用echarts图表——柱状图

柱状图是比较常用的图形结构&#xff0c;所以我先收集一些精美的柱状图 一、柱状图&#xff1a;设置圆角和颜色 <template><div class"box" ref"chartDom"></div> </template> <script setup> import { ref, onMounted } fr…

无法启动此程序,因为计算机中“找不到msvcp140.dll”的解决方法

msvcp140.dll是Microsoft Visual C 2015 Redistributable的一个动态链接库文件&#xff0c;它是许多基于Visual Studio开发的应用程序和游戏的必要组件。当计算机上缺失msvcp140.dll文件时&#xff0c;可能会导致以下问题&#xff1a; 1. 程序无法启动&#xff0c;提示“找不到…

位图/布隆过滤器

一、位图 1.1位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&#xff0c;数据无重复的场景。通常是用来判断某个数据存不存在的。 1.2位图的实现 template<size_t N>class bitset{public:bitset(){//需要N个比特位&#xff0c;…

生产设备巡检管理系统

凡尔码搭建生产设备巡检系统是通过确保巡检工作的质量以及提高巡检工作的效率来提高设备维护水平的一种系统&#xff0c;它对巡检管理考核工作从巡检人员、巡检任务、隐患管理、图像视频、盯防考核进行严格、科学的统计、分析&#xff0c;从而有效的保障巡检工作的顺利展开&…

Opengl之立方体贴图

简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢?立方体贴图有一个非常有用的特性,它可以通过一…

约束优化算法(optimtool.constrain)

import optimtool as oo from optimtool.base import np, sp, pltpip install optimtool>2.4.2约束优化算法&#xff08;optimtool.constrain&#xff09; import optimtool.constrain as oc oc.[方法名].[函数名]([目标函数], [参数表], [等式约束表], [不等式约数表], [初…

VulnHub Earth

一、信息收集 1.主机和端口扫描 nmap -sS 192.168.103.1/24 发现443端口有DNS解析&#xff0c;在hosts文件中添加DNS解析&#xff1a; 2.收集earth.local信息 发现有Previous Messages 37090b59030f11060b0a1b4e0000000000004312170a1b0b0e4107174f1a0b044e0a000202134e0a161…

Electron笔记

基础环境搭建 官网:https://www.electronjs.org/zh/ 这一套笔记根据这套视频而写的 创建项目 方式一: 官网点击GitHub往下拉找到快速入门就能看到下面这几个命令了 git clone https://github.com/electron/electron-quick-start //克隆项目 cd electron-quick-start //…

前端position: absolute是相对于谁定位的?

1. 当祖父元素是relative定位, 父元素是absolute定位, 子元素也是absolute定位 <script setup></script><template><div class"relative"><p class"absolute1">absolute1<p class"absolute2">absolute2<…

计算机竞赛 题目:基于python的验证码识别 - 机器视觉 验证码识别

文章目录 0 前言1 项目简介2 验证码识别步骤2.1 灰度处理&二值化2.2 去除边框2.3 图像降噪2.4 字符切割2.5 识别 3 基于tensorflow的验证码识别3.1 数据集3.2 基于tf的神经网络训练代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于pyt…

使用ebpf 监控linux内核中的nat转换

1.简介 Linux NAT&#xff08;Network Address Translation&#xff09;转换是一种网络技术&#xff0c;用于将一个或多个私有网络内的IP地址转换为一个公共的IP地址&#xff0c;以便与互联网通信。 在k8s业务场景中&#xff0c;业务组件之间的关系十分复杂. 由于 Kubernete…

gin 框架的 JSON Render

gin 框架的 JSON Render gin 框架默认提供了很多的渲染器&#xff0c;开箱即用&#xff0c;非常方便&#xff0c;特别是开发 Restful 接口。不过它提供了好多种不同的 JSON Render&#xff0c;那么它们的区别是什么呢&#xff1f; // JSON contains the given interface obje…