Vue源码解析之Template转化为AST的实现方法

Vue源码解析之Template转化为AST的实现方法

在Vue的编译过程中,将模板字符串转换为AST(抽象语法树)是至关重要的一步。本文将介绍Vue源码中关于Template转化为AST的实现方法。

Template转化为AST的流程

Vue中的模板转化为AST的流程主要包括以下几个步骤:

  1. 解析(parse)模板字符串,生成token数组
  2. 将token数组转化为AST

解析模板字符串

解析模板字符串的过程,核心是正则表达式,主要是针对html标签的匹配。具体实现方法可以看下面的代码示例:

function parseHTML(html, options) {

  while (html) {
    let textEnd = html.indexOf('<')
    if (textEnd === 0) {
      const endTag = html.match(endTagRE)
      if (endTag) {
        advance(endTag[0].length)
        continue
      }
    }

    let text, rest, next
    if (textEnd >= 0) {
      rest = html.slice(textEnd)
      while (
        !endTagRE.test(rest) &&
        !startTagOpenRE.test(rest) &&
        !commentRE.test(rest)
      ) {
        next = rest.indexOf('<', 1)
        if (next < 0) break
        textEnd += next
        rest = html.slice(textEnd)
      }
      text = html.substring(0, textEnd)
    }

      // ...more parsing code

  }
}

parseHTML函数输入的参数,就是我们要转化为AST的模板字符串,这里的实现方法中,首先判断字符串是否以"\<"开始,如果不是则认为是普通文本,直接截取并保存下来;如果是则表示匹配到了标签,这时候我们会进一步判断是否是结束标签,如果是的话则直接跳过;否则就是正常的开始标签,我们会进行标签解析。

将token数组转化为AST

经过第一步处理后,我们得到了一个token数组,接下来就可以通过此数组构建AST了。Vue的AST是通过使用 acorn 实现的,这是一个轻量级的 JavaScript 解析器,可以把JavaScript代码转化为抽象语法树(AST)。

Vue的AST结构和一般的AST结构有点不同,因为它包含了许多模板语法相关的节点,如v-if、v-for、v-on等等特殊节点。下面是一个AST的示例:

  {
    type: 1, // AST节点的类型,标签类型为1
    tag: 'div',    // 标签名称
    plain: true,   // 普通标签,非组件标签
    static: false, // 是否为静态节点
    ...
    children: [
      // 子节点为数组
      {
        type: 2, // AST节点的类型,属性类型为2
        name: 'v-if', // 属性名称
        value: 'isShow' // 属性值
      },
      {
        type: 3, // AST节点类型,文本节点类型为3
        text: 'hello world' // 文本节点的文本内容
      }
    ]
  }

示例演示

下面是一个简单的模板字符串,我们将会对其进行解析,得到AST:

<div>
  <h1 v-if="isShow">{{ title }}</h1>
  <p class="desc" v-for="(item, index) in list" :key="item.id" @click="handleClick">{{ item.name }}</p>
</div>

将上述模板字符串传入parseHTML函数进行解析,得到token数组:

[
  { type: 3, text: '\n  ' },
  { type: 1, tag: 'div', attrsList: [], attrsMap: {}, start: 0, end: 5, unarySlash: false, children: [] },
  { type: 3, text: '\n  ' },
  { type: 1,
    tag: 'h1',
    attrsList: [{ name: 'v-if', value: 'isShow' }],
    attrsMap: { v-if: 'isShow' },
    start: 7,
    end: 29,
    unarySlash: false,
    children: [ { type: 2, name: 'title', value: '_s(title)', dynamic: true, start: 20, end: 26, loc: [Object] } ] 
  },
  { type: 3, text: '\n  ' },
  { type: 1,
    tag: 'p',
    attrsList: 
     [ { name: 'class', value: 'desc' },
       { name: 'v-for', value: '(item, index) in list' },
       { name: ':key', value: 'item.id' },
       { name: '@click', value: 'handleClick' } ],
    attrsMap: 
     { class: 'desc',
       'v-for': '(item, index) in list',
       ':key': 'item.id',
       '@click': 'handleClick' },
    start: 31,
    end: 89,
    unarySlash: false,
    children: [ { type: 3, text: ' {{ item.name }}' } ] 
  },
  { type: 3, text: '\n' } 
]

将上述token数组传入parse函数中,得到AST:

