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'\n" +
' \n' +
'export default {\n' +
' expose: [],\n' +
' setup(__props) {\n' +
'\n' +
' let a = 1\n' +
' const b = 2\n' +
' function c() {}\n' +
' class d {}\n' +
' \n' +
'return { a, b, c, d, x }\n' +
'}\n' +
'\n' +
'}',
loc: {
source: '\n' +
" import { x } from './x'\n" +
' let a = 1\n' +
' const b = 2\n' +
' function c() {}\n' +
' class d {}\n' +
' ',
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: [
'\n' +
' <script setup>\n' +
" import { x } from './x'\n" +
' let a = 1\n' +
' const b = 2\n' +
' function c() {}\n' +
' class d {}\n' +
' </script>\n' +
' '
],
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'
