搭错车的小火柴

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  80 随笔 :: 0 文章 :: 40 评论 :: 19万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

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'

 

3.createVNode

vue3中可以帮助你早点下班的9个开发技巧!

用法:

复制代码
const message = {
            setup() {
                const num = ref(1)
                return {
                    num
                }
            },
            template: `<div>
                        <div>{{num}}</div>
                        <div>这是一个弹窗</div>
                      </div>`
        }

// 初始化组件生成vdom
  const vm = createVNode(message)
  // 创建容器,也可以用已经存在的
  const container = document.createElement('div')
  //render通过patch 变成dom
  render(vm, container)
// 弹窗挂到任何你想去的地方  
document.body.appendChild(container.firstElementChild)
复制代码

 

posted on   搭错车的小火柴  阅读(13256)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示