Vue3编译器

本文深入探讨Vue3的编译过程,从parse()、transform()到generate(),解析模板成AST,转换为JavaScript AST,并生成渲染函数。详细介绍了模板的词法分析、语法分析、语义分析以及AST节点的操作和转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是编译

编译是指将源代码翻译成目标代码的过程

完整编译过程:
完整编译过程
整个编译过程可以分为编译前端和编译后端。

  • 编译前端通常与目标平台无关,仅负责分析源代码。
  • 编译后端通常与目标平台有关,涉及中间代码的生成、优化及目标代码生成。

Vue的编译流程

对Vue的模板编译器来说,源代码就是组件的模板,标代码就是渲染函数
编译器工作流程
AST是 abstract snytax tree 的首字母缩写,即抽象语法树,是一个具有层级结构的对象。
模板AST就是用来描述模板的抽象语法树,具有与模板同构的嵌套结构。

  • 不同类型的节点通过type属性区分
  • 子节点存储在children数组中
  • 标签节点的属性节点和指令节点存储在props数组中
  • 不同类型的节点会使用不同的对象属性进行描述
const ast = {
   
   
	// 逻辑根节点
	type: 'Root',
	children: [
		// div标签节点
		{
   
   
			type: 'Element',
			tag: 'div',
			children: [
				// h1标签节点
				{
   
   
					type: 'Element',
					tag: 'h1',
					props: [
						// v-if指令节点
						{
   
   
							type: 'Directive',
							name: 'if'
							exp: {
   
   
								// 表达式节点
								type: 'Expression',
								content: 'ok'
							}
						}
					]
				}
			]
		}
	
	]
}

对具体步骤进行一些封装

  • parse():完成对模板的词法分析和语法分析,得到模板AST
  • transform():完成对模板的语义分析,实现模板AST到JavaScript AST的转换工作
  • generate():根据JavaScript AST生成渲染函数
    Vue的模板编译完整过程
// 目标代码生成过程
const templateAST = parse(template)
const jsAST = transform(templateAST)
const code = generate(jsAST)

parse()

将模板字符串解析为模板AST的解析器。

解析器的入参是字符串模板,解析器会逐个读取字符串模板中的字符,根据一定的规则通过有限状态自动机进行切割。

有限状态指有限个状态,自动机指随着字符的输入,解析器会自动在不同状态间迁移。

有限状态自动机可以帮助我们完成对模板的标记化

文本模式指的是解析器在工作时所进入的一些特殊状态,在不同的特殊状态下,解析器对文本的解析行为会有所不同。解析器行为规范

模式 能否解析标签 是否支持HTML实体
DATA
RCDATA
RAWTEXT
CDATA

解析器的状态机图

const parse = (str) => {
   
   
  // 定义上下文对象
  const context = {
   
   
    // source是模板内容,用于在解析过程中进行消费
    source: str,
    // 解析器当前处于的文本模式,初始模式为DATA
    mode: TextModes.DATA,
    // 消费指定数量的字符
    advanceBy(num) {
   
   
      context.source = context.source.slice(num);
    },
    // 消费空白字符
    advanceSpaces() {
   
   
      // 匹配空白字符
      const match = /^[\t\r\n\f ]+/.exec(context.source);
      if (match) {
   
   
        // 消费空白字符
        context.advanceBy(match[0].length);
      }
    },
  };
  // 调用parseChildren函数开始解析, 它返回解析后得到的子节点
  const nodes = parseChildren(context, []);

  return {
   
   
    type: "Root",
    children: nodes,
  };
};

解析子元素

