vue3源码学习03-编译原理、静态提升及patch流程

87 阅读2分钟

本节主要内容

  1. 编译流程
  2. 静态提升
  3. patch流程

ps:再不写出来我就真的快忘了。。。

编译流程

1、app.mount里拿到template

// /packages/runtime-dom/src/index.ts
// 又回到最初的起点
export const createApp = ((...args) => {
  const app = ensureRenderer().createApp(...args)
  const { mount } = app
  // 重写mount方法
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
    const container = normalizeContainer(containerOrSelector)
    if (!container) return

    const component = app._component
    if (!isFunction(component) && !component.render && !component.template) {
      // 在真实mount之前拿到template拿到template
      component.template = container.innerHTML
    }

    // clear content before mounting
    container.innerHTML = ''
    const proxy = mount(container, false, container instanceof SVGElement)
    if (container instanceof Element) {
      container.removeAttribute('v-cloak')
      container.setAttribute('data-v-app', '')
    }
    return proxy
  }

  return app
}) as CreateAppFunction<Element>

2、compileToFunction 编译template

// 这部分又可以拆分为三步:
export function baseCompile(
  template: string | RootNode,
  options: CompilerOptions = {}
): CodegenResult {
  // I:将template解析为抽象语法树
  const ast = isString(template) ? baseParse(template, options) : template
  const [nodeTransforms, directiveTransforms] =
    getBaseTransformPreset(prefixIdentifiers)
  //xxxx
  
  // II:对抽象语法树做深加工,方便各个平台打包成各自可用的版本
  transform(
    ast,
    extend({}, options, {
      prefixIdentifiers,
      nodeTransforms: [
        ...nodeTransforms,
        ...(options.nodeTransforms || []) // user transforms
      ],
      directiveTransforms: extend(
        {},
        directiveTransforms,
        options.directiveTransforms || {} // user transforms
      )
    })
  )
  return generate(
    ast,
    extend({}, options, {
      prefixIdentifiers
    })
  )
} 

// III:将抽象语法树转换为render函数
const { code } = compile(template, opts) // compile方法就是一个baseCompile的高阶
const render = (
    __GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom)
  ) as RenderFunction

那么是在什么编译的呢?? 回到第一节我们写的初始化流程,setupComponent部分 setupComponent->setupStatefulComponent->handleSetupResult->finishComponentSetup->!!!compile 以上流程拿到最终编译参数后,去生成render函数 Component.render = compile(template, finalCompilerOptions)

静态提升

  • 将静态节点缓存在render函数外面,之后不再重新创建
  • 补丁标记和动态属性记录
  • 缓存事件处理程序 onClick的属性被处理为箭头函数,缓存生成的箭头函数,之后渲染不会再重新创建箭头函数即不会再重新给属性复制。
  • 块block

这里可以运行vue项目的命令:npm run dev-compiler, 这里可以直观的看到抽象语法树的结构,和vue做的静态提升的节点。

patch流程

patchFlags、shapeFlags