第十五节: AST树转render函数

58 阅读1分钟

分析流程

/*
_c -> 创建元素信息
_v -> 创建文本信息
_s -> 把变量替换成对应的东西 {{ name }} -> _s(name)
// 分析要拼接的格式如下:
function render() {
  return `
    _c(
      'div', 
      // => style进行对象化处理
      {id: 'app', style: {'color': 'red', 'font-size': '20px'}}
      _v("你好, " + _s(name)),//文本去替换拼接
      _c(
        'span',
        {
          class: 'text',
          style: {
            'color': 'green'
          }
        },
        _v(_s(age))
      )
    )
  ` 
}
*/

模板文件 index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app" style="color: red;font-size: 20px;">
    你好, {{ name }} 撒旦飞洒地方
    <span class="text" style="color: green">{{ age }}</span>
    <p>喜喜喜喜喜喜</p>
  </div>
  <script src="dist/vue.js"></script>
  <script>
    // umd -> window.Vue
    // console.log(Vue);
    const vm = new Vue({
      el: "#app",
      data() {
        return {
          name: "蒋小白",
          age: 18,
          msg: 'vue zzz',
          a: {
            b: 10
          },
          list: [1,2, 3]
        }
      },
      props: {},
      watch: {},
      computed: {}
    })
  </script>
</body>
</html>
模板编译文件的入口文件 compile/index.js
import { parseHtmlToAst } from './astParser';
import { generate } from './generate';
// 进行模板编译的
function compileToFunction(html) {
  // html -> 转化成Ast
  const ast = parseHtmlToAst(html);// 源码叫 parseHTML
  
  //------------------------- 如下代码是本次笔记的内容 --------------------
  // AST -> render
  const code = generate(ast);
  const render = new Function(`
    with(this) { return ${code} }
  `)
  console.log(render);
}

遍历成render函数的处理文件

// 处理属性 ①
function formatProps(attrs) {
  let attrStr ='';

  for(let i = 0; i < attrs.length; i++) {
    let attr = attrs[i];
    // 处理style的格式
    if(attr.name === 'style') {
      let styleAttrs = {};
      attr.value.split(';').map((styleAttr) => {
        let [key, value] = styleAttr.split(':');
        styleAttrs[key] = value;
      });
      attr.value = styleAttrs;
    }

    attrStr += `${attr.name}:${JSON.stringify(attr.value)},`
  }
  // 返回并去掉最后一个,
  // id:"app",style:{"color":" red","font-size":" 20px"}
  return `{${attrStr.slice(0, -1)}}`;
}

// 格式化children ②
function getChildren(el) {
  const children = el.children;
  if(children) {
    // generateChild 精髓
    return children.map(child => generateChild(child)).join(',');
  }
}

// 匹配双大括号正则
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
// ③
function generateChild(node) {
  // type 1 元素 3文本
  if(node.type === 1) {
    return generate(node);
  } else if(node.type === 3) {
    let text = node.text;//获取文本
    if(!defaultTagRE.test(text)) {//纯文本
      return `_v(${JSON.stringify(text)})`;
    }

    let match,
        index,
        lastIndex = defaultTagRE.lastIndex = 0,
        textArr = [];

    //可能会有 {{ xxx }} or {{}} or {{}}xxx
    while(match = defaultTagRE.exec(text)) {
      index = match.index;
      if(index > lastIndex) {
        textArr.push(JSON.stringify(text.slice(lastIndex, index)));
      }

      textArr.push(`_s(${match[1].trim()})`);
      lastIndex = index + match[0].length;
    }

    if(lastIndex < text.length) {
      textArr.push(JSON.stringify(text.slice(lastIndex)))
    }

    return `_v(${textArr.join('+')})`
  }
}


function generate(el) {
  console.log(el);
  // 处理children;
  let children = getChildren(el);

  let code = `
    _c(
      '${el.tag}',//标签
      ${
        el.attrs.length > 0
        ? `${formatProps(el.attrs)}` 
        : 'undefined' 
      }
      ${
        children ? `,${children}` : ''
      }
    )
  `;
  console.log(code, 88888);// @9527
  return code;
}

export {
  generate
}

处理的给是如下 (ctrl+f) @9527 处

image.png