function parseChildren(context, ancestors) {
   
   
  // 定义nodes数组存储子节点, 是最终的返回值
  let nodes = [];
  // 从上下文对象中取得当前状态,包括模式mode和模板内容source
  const {
   
    mode, source } = context;

  // 开启while循环,只要满足条件就会一直对字符串进行解析
  while (!isEnd(context, ancestors)) {
   
   
    let node;
    // 只有DATA模式和RCDATA模式才支持插值节点的解析
    if (mode === TextModes.DATA || mode === TextModes.RCDATA) {
   
   
      // 只有DATA模式才支持标签节点的解析
      if (mode === TextModes.DATA && source[0] === "<") {
   
   
        if (source[1] === "!") {
   
   
          if (source.startsWith("<!--")) {
   
   
            // 注释
            node = parseComment(context);
          } else if (source.startsWith("<![CDATA[")) {
   
   
            // CDATA
            node = parseCDATA(context, ancestors);
          }
        } else if (source[1] === "/") {
   
   
          // 结束标签
          // 状态机缺少与之对应的开始标签
          console.log("无效的结束标签");
          continue;
        } else if (/[a-z]/i.test(source[1])) {
   
   
          // 标签
          node = parseElement(context, ancestors);
        }
      } else if (source.startsWith("{
   
   {")) {
   
   
        // 解析插值
        node = parseInterpolation(context);
      }
    }

    // node不存在,说明处于其他模式,此时一切内容都作为文本处理
    if (!node) {
   
   
      // 解析文本节点
      node = parseText(context);
    }

    nodes.push(node);
  }

  return nodes;
}


解析器遇到开始标签时,将该标签压入父级节点栈,并开启新的状态机;遇到结束标签且父级节点栈中存在于该标签同名的开始标签节点时,停止当前正在运行的状态机

function isEnd(context, ancestors) {
   
   
  // 当模版内容解析完毕后,停止
  if (!context.source) return true;

  // 与父级节点栈内所有节点作比较
  for (let i = ancestors.length - 1; i >= 0; --i) {
   
   
    // 如果遇到结束标签,只要父级节点栈中存在与该标签同名的开始标签节点,就停止
    if (context.source.startWith(`</${
     
     ancestors[i].tag}`)) {
   
   
      return true
<think>好的,我现在需要帮助用户解决在Vue3中配置和使用SASS编译器的问题。首先,我得回顾一下自己之前学过的相关知识,同时结合用户提供的三个引用内容,虽然这些引用主要是关于Vue2和HBuilderX的,但可能需要调整到Vue3的环境。 首先,Vue3的项目通常使用Vue CLI或者Vite来搭建,而用户的问题是关于配置SASS编译器的。SASS在Vue项目中通常通过安装相应的loader来处理,比如sass-loader。在Vue2中,配置可能是在webpack配置文件里,比如util.js或者webpack.base.conf.js,如引用1和2提到的。但Vue3如果使用Vite的话,配置方式会不同,因为Vite使用rollup或esbuild,而不是webpack。所以可能需要分别考虑两种情况:使用Vue CLI创建的Vue3项目和使用Vite创建的Vue3项目。 接下来,用户提供的引用中提到在Vue2中需要修改util.js或webpack.base.conf.js来配置sass-loader。而在Vue3中,如果使用Vue CLI,虽然底层还是webpack,但配置方式可能有所变化。而如果使用Vite,则配置文件是vite.config.js,并且配置方法更简单,可能不需要太多手动配置。 首先,用户需要安装sass和sass-loader。根据Vue3的文档,可能需要安装sass(Dart Sass)而不是node-sass,因为node-sass已经过时了。所以正确的依赖应该是sass和sass-loader。需要确认在Vue CLI项目中是否需要sass-loader,或者在Vite中是否自动处理。 然后,全局变量的配置。在引用1中,提到修改util.js中的sass-loader配置,添加data选项来引入全局scss文件。在Vue3Vue CLI项目中,可能需要通过vue.config.js来配置loader。而在Vite中,可以在vite.config.js中使用css.preprocessorOptions来配置全局变量。 另外,用户可能遇到的常见问题包括node版本问题,如引用3提到的,可能需要调整node版本或重新安装sass编译器。需要提醒用户注意这一点,尤其是在安装依赖时可能出现的兼容性问题。 现在需要整理步骤: 1. 安装依赖:sass和sass-loader(可能不需要,因为Vue CLI或Vite已经内置了相应的处理,但可能需要安装sass)。 2. 创建或修改配置文件(vue.config.js或vite.config.js)来配置全局变量。 3. 在组件中使用SASS/SCSS,通过lang="scss"指定。 4. 处理可能出现的错误,比如node版本问题,sass编译器安装问题等。 需要分情况说明:使用Vue CLI创建的Vue3项目和使用Vite创建的Vue3项目。但可能用户更倾向于使用Vite,因为Vue3推荐使用Vite。或者需要同时覆盖两种情况。 现在需要验证这些步骤是否正确。比如,在Vite项目中,安装sass,然后在vite.config.js中配置preprocessorOptions,添加additionalData来引入全局文件。然后在组件中使用lang="scss"即可。 另外,关于引用中的内容,虽然引用是关于Vue2的,但配置思路可能类似,只是配置文件的位置和语法不同。需要将配置方法转换到Vue3的相应工具链中。 可能还需要提及在单文件组件中如何使用scss,例如在style标签中添加lang="scss",并且确保安装了sass依赖。 最后,生成相关问题,比如如何配置全局变量、如何处理编译错误、如何优化SASS编译等。 总结回答结构: 1. 安装依赖:sass 2. 配置全局变量(根据不同构建工具) a. Vue CLI项目:修改vue.config.js中的css.loaderOptions.scss b. Vite项目:修改vite.config.js中的css.preprocessorOptions.scss 3. 在组件中使用scss 4. 常见问题处理(node版本、依赖安装) 确保步骤清晰,语法正确,并且引用中的方法适当调整到Vue3的环境。</think>### Vue3中配置和使用SASS编译器教程 #### 1. 安装依赖 在项目根目录执行以下命令安装SASS编译器: ```bash npm install sass -D # 或使用yarn yarn add sass -D ``` #### 2. 配置全局变量(以Vite项目为例) 在`vite.config.js`中添加以下配置[^1]: ```javascript import { defineConfig } from &#39;vite&#39; export default defineConfig({ css: { preprocessorOptions: { scss: { additionalData: `@import "@/styles/variables.scss";` } } } }) ``` > 注:`@/`表示src目录,需在路径别名中配置 #### 3. 单文件组件使用 在`.vue`文件中添加: ```html <style lang="scss"> $primary-color: #1890ff; .container { background: $primary-color; } </style> ``` #### 4. 配置扩展(Vue CLI项目) 在`vue.config.js`中配置: ```javascript module.exports = { css: { loaderOptions: { scss: { additionalData: `@import "~@/styles/variables.scss";` } } } } ``` #### 5. 常见问题处理 - **Node版本问题**:建议使用Node 14+版本,若出现`sass`编译错误,尝试重装编译器: ```bash npm rebuild sass ``` - **路径别名失效**:确保在`vite.config.js`/`vue.config.js`中正确配置`resolve.alias` - **变量未生效**:检查文件路径是否正确,全局文件是否需要添加`!default`标记
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值