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'