This is the 21st day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

One of the core parts of the rendering process is the generation of the render function. The core parts of the whole process are divided into two parts:

  1. throughparseHTMLFunction willHTMLCompiled intoAST(PS: Completed above)
  2. throughASTgeneraterenderFunction (PS: next important point)

The target

First of all, let’s be clear when we have the following HTML

<div id="app"><p>hi {{msg}}</p> hello</div>
Copy the code

This is compiled to AST as with the previous parseHTML

This corresponds to the generated render function

function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('p', [_v("hi " + _s(msg))]), _v(" hello")]}}Copy the code

The correlation of HTML, AST and render function is clarified, and the whole process of the three is

function compileToFunction(template) {
  // 1. Parse HTML to generate AST
  const ast = parseHTML(template);
  // optimize(ast, options)

  // 2. Generate render function according to AST
  const { render } = generator(ast);

  // render returns the virtual DOM
  return {
    ast,
    render,
  };
}
Copy the code

The expected generator function should be

function generator(ast) {
  // Code to be generated
  return {
    render: new Function(`with(this) { 
      return ${code}} `),}; }Copy the code

The code is the one above

_c('div', {
  attrs: {
    "id": "app"
  }
}, [_c('p', [_v("hi " + _s(msg))]), _v(" hello")])
Copy the code

The final function returned uses with, where code is a string (as shown above). The core of the AST generation process of the Render function is to recursively traverse all AST nodes and concatenate the corresponding string by judging the AST node type

Because Vue components can only have one tag, we can directly get the tag of the tag of code

const code = `_c(
    "${el.tag}"
)`;
Copy the code

If the current element node has attributes, as shown in the following HTML

<div id="app" class="content"></div>
Copy the code

When it is converted to an AST, its properties are represented as

{
  attrs: [{
    name: 'id'.value: 'app'
  }, {
    name: 'class'.value: 'content'}}]Copy the code

In the render function it is

{
  attrs: {
    'id': 'app'.'class': 'content'}}Copy the code

You need a function that handles the attributes on the element tag and converts them to the format expected by the Render function

const genProps = (attrs) = > {
  let str = "";
	
  // Iterate over all attributes of the current element node on the AST
  for (let i = 0; i < attrs.length; i++) {
    let attr = attrs[i];
		
    Word-wrap: break-word! Important;" > < span style="color: aqua; ==> {style: {color: aqua}}
    if (attr.name === "style") {
      let obj = {};
			
      // Cut each attribute and its value in the style
      // "color: aqua; height: 200px;" ===> ['color: aqua', ' height: 200px']
      attr.value.split(";").forEach((item) = > {
        // Get attributes and their values
        let [key, value] = item.split(":");
        if(key) { obj[key] = value.trim(); }}); attr.value = obj; }// Concatenate data with strings
    str += `${attr.name}: The ${JSON.stringify(attr.value)}, `;
  }
	
  // Return a string with concatenation completed
  return ` {${str.slice(0, -1)}} `;
};
Copy the code

At this point the code can be further refined

const code = `_c(
    "${ast.tag}", 
    ${ast.attrs.length ? genProps(ast.attrs) : "undefined"}) `;
Copy the code

This completes the initialization of the render function generation and the processing of the properties