SoFunction
Updated on 2025-04-04

Introduction to the code generation method of Vue compiler

Here we will discuss how to generate the code of rendering functions based on JavaScript AST, i.e. code generation. Code generation is essentially the art of string splicing. You need to access nodes in JavaScript AST to generate corresponding JavaScript code for each type of node.

In this section, the generate function will be implemented to complete the code generation task.

function compile(template) {
	//Template AST	const ast = parse(template)
	//Convert template AST to JavaScript AST	transform(ast)
	// Code generation	const code = generate()
	return code
}

Here, code generation also requires context objects.

function generate(node){
	const context = {
		// Store the final generated rendering function code		code: '',
		// When generating code, the code is spliced ​​by calling the push function		push(code){
			 += code
		}
	}
	// This method completes the code generation work	genNode(node,context)
	// Return the rendering function code	return 
}

In addition, in order to make the final generated code more readable, we need to consider the format of the generated code. This requires extending the context object and adding tool functions to it to complete line breaks and indents, as shown in the following code:

function generate(node){
	const context = {
		code: '',
		push(code){
			 += code
		},
		// The current indentation level, the initial value is 0, that is, there is no indentation		currentIndent: 0,
		// This function is used to break lines, that is, to append \n characters after the code string		// In addition, indentation should be retained when newlines, so currentIndent * 2 space characters should be added		newline(){
			 += '\n'+` `.repeat()
		},
		// Used to indent, that is, let currentIndent increase itself and call the newline function		indent(){
			++
			()
		},
		// Cancel the indentation		deIndent(){
			--
			()
		},
	}
	genNode(node, context)
	return 
}

With these basic abilities, you can start writing genNode functions to complete the code generation work. The principle of code generation is actually very simple. You only need to match various types of JavaScript AST nodes and call the corresponding generation function, as shown in the following code:

function genNode(node,context){
	switch(){
		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
	}
}

If you need to add node types in the future, you only need to add the corresponding processing branch to the genNode function.

Next, we gradually improve the code generation work, first implement the code generation of function life statements, that is, the genFunctionDecl function, as shown in the following code:

function genFunctionDecl(node,context){
	// Remove tool functions from context object	const {push, indent, deIndent} = context
	push(`function ${}`)
	push(`(`)
	//Call genNodeList to generate code for the function's parameters	genNodeList(, context)
	push(`)`)
	push(`{`)
	indent()
	// Generate code for the function body, here the genNode function is called recursively	(n=>genNode(n,context))
	deIndent()
	push(`}`)
}

The genFunctionDecl function is used to generate corresponding JavaScript code for nodes of function declaration type. Taking the declaration node as an example, the final code it generates will be:

function render(){
	...Function body
}

The implementation of the genNodeList function is as follows:

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

One thing to note here is that every time a node is processed, a comma needs to be spliced ​​after the generated code.

In fact, the genArrayExpression function takes advantage of this feature to realize the code generation of array expressions, as shown in the following code:

function genArrayExpression(node, context){
	const {push} = context
	// Append square brackets	push(`[`)
	genNodeList(, context)
	// Complete brackets	push(`]`)
}

For the genFunctionDecl function, it is also important to note that since the function body itself is also an array of nodes, it is necessary to traverse it and call the genNode function to generate code recursively.

For nodes of type ReturnStatement and StringLiteral, generating code for them is simple, as follows:

function genReturnStatement(node, context){
	const {push} = context
	push(`return `)
	genNode(, context)
}
function genStringLiteral(node, context){
	const {push} = context
	// For string literals, you only need to append the corresponding string to	genNode(`'${}'`)
}

Finally, only the genCallExpression function is left, which is implemented as follows:

function genCallExpression(node, context){
	const {push} = context
	// Get the called function name and parameter list	const {callee, arguments: args} = node
	// Generate function call code	push(`${}`)
	genNodeList(args,context)
	push(`)`)
}

Together with the implementation of the above generator function, you can get the rendering function code that matches the tone. Use the following code to test it

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

The final code string is as follows:

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

This is the article about the introduction to the code generation method of Vue compiler implementation. For more related Vue code generation content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!