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函数的作用和实现细节,让读者更好地理解该函数的作用和用法。
相关文章:
VUE模板编译的实现原理
前言 在Vue.js 2.0中,模板编译是通过将模板转换为渲染函数来实现的。渲染函数是一个函数,它返回虚拟DOM节点,用于渲染实际的DOM。Vue.js的模板编译过程可以分为以下几个步骤: 将模板解析为抽象语法树(AST)…...
基础算法之——【动态规划之路径问题】1
今天更新动态规划路径问题1,后续会继续更新其他有关动态规划的问题!动态规划的路径问题,顾名思义,就是和路径相关的问题。当然,我们是从最简单的找路径开始! 动态规划的使用方法: 1.确定状态并…...
三十三、【进阶】索引的分类
1、索引的分类 (1)总分类 主键索引、唯一索引、常规索引、全文索引 (2)InnoDB存储引擎中的索引分类 2、 索引的选取规则(InnoDB存储引擎) 如果存在主键,主键索引就是聚集索引; 如果不存在主键ÿ…...
VBox启动失败、Genymotion启动失败、Vagrant迁移
VBox启动失败、Genymotion启动失败、Vagrant迁移 2023.10.9 最新版本vbox7.0.10、Genymotion3.5.0 Vbox启动失败 1、查看日志 Error -610 in supR3HardenedMainInitRuntime! (enmWhat4) Failed to locate ‘vcruntime140.dll’ 日志信息查看方法->找到虚拟机所在位置->…...
一篇短小精悍的文章让你彻底明白KMP算法中next数组的原理
以后保持每日一更,由于兴趣较多,更新内容不限于数据结构,计算机组成原理,数论,拓扑学......,所谓:深度围绕职业发展,广度围绕兴趣爱好。往下看今日内容 一.什么是KMP算法 KMP&#x…...
CSS盒子定位的扩张
定位的扩展 绝对定位(固定定位)会完全压住盒子 浮动元素不会压住下面标准流的文字,而绝对定位或固定位会压住下面标准流的所有内容 如果一个盒子既有向左又有向右,则执行左,同理执行上 显示隐藏 display: none&…...
SpringBoot整合POI实现Excel文件读写操作
1.环境准备 1、导入sql脚本: create database if not exists springboot default charset utf8mb4;use springboot;create table if not exists user (id bigint(20) primary key auto_increment comment 主键id,username varchar(255) not null comment 用…...
从零开始的力扣刷题记录-第八十七天
力扣每日四题 129. 求根节点到叶节点数字之和-中等130. 被围绕的区域-中等437. 路径总和 III-中等376. 摆动序列-中等总结 129. 求根节点到叶节点数字之和-中等 题目描述: 给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 …...
【1】c++设计模式——>UML类图的画法
UML介绍 UML:unified modeling language 统一建模语言 面向对象设计主要就是使用UML类图,类图用于描述系统中所包含的类以及他们之间的相互关系,帮助人们简化对系统的理解,他是系统分析和设计阶段的重要产物,也是系统编码和测试的…...
SAP UI5 指定 / 变更版本
SAP UI5 指定 / 变更版本 Currently, SAP Fiori tools support SAP Fiori elements and SAPUI5 freestyle projects with minimum SAPUI5 versions 1.65 or higher. In case there’s a need to test an existing projects with a lower SAPUI5 version, the following worka…...
SpringMVC中异常处理详解
单个控制器异常处理 // 添加ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})public String exceptionHandle1(Exception ex, Model mo…...
PPT课件培训视频生成系统实现全自动化
前言 困扰全动自化的重要环节,AI语音合成功能,终于可以实现自动化流程,在此要感谢团队不懈的努力和韧性的精神! 实现原理 请参照我的文章《Craneoffice云PPT课件培训视频生成系统》 基本流程 演示视频 PPT全自动 总结 过去实…...
基于腾讯云的OTA远程升级
一、OTA OTA即over the air,是一种远程固件升级技术,它允许在设备已经部署在现场运行时通过网络远程更新其固件或软件。OTA技术有许多优点,比如我们手机系统有个地方做了优化,使用OTA技术我们就不用召回每部手机,直接通过云端就可…...
如何在VS2022中进行调试bug,调试的快捷键,debug与release之间有什么区别
什么是bug 在学习编程的过程中,应该都听说过bug吧,那么bug这个词究竟是怎么来的呢? 其实Bug的本意是“虫子”或者“昆虫”,在1947年9月9日,格蕾丝赫柏,一位为美国海军工作的电脑专家,也是最早…...
初识jmeter及简单使用
目录 1、打开页面: 2、添加线程组: 3、线程组中设置参数: 4、添加请求 5、添加一个http请求后,设置请求内容 6、添加察看结果树 7、执行,查看结果 一般步骤是:在测试计划下面新建一个线程组…...
Spring 在多线程环境下如何确保事务一致性
问题在现 如何解决异步执行 多线程环境下如何确保事务一致性 事务王国回顾 事务实现方式回顾 编程式事务 利用编程式事务解决问题 问题分析完了,那么如何解决问题呢? 小结 问题在现 我先把问题抛出来,大家就明白本文目的在于解决什…...
[Machine Learning] Learning with Noisy Data
文章目录 Probabilistic Perspective of NoiseBias and VarianceRobustness among Surrogate Loss FunctionsNMF Probabilistic Perspective of Noise 假设数据来源于一个确定的函数,叠加了高斯噪声。我们有: y h ( x ) ϵ y h(x) \epsilon yh(x)ϵ…...
C++中有哪些常用的标准库?
C中有许多常用的标准库,这些库提供了丰富的功能和工具,方便开发人员进行各种任务。以下是一些常见的C标准库: iostream:用于输入和输出操作,包括cin、cout和cerr等类和函数。algorithm:提供了许多常用的算…...
软考-信息安全工程师概述
本文为作者学习文章,按作者习惯写成,如有错误或需要追加内容请留言(不喜勿喷) 本文为追加文章,后期慢慢追加 2023年10月 信息考试大纲 通过本考试的合格人员能够掌握网络信息安全的基础知识和技术原理,…...
在Taotoken模型广场根据任务与预算挑选合适模型的实践心得
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在Taotoken模型广场根据任务与预算挑选合适模型的实践心得 作为一名日常需要与各类大模型打交道的开发者,模型选型是项…...
第一章 微信小程序概述与开发准备
第一章 微信小程序概述与开发准备 📚 系列教程:微信小程序投票系统完整开发 🔗 上一章:无 🔗 下一章:第二章 - 小程序目录结构与核心文件详解 1.1 什么是微信小程序 微信小程序(Mini Program&a…...
CursorLearn2API:基于AI辅助编程的本地代码自动化部署为云端API实践
1. 项目概述:从本地代码到云端API的自动化桥梁最近在折腾一个挺有意思的项目,叫gmh5225/cursorlearn2api。乍一看这个标题,可能有点摸不着头脑,但如果你是一个经常在本地用 Cursor 这类 AI 辅助编程工具写代码,同时又想…...
TVA动态阈值在昇腾310的适配要点
重磅预告:本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容,该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著,特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...
应对2026知网维普算法更新:论文降AI全攻略,实测3款主流工具与手动微调方法
自从央视公开探讨初稿写作的AI味儿现象:据相关数据显示,近六成师生习惯使用生成式辅助,其中近三成学生将其用于核心初稿的撰写,各高校针对AIGC的审查便日益严格。 正是因为这种大背景,四月一到,定稿通知刚…...
Linux应用层直接操作硬件寄存器:原理、实现与安全实践
1. 项目概述:为什么要在应用层操作寄存器? 在嵌入式Linux开发或者驱动调试的日常工作中,我们常常会遇到一个看似“越界”的需求:在用户空间的应用层程序里,直接去读写某个硬件寄存器的值。这听起来有点“离经叛道”&am…...
终极指南:5步解锁完整Koikatu游戏体验的HF Patch安装方案
终极指南:5步解锁完整Koikatu游戏体验的HF Patch安装方案 【免费下载链接】KK-HF_Patch Automatically translate, uncensor and update Koikatu! and Koikatsu Party! 项目地址: https://gitcode.com/gh_mirrors/kk/KK-HF_Patch 你是否曾经为《恋活…...
终极解决方案:3分钟轻松解决腾讯游戏ACE-Guard卡顿问题
终极解决方案:3分钟轻松解决腾讯游戏ACE-Guard卡顿问题 【免费下载链接】sguard_limit 限制ACE-Guard Client EXE占用系统资源,支持各种腾讯游戏 项目地址: https://gitcode.com/gh_mirrors/sg/sguard_limit 还在为腾讯游戏中的ACE-Guard进程占用…...
VMOS+小黄鸟无root抓包实战:从环境搭建到证书导入的完整避坑指南
1. 为什么需要VMOS小黄鸟组合抓包 很多安卓开发者或者安全爱好者都遇到过这样的困扰:想要分析某个APP的网络请求,却发现抓包工具显示"无网络连接"。这种情况在安卓7.0及以上版本尤为常见,主要是因为系统加强了SSL证书验证机制。传统…...
Vector CAN卡配置避坑指南:xlSetApplConfig函数详解与硬件通道分配实战
Vector CAN卡配置避坑指南:xlSetApplConfig函数详解与硬件通道分配实战 当你在深夜调试Vector CAN设备时,突然看到"Channel already assigned"的红色错误提示,是否感到一阵窒息?这种场景对于使用Vector硬件进行二次开发…...
