我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue编译器实现代码生成方法介绍

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue编译器实现代码生成方法介绍

这里将讨论如何根据JavaScript AST生成渲染函数的代码,即代码生成。代码生成本质上是字符串拼接的艺术。需要访问JavaScript AST中的节点,为每一种类型的节点生成相符的 JavaScript代码。

本节,将实现 generate函数来完成代码生成的任务。

function compile(template) {
	//模板 AST
	const ast = parse(template)
	//将模板 AST转换为JavaScript AST
	transform(ast)
	// 代码生成
	const code = generate(ast.jsNode)
	return code
}

在这里,代码生成也需要上下文对象。

function generate(node){
	const context = {
		// 存储最终生成的渲染函数代码
		code: '',
		// 在生成代码时,通过调用 push 函数完成代码的拼接
		push(code){
			context.code += code
		}
	}
	// 该方法完成代码生成的工作
	genNode(node,context)
	// 返回渲染函数代码
	return context.code
}

另外,为了让最终生成的代码具有较强的可读性,要考虑生成代码的格式,这就需要扩展context对象,为其增加用来完成换行和缩进的工具函数,如下面代码所示:

function generate(node){
	const context = {
		code: '',
		push(code){
			context.code += code
		},
		// 当前缩进的级别,初始值为 0,即没有缩进
		currentIndent: 0,
		// 该函数用来换行,即在代码字符串的后面追加 \n 字符
		// 另外,换行时应该保留缩进,所以还要追加 currentIndent * 2 个空格字符
		newline(){
			context.code += '\n'+` `.repeat(context.currentIndent)
		},
		// 用来缩进,即让 currentIndent 自增后,调用换行函数
		indent(){
			context.currentIndent++
			context.newline()
		},
		// 取消缩进
		deIndent(){
			context.currentIndent--
			context.newline()
		},
	}
	genNode(node, context)
	return context.code
}

有了这些基础能力之后,就可以开始编写 genNode 函数来完成代码生成的工作了。代码生成的原理其实很简单,只需要匹配各种类型的 JavaScript AST节点,并调用对应的生成函数即可,如下面的代码所示:

function genNode(node,context){
	switch(node.type){
		case 'FunctionDecl':
			genFunctionDecl(node,context)
			break
		case 'ReturnStatement':
			genReturnStatement(node,context)
			break
		case 'CallExpression':
			genCallExpression(node,context)
			break
		case 'StringLiteral':
			genStringLiteral(node,context)
			break
		case 'ArrayExpression':
			genArrayExpression(node,context)
			break
	}
}

如果后续需要增加节点类型,只需要再genNode函数中添加相应的处理分支即可

接下来逐步完善代码生成工作,首先实现函数生命语句的代码生成,即genFunctionDecl函数,如下面的代码所示:

function genFunctionDecl(node,context){
	// 从context 对象中取出工具函数
	const {push, indent, deIndent} = context
	push(`function ${node.id.name}`)
	push(`(`)
	//调用 genNodeList 为函数的参数生成代码
	genNodeList(node.params, context)
	push(`)`)
	push(`{`)
	indent()
	//为函数体生成代码,这里递归地调用了 genNode 函数
	node.body.forEach(n=>genNode(n,context))
	deIndent()
	push(`}`)
}

genFunctionDecl函数用来为函数声明类型的节点生成对应的 JavaScript代码。以声明节点为例,它最终生成的代码将会是:

function render(){
	...函数体
}

genNodeList函数的实现如下:

function genNodeList(nodes, context){
	const {push} = context
	for(let i=0;i<nodes.length;i++){
		const node = nodes[i]
		genNode(node,context)
		if(i<nodes.length - 1){
			push(',')
		}
	}
}

这里要注意的一点是,每处理完一个节点,需要在生成的代码后面拼接逗号。

实际上,genArrayExpression函数就利用了这个特点来实现对数组表达式的代码生成,如下面的代码所示:

function genArrayExpression(node, context){
	const {push} = context
	// 追加方括号
	push(`[`)
	genNodeList(node.elements, context)
	// 补全方括号
	push(`]`)
}

对于 genFunctionDecl 函数,另外需要注意的是,由于函数体本身也是一个节点数组,所以需要遍历它并递归地调用 genNode 函数生成代码。

对于ReturnStatement和StringLiteral类型的节点来说,为它们生成代码很简单,如下所示:

function genReturnStatement(node, context){
	const {push} = context
	push(`return `)
	genNode(node.return, context)
}
function genStringLiteral(node, context){
	const {push} = context
	// 对于字符串字面量,只需要追加与 node.value 对应的字符串即可
	genNode(`'${node.value}'`)
}

最后,只剩下genCallExpression 函数了,实现如下:

function genCallExpression(node, context){
	const {push} = context
	// 取得被调用函数名称和参数列表
	const {callee, arguments: args} = node
	// 生成函数调用代码
	push(`${callee.name}`)
	genNodeList(args,context)
	push(`)`)
}

配合上述生成器函数的实现,就能得到符合语气的渲染函数代码,用下面的代码测试

const ast = parse(`<div><p>Vue</p><p>Template</p></div>`)
transform(ast)
const code = generate(ast.jsNode)

最终得到的代码字符串如下:

function render (){
	return h('div',[h('p','Vue'), h('p','Template')])
}

到此这篇关于Vue编译器实现代码生成方法介绍的文章就介绍到这了,更多相关Vue代码生成内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue编译器实现代码生成方法介绍

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Vue编译器实现代码生成方法介绍

这篇文章主要介绍了Vue编译器实现代码生成方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-01-05

vue codemirror如何实现在线代码编译器

vue codemirror如何实现在线代码编译器,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。前言如果我们想在Web端实现在线代码编译的效果,那么需要使用组件
2023-06-22

React代码分割的实现方法介绍

虽然一直有做react相关的优化,按需加载、dll分离、服务端渲染,但是从来没有从路由代码分割这一块入手过,所以下面这篇文章主要给大家介绍了关于React中代码分割的方式,需要的朋友可以参考下
2022-12-03

Vue全局监测错误并生成错误日志实现方法介绍

在做完一个项目后,之后的维护尤为重要。这时,如果项目配置了错误日志记录,这样能大大减少维护难度。虽然不一定能捕获到全部的错误,但是一般的错误还是可以监测到的。这样就不用测试人员去一遍一遍复现bug了
2022-11-13

单独编译Android 源代码中的模块实现方法

第一次下载好Android源代码工程后,我们通常是在Android源代码工程目录下执行make命令,经过漫长的等待之后,就可以得到Android系统镜像system.img了。以后如果我们修改了Android源代码中的某个模块或
2022-06-06

 Redis串行生成顺序编码的方法怎么实现

这篇“ Redis串行生成顺序编码的方法怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“ Redis串行生成顺序编码的
2023-06-29

Android中实现自动生成布局View的初始化代码方法

在android开发过程中,界面布局是及其重要的,但同时也是复杂。有的时候我们急于实际运行查看布局效果。但是android的编译速度我实在不想吐槽啥,尤其在布局越来越复杂,项目越来越大,资源文件越来越多的情况下。 尤其是是android的v
2022-06-06

编程热搜

目录