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'