Standing on the shoulders of giants to see VUE, vUE design and implementation from Huo Chunyang. The author explains the underlying vuE3 implementation step by step in the form of questions. The idea is very clever, here will be the main logic in the book series, which is also my own record after reading, I hope that through this form can communicate with everyone to learn.

The opening

The vue. js template compiler is used to compile templates into rendering functions, which are divided into three main steps:

  1. Analyze the template and parse it into template AST
  2. Convert the template AST to a JavaScript AST that describes the rendering function
  3. Generate the rendering function code according to the JavaScript AST

The first step of the Parser process is to construct a lexical parser from a finite-state automaton, which is the Whiler loop traversal template. Such as

hello

The second step, transform, transforms the template AST into a JavaScript AST. When accessing the nodes of the AST, the AST is traversed in depth optimization mode. Access to nodes is divided into entry and exit stages, and only the current node is processed in the exit node stage. To achieve the AST conversion. There are two other points to note here, one is the plug-in mechanism and the other is context. To understand the access and replacement operations of a decoupled node, the node can be wrapped into separate transformation functions that can be registered as arrays that are iterated over and executed using callbacks that take the array parameters. Context objects maintain information about the current state of the program, the parent node currently accessed, and the location index of the node currently accessed.

Third, turn the JavaScript AST into a function of render

15.1. Compiler for template DSL

The process by which a compiler translates source code into object code is called compilation. The complete compilation process includes:

Vue template compiler

  1. By encapsulating the parse function to complete the lexical analysis and syntax analysis of the template, the template AST is obtained.
  2. Once you have a template AST, you can perform semantic analysis on it and transform it, such as checking whether v-IF instruction exists in V-else instruction and analyzing whether it is static or constant, etc
  3. Transform from template AST to JavaScript AST is completed by encapsulating transform function.
  4. Generate render function according to JavaScriptAST through generate function;
function compiler(template) {
  const templateAST = parser(template) / / template AST
  const jsAST = transform(templateAST) // javascript AST
  const code = generate(jsAST)  // Code generation
}
Copy the code

15.2 Implementation principle and state machine of Parser

The input parameter to the parser is the string template. The parser reads the characters in the string template one by one and slices the whole string into tokens according to certain rules.

How the parser cuts the template is based on finite state automata: in a finite number of states, the parser automatically migrates between different states as characters are entered.

The state machine is divided into six states: initial state, label start state, label name state, text state, end label state, and end label name state.

According to these six state machines, the template is traversed, the switch determines which state is hit, and then the output format is processed

const tokens = tokenzie(`<p>Vue</p>`)
/ / /
// { type: 'tag', name: 'p' },
// { type: 'text', content: 'Vue' },
// { type: 'textEnd', name: 'p' }
// ]
Copy the code

Finally, with finite automatic state machines, we can parse templates into tokens that can then be used to build an AST.

15.3. Construct the AST

HTML is a markup language with a very fixed format and naturally nested tag elements that form parent-child relationships. So the AST results in a tree deconstruction that describes HTML by constructing json data formats with specific attribute values.

Iterate through the Token list to maintain a stack elementStack, and when a start tag is pushed onto the stack, an end tag is encountered to pop the current stack element. Whiler cycles through tokens until all tokens have been scanned and an AST tree is generated.

Code-for-vue-3-book /code-15.1.html at master · HcySunYang/code-for-vue-3-book

15.4. AST transformation and plug-in system

15.4.1. Node Access

To transform an AST, you need to access every node of the AST so that you can modify, replace, or delete a specific point.

function traverseNode(ast) {
  const currentNode = ast
  const children = currentNode.children
  if (children) {
    for (let i = 0; i < children.length; i++) { traverseNode(children[i]) } } }Copy the code

The replacement logic is decoupled through callback functions to prevent traverseNode logic from being too bloated. Pass in the context and inject the nodeTransforms array method.

function traverseNode(ast, context) {
  const currentNode = ast
  const transforms = context.nodeTransforms
  for (let i = 0; i < transforms.length; i++) {
    transforms[i](currentNode, context)
  }
  const children = currentNode.children
  if (children) {
    for (let i = 0; i < children.length; i++) { traverseNode(children[i]) } } }Copy the code

15.4.2. Convert context and node operations

Context is the “global variable” of a program in a certain scope. Context can be regarded as the context data during the AST conversion function. All AST conversion functions can share nodes through the context. Setting the value of the context ensures that the information stored in the context object is correct in the recursive transformation.

function traverseNode(ast, context) {
  context.currentNode = ast
  const transforms = context.nodeTransforms
  for (let i = 0; i < transforms.length; i++) {
    transforms[i](context.currentNode, context)
  }
  const children = context.currentNode.children
  if (children) {
    for (let i = 0; i < children.length; i++) { context.parent = context.currentNode context.childIndex = i traverseNode(children[i], context) } } }Copy the code

This allows you to add, replace, or delete nodes.

function transform(ast) {
  const context = {
    currentNode : null.childIndex: 0.parent: null.// Replace the node
    replaceNode(node) {
      context.parent.children[context.childIndex] = node
      context.currentNode = node
    },
    // Delete a node
    removeNode() {
      if (context.parent) {
        context.parent.children.splice(context.childIndex, 1)
        context.currentNode = null}},nodeTransforms: [
      transformElement,
      transformText
    ]
  }
  traverseNode(ast, context)
}
Copy the code

15.4.3 Entry and Exit

In the process of transforming AST nodes, it is often necessary to decide how to transform the current node according to the situation of the child nodes, which requires that the transformation operation of the parent node must wait until all its child nodes have been transformed.

The access to the node is divided into two stages, namely the entry stage and then the exit stage. When the transformation function is in the entry stage, the heart enters the parent node and then enters the child node. When the transition function is in the exit phase, it exits the child node first and then the parent node. In this way, as soon as we exit the node stage and process the node we are currently visiting, we can ensure that all its children are processed.

Methods are pushed into exitFns while iterating through the methods by setting an array of exitFns. Finally, the while loop executes the exitFns function, using queue first-in, last-out logic.

15.5. Convert template AST to Javascript AST

Vue

Template

Need to convert to

function render() {
	return h('div', {
		h('p'.'Vue'),
		h('p'.'Template')})}Copy the code

The AST generated from the template in the previous section needs to be converted to Javascript AST, and the description of Javascript AST,

const FunctionDeclNode = {
  type: 'FunctionDecl'.// indicates that the node is declared as a function
  id: {
    type: 'Identifier'.name: 'render' // name Specifies the name used to store identifiers
  },
  params: [].// Render function parameters
  body: [{type: 'ReturnStatement'.return: null}}]Copy the code

Code-for-vue-3-book /code-15.5.html at master · HcySunYang/code-for-vue-3-book

15.6 code generation

Convert the generated JavaScript to render function format

Code-for-vue-3-book /code-15.6.html at master · HcySunYang/code-for-vue-3-book