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方法实现简单计时器

    我来详细讲解一下如何基于Vue方法实现简单计时器。 前置知识 在学习本攻略前,需要你掌握以下内容: Vue.js基础知识,包括组件、数据绑定、生命周期等 Vue方法,包括计算属性、监听器、函数以及方法的使用 准备工作 在开始编写计时器之前,需要在Vue项目中创建一个组件,用来接收我们的计时器代码。 <template> <div> &…

    Vue 2023年5月29日
    00
  • 浅谈Vue 函数式组件的使用技巧

    下面我们就来详细讲解一下“浅谈Vue 函数式组件的使用技巧”的完整攻略。 什么是Vue函数式组件 在Vue中,组件代表着一个独立的模块,它可以被包含在页面中的任何地方,并可以重复使用。Vue中的组件有两种类型:状态组件和函数式组件。函数式组件是一种无状态组件,它不会保留状态,只会根据传入的props渲染其内容,通常用于列表、表格等无需维护复杂状态的组件中。在…

    Vue 2023年5月28日
    00
  • Vue中render函数调用时机与执行细节源码分析

    Vue中的render函数是用来生成虚拟DOM(Virtual DOM)的函数。当组件的状态发生改变时,Vue会重新执行render函数,生成新的虚拟DOM,并通过比对新旧虚拟DOM的差异,最终更新视图。在Vue的生命周期中,render函数执行的时机与执行细节如下: 执行时机 初始化时执行 组件的render函数在组件初始化时执行一次,用来生成组件的初始虚…

    Vue 2023年5月28日
    00
  • vue双击事件2.0事件监听(点击-双击-鼠标事件)和事件修饰符操作

    下面就给您详细讲解一下“Vue双击事件2.0事件监听(点击-双击-鼠标事件)和事件修饰符操作”的完整攻略。 什么是Vue双击事件2.0事件监听 Vue双击事件是指在Vue框架中注册的鼠标单击事件,在间隔一定时间后再次点击鼠标,使其触发双击事件的一种事件处理方式。在Vue 2.0版本中,双击事件具有更高的可定制性和可扩展性。 点击事件 在Vue中,可以通过 v…

    Vue 2023年5月29日
    00
  • 分享7个成为更好的Vue开发者的技巧

    分享7个成为更好的Vue开发者的技巧 作为一名Vue开发者,提高技能水平是非常重要的,下面是分享7个成为更好的Vue开发者的技巧: 1. 熟悉Vue的核心概念 Vue的核心概念包括:模板语法、组件、生命周期等,必须要熟悉和掌握它们,才能更好地开发Vue应用。 示例代码: <template> <div> <h3>{{ me…

    Vue 2023年5月27日
    00
  • vue+element开发使用el-select不能回显的处理方案

    下面是详细的解释。 背景 在Vue+Element前端开发中,使用el-select组件时,有时我们需要实现对下拉选项的回显功能。但是实际使用中,发现el-select在使用v-model绑定数据进行回显时存在问题,即无法正确地显示默认值。 原因 这是因为在Vue中,父组件在挂载之前会先于子组件执行,导致el-select还没有加载完,所以无法正确地设置默认…

    Vue 2023年5月28日
    00
  • 详解基于Vue cli生成的Vue项目的webpack4升级

    下面我将详细讲解“详解基于Vue CLI生成的Vue项目的webpack4升级”的完整攻略,包括具体步骤和示例说明。 1. 升级准备 1.1. 确认Vue CLI版本 首先需要确认当前使用的Vue CLI版本是否是3.x版本。如果不是3.x,需要先进行升级。可以通过以下命令检查版本: vue –version 如果版本低于3.x,可以通过以下命令进行升级:…

    Vue 2023年5月28日
    00
  • vue路由传参-如何使用encodeURI加密参数

    标题:Vue 路由传参 – 如何使用 encodeURI 加密参数 概述 Vue 路由传参是开发中常用的功能之一,通常我们直接在路由后面带上参数,例如:/blog?id=1。但是在实际应用中,由于参数内容可能包含一些特殊字符,如中文、空格等,因此需要对参数进行编码,以防止路由传参失效。其中,encodeURI() 可以将字符串进行编码,以便在 URI 中使用…

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