在vue2中会去解析你的template里面的代码变成对应的AST语法树,同时生成render函数,代码如下

const template = `<div id="app" color="red" bg="22">{{name}}<div>{{aaa}}</div></div>`;
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; //用来描述标签名的
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 </div> 捕获的是结束标签的标签名
const attribute =
  /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的
const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // 匹配{{ }} 中间的内容

function parserHTML(html) {
  function advance(n) {
    html = html.substring(n); //每次根据传入的长度截取html
  }
  let root; // 树的操作,需要根据开始标签和结束标签生成一个树
  let stack = [];
  function createASTElement(tagName, attrs) {
    return {
      tag: tagName,
      attrs,
      children: [],
      parent: null,
      type: 1,
    };
  }
  function start(tagName, attrs) {
    console.log("开始标签", tagName, attrs);
    let element = createASTElement(tagName, attrs);
    if (root === undefined) {
      root = element;
    }
    let parent = stack[stack.length - 1]; //取到栈里面的最后一个
    if ((parent)) {
      // 让儿子记住父亲
      element.parent = parent;
      // 让父亲记住儿子
      parent.children.push(element);
    }

    stack.push(element);
  }
  function end(tagName) {
    console.log("结束标签", tagName);
    stack.pop();
  }
  function chars(text) {
    text = text.replace(/\s/g, "");
    if (text) {
      let parent = stack[stack.length - 1];
      parent.children.push({
        type: 3,
        text,
      });
    }
    console.log("文本标签", text);
  }
  while (html) {
    let textEnd = html.indexOf("<");
    if (textEnd === 0) {
      const startTagMatch = parseStartTag();
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }
      let matches;
      //走到结束标签
      if ((matches = html.match(endTag))) {
        end(matches[1]);
        advance(matches[0].length);
        continue;
      }
    }
    let text;
    if (textEnd >= 0) {
      text = html.substring(0, textEnd);
    }
    if (text) {
      advance(text.length);
      chars(text);
      // break;
    }
  }

  // 解析开始标签 {tag:'div',attrs:[...}]}
  function parseStartTag() {
    const matches = html.match(startTagOpen);
    if (matches) {
      const match = {
        tagName: matches[1],
        attrs: [],
      };
      advance(matches[0].length); //解析之后就删除
      //继续解析标签的属性
      let attr, end;
      while (
        !(end = html.match(startTagClose)) &&
        (attr = html.match(attribute))
      ) {
        //只要没有匹配到结束标签就一直匹配
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5] || true,
        });
        advance(attr[0].length); //解析一个属性删除一个
      }
      //这时候到结束标签了,去处理结束标签 >
      if (end) {
        advance(end[0].length);
        return match;
      }
    }
  }
  return root;
}

const ast = parserHTML(template);
console.log(ast);
Last modification:October 21, 2021
If you think my article is useful to you, please feel free to appreciate