This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

How does Vue translate HTML into AST? The general process is a bit complicated. Before we start writing the actual code, let’s take a look at a simple compilation example

The first step is to compile this HTML explicitly

<div id="app">
  <p>hello</p>
</div>
Copy the code

Compile to AST (easy version)

The results can be used to reverse the first utility function: create the AST root object

const createASTElement = (tagName, attrs) = > {
  return {
    tag: tagName, / / tag name
    type: ELEMENT_TYPE, // Node type
    children: []./ / child nodes
    attrs, / / property
    parent: null./ / the parent node
  };
}
Copy the code

Each AST object has a type attribute that represents the DOM type of the current node, with a label node of 1 and a text node of 3

const ELEMENT_TYPE = 1;
const TEXT_TYPE = 3;
Copy the code

The process,

The HTML above is passed in as a string at the beginning of the processing

let html = '<div id="app"><p>hello</p></div>';
Copy the code

Two variables need to be defined before parsing begins

let currentParent = null; // Identifies the current parent
let stack = [];
Copy the code

If you parse the string, you will initially determine that it is a start tag using the re startTagOpen. You can get the tag name and type 1

At this point, the problem is how to continue to obtain its corresponding attributes?

In Vue, the processing method is: cut the string, cut the HTML, and then cut the data to continue the recursive processing. For example, the above parsing to

 html = 'id="app"><p>hello</p></div>';
Copy the code

Because of the need to constantly intercept HTML in the parsing process, it can be encapsulated

const advance = (n) = > {
  html = html.substring(n);
};
Copy the code

After the HTML is processed, the regular attribute matches the data and the name and value of the attribute can be obtained

let attr = html.match(attribute);
const name = arr[1];
const value = attr[3] || attr[4] || attr[5];
Copy the code

At this point, the start label processing is complete, and the corresponding AST node can be created through the above, and all the start label processing can be extracted into the function start

const start = (tagName, attrs = []) = > {
  // Create an AST element when the start tag is encountered
  let element = createASTElement(tagName, attrs);

  if(! root) { root = element; }// mark the current element as parent
  currentParent = element;
  // Put the start tag on the stack
  stack.push(element); 
};
Copy the code

When you’re done, you need to call advance again to intercept the HTML, where the HTML is

 html = '><p>hello</p></div>';
Copy the code

If you continue processing, the re startTagClose matches the data, indicating that the end symbol of a tag >, indicating that the current parsing is complete

Continue parsing the p tag, as described above, until it is parsed, at which point the HTML is parsed

 html = 'hello</p></div>';
Copy the code

At this point, it can be judged as a text node, directly create the corresponding AST, and extract it as the function chars

const chars = (text) = > {
  // Remove extra Spaces
  text = text.replace(/\s/g."");

  if (text) {
    return {
      text,
      type: TEXT_TYPE, }; }};Copy the code

At this point the stack for

stack = ['divAST'.'pAST'];
Copy the code

The current HTML for

 html = '</p></div>';
Copy the code

At this time, it can be judged as the end tag, and the corresponding position of the start tag node can be found in the stack, and the end processing can be carried out successively. Thus, the general process of the whole HTML to AST is established, and a lot of details are not processed, such as the processing of the annotation node