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