Vue Template 常用写法对应的 AST 数据结构

359 阅读3分钟

数据结构

节点类型 type

export const enum NodeTypes {
  ROOT, 
  ELEMENT,
  TEXT,
  COMMENT,
  SIMPLE_EXPRESSION,
  INTERPOLATION,
  ATTRIBUTE,
  DIRECTIVE,
  // containers
  COMPOUND_EXPRESSION,
  IF,
  IF_BRANCH,
  FOR,
  TEXT_CALL,
  // codegen
  VNODE_CALL,
  JS_CALL_EXPRESSION,
  JS_OBJECT_EXPRESSION,
  JS_PROPERTY,
  JS_ARRAY_EXPRESSION,
  JS_FUNCTION_EXPRESSION,
  JS_CONDITIONAL_EXPRESSION,
  JS_CACHE_EXPRESSION,

  // ssr codegen
  JS_BLOCK_STATEMENT,
  JS_TEMPLATE_LITERAL,
  JS_IF_STATEMENT,
  JS_ASSIGNMENT_EXPRESSION,
  JS_SEQUENCE_EXPRESSION,
  JS_RETURN_STATEMENT
}

基础数据示例

标签

<div></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

组件

<my-component></my-component>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "my-component",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

静态属性

<div id="test"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 6,
          "name": "id",
          "value": {
            "type": 2,
            "content": "test"
          }
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

注释

<!-- <div class="item"> -->
{
  "type": 0,
  "children": [
    {
      "type": 3,
      "content": " <div class=\"item\"> "
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

指令相关

v-bind

<div :key="key"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "bind",
          "exp": {
            "type": 4,
            "content": "key",
            "isStatic": false,
            "constType": 0
          },
          "arg": {
            "type": 4,
            "content": "key",
            "isStatic": true,
            "constType": 3
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

v-on

<div @click="handleClick"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "on",
          "exp": {
            "type": 4,
            "content": "handleClick",
            "isStatic": false,
            "constType": 0
          },
          "arg": {
            "type": 4,
            "content": "click",
            "isStatic": true,
            "constType": 3
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

v-model

<input v-model="text"/>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "input",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "model",
          "exp": {
            "type": 4,
            "content": "text",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": true,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

v-if

<div v-if="show"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "if",
          "exp": {
            "type": 4,
            "content": "show",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

v-show

<div v-show="show"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "show",
          "exp": {
            "type": 4,
            "content": "show",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

v-for

<div v-for="(item, index) in list"></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "for",
          "exp": {
            "type": 4,
            "content": "(item, index) in list",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

自定义指令

<p v-pin="200"></p>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "p",
      "tagType": 0,
      "props": [
        {
          "type": 7,
          "name": "pin",
          "exp": {
            "type": 4,
            "content": "200",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

综合示例

<div class="hello-class" v-for="(item, index) in list" v-if="item" :key="index" ></div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [
        {
          "type": 6,
          "name": "class",
          "value": {
            "type": 2,
            "content": "hello-class"
          }
        },
        {
          "type": 7,
          "name": "for",
          "exp": {
            "type": 4,
            "content": "(item, index) in list",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        },
        {
          "type": 7,
          "name": "if",
          "exp": {
            "type": 4,
            "content": "item",
            "isStatic": false,
            "constType": 0
          },
          "modifiers": []
        },
        {
          "type": 7,
          "name": "bind",
          "exp": {
            "type": 4,
            "content": "index",
            "isStatic": false,
            "constType": 0
          },
          "arg": {
            "type": 4,
            "content": "key",
            "isStatic": true,
            "constType": 3
          },
          "modifiers": []
        }
      ],
      "isSelfClosing": false,
      "children": []
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

嵌套数据展示

文本

<div>hello</div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": [
        {
          "type": 2,
          "content": "hello"
        }
      ]
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

插值

<div>{{hello}}</div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": [
        {
          "type": 5,
          "content": {
            "type": 4,
            "isStatic": false,
            "constType": 0,
            "content": "hello"
          }
        }
      ]
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

文本 + 插值

<div>{{hello}} world</div>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "div",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": [
        {
          "type": 5,
          "content": {
            "type": 4,
            "isStatic": false,
            "constType": 0,
            "content": "hello"
          }
        },
        {
          "type": 2,
          "content": " world"
        }
      ]
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

slot

<my-component>
  <template #title>
    hello title
  </template>
</my-component>
{
  "type": 0,
  "children": [
    {
      "type": 1,
      "ns": 0,
      "tag": "my-component",
      "tagType": 0,
      "props": [],
      "isSelfClosing": false,
      "children": [
        {
          "type": 1,
          "ns": 0,
          "tag": "template",
          "tagType": 3,
          "props": [
            {
              "type": 7,
              "name": "slot",
              "arg": {
                "type": 4,
                "content": "title",
                "isStatic": true,
                "constType": 3
              },
              "modifiers": []
            }
          ],
          "isSelfClosing": false,
          "children": [
            {
              "type": 2,
              "content": " hello title "
            }
          ]
        }
      ]
    }
  ],
  "helpers": [],
  "components": [],
  "directives": [],
  "hoists": [],
  "imports": [],
  "cached": 0,
  "temps": 0
}

示例代码

const { baseParse } = require('@vue/compiler-core')
const fs = require('fs')
const path = require('path')


const source = `
    <div></div>
`.trim()

const result = baseParse(source)

function replacer(key, value) {
  if (key === "loc") { // 去除用不到的属性
    return undefined;
  }
  return value;
}

fs.writeFile(path.resolve('.', 'ast.json'), JSON.stringify(result, replacer, 2), (err) => {
})

其他

目前只列出了 template 中的部分写法。