{
  type: 1,
  tag: 'div',
  attrsList: [],
  attrsMap: {},
  rawAttrsMap: {},
  parent: undefined,
  children: [
    {
      type: 3,
      text: '\n  '
    },
    {
      type: 1,
      tag: 'h1',
      attrsList: [
        {
          name: 'v-if',
          value: 'isShow'
        }
      ],
      attrsMap: {
        'v-if': 'isShow'
      },
      rawAttrsMap: {},
      parent: [Circular],
      children: [
        {
          type: 2,
          expression: '_s(title)',
          tokens: [
            {
              '@binding': 'title'
            }
          ],
          text: '{{ title }}'
        }
      ],
      plain: false,
      static: false,
      staticRoot: false,
      hasBindings: true,
      key: undefined,
      scopedSlots: {},
      slotTarget: undefined,
      slotTargetDynamic: undefined,
      slotScope: undefined,
      attrs: []
    },
    {
      type: 3,
      text: '\n  '
    },
    {
      type: 1,
      tag: 'p',
      attrsList: [
        {
          name: 'class',
          value: 'desc'
        },
        {
          name: 'v-for',
          value: '(item, index) in list'
        },
        {
          name: ':key',
          value: 'item.id'
        },
        {
          name: '@click',
          value: 'handleClick'
        }
      ],
      attrsMap: {
        'class': 'desc',
        'v-for': '(item, index) in list',
        ':key': 'item.id',
        '@click': 'handleClick'
      },
      rawAttrsMap: {},
      parent: [Circular],
      children: [
        {
          type: 3,
          text: ' {{ item.name }}'
        }
      ],
      plain: false,
      static: false,
      staticRoot: false,
      hasBindings: true,
      key: undefined,
      scopedSlots: {},
      slotTarget: undefined,
      slotTargetDynamic: undefined,
      slotScope: undefined,
      attrs: [],
      computedName: undefined
    },
    {
      type: 3,
      text: '\n'
    }
  ],
  plain: false,
  static: false,
  staticRoot: false,
  hasBindings: false,
  key: undefined,
  scopedSlots: {},
  slotTarget: undefined,
  slotTargetDynamic: undefined,
  slotScope: undefined,
  attrs: []
}

以上就是Vue源码解析之Template转化为AST的实现方法的详细说明,其中包含了针对模板字符串的解析方法和将token转化为具有AST结构的方法,我们通过示例演示模板字符串的解析和AST的转化过程,希望对大家有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue源码解析之Template转化为AST的实现方法 - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • vue watch监控对象的简单方法示例

    下面是详细讲解 “Vue watch 监控对象的简单方法示例” 的完整攻略。 什么是 Vue Watch? Vue 是一个响应式框架,数据的变化能够自动触发视图的更新。Vue Watch 提供了一种方式来监听 Vue 实例中某个数据的变化,当它的值发生变化时,可以触发特定的操作。 如何在 Vue 中使用 Watch 在 Vue 实例中,可以使用 watch …

    Vue 2023年5月28日
    00
  • Vue前端如何实现生成PDF并下载功能详解

    生成PDF并下载功能是许多Web应用程序需要的一个重要功能,为Vue项目添加PDF的生成可以满足这些需求。在Vue前端如何实现生成PDF并下载功能的攻略中,需要完成以下步骤: 第一步:安装依赖 首先需要安装一个支持PDF生成的依赖包jspdf,方法如下: npm install jspdf –save 第二步:编写Vue组件 在Vue组件中,通过创建画布和…

    Vue 2023年5月27日
    00
  • vue完成项目后,打包成静态文件的方法

    Vue.js是一种流行的JavaScript框架,它适用于构建交互式Web应用程序。Vue.js提供了许多有用的特性,使得开发人员可以轻松地构建模块化的应用程序。当你完成了Vue.js的应用程序,你可以将其打包成静态文件。 以下是将Vue.js应用程序打包成静态文件的完整攻略: 步骤1:安装Vue CLI Vue CLI是一个专门用于Vue.js应用程序的命…

    Vue 2023年5月28日
    00
  • Vue重要修饰符.sync对比v-model的区别及使用详解

    我们来详细讲解一下“Vue重要修饰符.sync对比v-model的区别及使用详解”的完整攻略。 什么是.sync修饰符? .sync是Vue.js中的一个重要修饰符,它是v-bind的一个语法糖。.sync可以在子组件中使用父组件的数据,并实现双向绑定,自动更新父组件中的数据。 通常情况下,父组件将数据通过props传递给子组件,但是这样只能实现单向数据流,…

    Vue 2023年5月29日
    00
  • vue3中ref和reactive的用法和解析(推荐)

    Vue3中ref和reactive的用法和解析 Vue3中的响应式系统 在Vue3中,响应式系统被重构为了更强大的API,并且与Vue2有很多不同之处。其中两个重要的API是ref和reactive。 ref是一个函数,用于将一个基本类型值或一个对象转换为响应式数据。而reactive则是一个函数,用于将一个对象转换为响应式数据。 使用ref 使用ref来创…

    Vue 2023年5月28日
    00
  • VueCli3构建TS项目的方法步骤

    下面是关于使用VueCli3构建TS项目的攻略: 1. 安装 VueCli3 如果你尚未安装VueCli3,需要使用以下命令进行安装: npm install -g @vue/cli 2. 创建项目 创建项目时,需要选择”Manually select features”,并勾选TypeScript、Linter / Formatter等功能。命令如下: v…

    Vue 2023年5月27日
    00
  • axios封装与传参示例详解

    针对题目“axios封装与传参示例详解”,我将分以下几个部分进行详细讲解: 什么是axios及其用途 axios的基本用法 axios的封装原理及方法 axios传参的详解及示例 1. 什么是axios及其用途 axios是一个基于Promise的HTTP请求客户端,可以用于浏览器和Node.js。它具有以下优点: 可同时在浏览器和Node.js中使用。 能…

    Vue 2023年5月28日
    00
  • VSCode搭建vue项目的实现步骤

    下面我将详细讲解 “VSCode搭建vue项目的实现步骤”的完整攻略。整个过程包括: 安装Node.js 安装Vue CLI 创建Vue项目 配置VSCode开发环境 运行Vue项目 示例说明 1. 安装Node.js 首先,需要在电脑上安装Node.js,以便在命令行终端中使用npm安装Vue CLI和其他必要依赖项。Node.js的官方安装包可以在官网下…

    Vue 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部