vue解析、编译
思路
vue的模板解析和编译的过程可以简化为下面的图片
这里都以vue3为例
解析
vue模板文件会被vue解析器解析,生成vue ast语法树。语法树节点会描述标签名、属性、子节点、绑定事件、绑定样式、静态节点标记等信息
步骤
针对SFC文件的解析,会通过模板状态机来完成,类似HTML状态机解析成tokens
开始标签 -> 标签名 -> 标签属性(vue中独有的动态绑定,vfor,vif特殊属性的解析,事件绑定解析,指令解析等
)-> 开始标签结束 -> 其中子节点的解析或内容解析 -> 结束标签解析 -> 标签名 -> 结束标签结束
PS. 换行符、回车、制表符、多行连接符等都要解析,解析成对应的节点
结果
生成出来的AST树分析优化后交由生成器处理
优化
vue会分析节点是否为静态节点,静态节点不会改变,意味着生成一次渲染函数就可以缓存起来,方便以后使用
编译
AST数据要通过vue代码生成器生成最终的渲染函数,渲染函数都是js函数,可以直接执行。生成的渲染函数相当于上图中的目标代码
SFC编译
SFC中的<template>、<script>、<style>标签会有不同的编译器编译,最终生成对应的js文件
script
SFC中,仅允许一个带有export default默认导出的script标签。例外的是,允许两个script标签,一个是script setup标签,一个是不带默认导出的script标签,vue会自动把script标签中定义的东西合并到setup导出中去
针对import导入部分,defineProps等这些宏定义,变量声明,函数声明,函数声明等都要生成对应js代码,最终生成的js代码
vue sfc文件中的代码
<script> |
生成的代码
import __vite__cjsImport0_lodash from "/node_modules/.vite/deps/lodash.js?v=8d62954f"; |
template
template会生成对应的render函数,render函数中会描述<template>标签中的DOM结构
sfc template标签中的代码
<template> |
生成的渲染函数
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { |
vue中的元素存在vue定义的很多属性,比如绑定,v-if,插槽等。针对每一个特殊属性都有对应的生成器生成对应代码
下面仅展示部分元素,属性的代码生成
- v-for:生成列表,列表每一项都会创建一个VNode(v-for列表渲染)
// <div v-for="(item, index) in 5" :key="index">
// {{ item }}
// </div>
// 简化后的关键代码
_renderList(5, (item, index) => {
return (_openBlock(), _createElementBlock("div", { key: index }, item, 1))
}) - v-if:生成的都是js代码,生成一个三元表达式块就可以,块里面有对应不同判断条件下创建不同VNode的代码(毕竟写v-if指令也是条件渲染,对应不同元素)
// <div v-if="isSecondOdd">
// Odd
// </div>
// <div v-else>
// No Odd
// </div>
// 简化后的关键代码
($setup.isSecondOdd)
? (_openBlock(), _createElementBlock("div", _hoisted_1, " Odd "))
: (_openBlock(), _createElementBlock("div", _hoisted_2, " No Odd "))
style
<style>标签中的内容会生成成一个独立的js文件
我这里的demo项目的文件名是这个:If.vue?vue&type=style&index=0&lang.css
下面是生成的文件的代码(别看这个文件是.css结尾,但这个文件确是js类型文件)
// 热更新部分的代码已去除 |
标签的类选择器和内联样式则是生成在render函数中,其实就是_createElementBlock函数的接收参数(属性)
生成的代码。可以看到下面的_createElementVNode接收的属性class和style
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { |
解析 -> 编译
vue源码,比较容易看懂,没有精简
export function baseCompile( |
SFC文件中,官方说必须包含三个必要的标签<template>、<script>、<style>,但编译检测只要包含其中两个即可
使用vite构建vue项目
这里只针对代码解析+编译的过程,不会从浏览器请求代码开始说起
vite:vue-plugin
会在开发服务器、打包阶段都有调用,目的是将vue文件转换成对应的js代码文件。开发阶段有transform的Hook,打包阶段有build相关Hook。都会编译SFC文件
同时这个插件也会生成HMR所需的代码
