vue-compiler-sfc主要是用来解析SFC组件,我们都知道,一个SFC(*.vue)文件三大要素是template、script、style,vue-compiler-sfc就是负责解析这三大要素。从源码src目录下,每个文件的命名大致就可以看出来各个文件的作用,我先从 compiler-sfc 的 index.ts 开始。
(最近看源码深有感触的是,官文只是在说要怎么做,看了源码才知道为什么要这么做,并且还可以怎么做……,再就是测试用例真的很有用)
index.ts 主要是导出了其他文件的功能。
1.parse.ts:解析三大要素
三大要素template、script、style的类型定义:

想要看看具体解析后的sfc里面descriptor的结构,可以去运行vue-next/packages/compiler-sfc/__tests__ 下面个文件的测试用例。
我们运行一个单测,看看parse script的结果:
test('should expose top level declarations', () => {
const script = compile(`
<script setup>
import { x } from './x'
let a = 1
const b = 2
function c() {}
class d {}
</script>
`)
assertCode(script.content)
console.log('----------------------')
console.log(script)
expect(script.content).toMatch('return { a, b, c, d, x }')
})
// 结果
{
type: 'script',
// 可以看到,编译后的content,帮忙 return 了所有变量
content: "import { x } from './x'
" +
'
' +
'export default {
' +
' expose: [],
' +
' setup(__props) {
' +
'
' +
' let a = 1
' +
' const b = 2
' +
' function c() {}
' +
' class d {}
' +
'
' +
'return { a, b, c, d, x }
' +
'}
' +
'
' +
'}',
loc: {
source: '
' +
" import { x } from './x'
" +
' let a = 1
' +
' const b = 2
' +
' function c() {}
' +
' class d {}
' +
' ',
start: { column: 21, line: 2, offset: 21 },
end: { column: 7, line: 8, offset: 131 }
},
attrs: { setup: true },
setup: true,
bindings: {
x: 'setup-maybe-ref',
a: 'setup-let',
b: 'setup-const',
c: 'setup-const',
d: 'setup-const'
},
map: SourceMap {
version: 3,
file: null,
sources: [ 'anonymous.vue' ],
sourcesContent: [
'
' +
' <script setup>
' +
" import { x } from './x'
" +
' let a = 1
' +
' const b = 2
' +
' function c() {}
' +
' class d {}
' +
' </script>
' +
' '
],
names: [],
mappings: 'AAEM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC,CAAC,CAAC;;;;AAFe;AACpB,CAAC,CAAC,CAAC,CAAC,CAAC,CACC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC,CAAC,CAAC;;;;'
},
scriptAst: undefined,
scriptSetupAst: [
Node {
type: 'ImportDeclaration',
start: 7,
end: 30,
loc: [SourceLocation],
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
specifiers: [Array],
source: [Node]
},
Node {
type: 'VariableDeclaration',
start: 37,
end: 46,
loc: [SourceLocation],
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
declarations: [Array],
kind: 'let'
},
Node {
type: 'VariableDeclaration',
start: 53,
end: 64,
loc: [SourceLocation],
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
declarations: [Array],
kind: 'const'
},
Node {
type: 'FunctionDeclaration',
start: 71,
end: 86,
loc: [SourceLocation],
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
id: [Node],
generator: false,
async: false,
params: [],
body: [Node]
},
Node {
type: 'ClassDeclaration',
start: 93,
end: 103,
loc: [SourceLocation],
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
id: [Node],
superClass: null,
body: [Node]
}
]
}
vue3的sfc的style模块多了一个 parseCssVars 的功能,支持在css中使用响应式的变量,用过v-bind去绑定。
示例见:vue-next/packages/compiler-sfc/__tests__/cssVars.spec.ts
2.compileStyle.ts 编译style模块
vue3 内置了对各种css语法的预处理器,详情见:vue-next/packages/compiler-sfc/src/stylePreprocessors.ts,主要包括:
export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus'