Просмотр исходного кода

perf(sfc): lazy initiatate hoisted vnodes

Anthony Fu 2 лет назад
Родитель
Сommit
e158025904

+ 5 - 2
packages/compiler-core/src/codegen.ts

@@ -42,6 +42,7 @@ import {
   CREATE_STATIC,
   CREATE_TEXT,
   CREATE_VNODE,
+  HOIST_LAZY,
   OPEN_BLOCK,
   POP_SCOPE_ID,
   PUSH_SCOPE_ID,
@@ -476,6 +477,7 @@ function genModulePreamble(
   if (genScopeId && ast.hoists.length) {
     ast.helpers.add(PUSH_SCOPE_ID)
     ast.helpers.add(POP_SCOPE_ID)
+    ast.helpers.add(HOIST_LAZY)
   }
 
   // generate import statements for helpers
@@ -585,14 +587,15 @@ function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
     if (exp) {
       const needScopeIdWrapper = genScopeId && exp.type === NodeTypes.VNODE_CALL
       push(
-        `const _hoisted_${i + 1} = ${
-          needScopeIdWrapper ? `${PURE_ANNOTATION} _withScopeId(() => ` : ``
+        `const _hoisted_${i + 1} = ${PURE_ANNOTATION} ${helper(HOIST_LAZY)}(() => (${
+          needScopeIdWrapper ? `_withScopeId(() => ` : ``
         }`,
       )
       genNode(exp, context)
       if (needScopeIdWrapper) {
         push(`)`)
       }
+      push('))')
       newline()
     }
   }

+ 2 - 0
packages/compiler-core/src/runtimeHelpers.ts

@@ -9,6 +9,7 @@ export const CREATE_ELEMENT_BLOCK = Symbol(__DEV__ ? `createElementBlock` : ``)
 export const CREATE_VNODE = Symbol(__DEV__ ? `createVNode` : ``)
 export const CREATE_ELEMENT_VNODE = Symbol(__DEV__ ? `createElementVNode` : ``)
 export const CREATE_COMMENT = Symbol(__DEV__ ? `createCommentVNode` : ``)
+export const HOIST_LAZY = Symbol(__DEV__ ? `hoistLazy` : ``)
 export const CREATE_TEXT = Symbol(__DEV__ ? `createTextVNode` : ``)
 export const CREATE_STATIC = Symbol(__DEV__ ? `createStaticVNode` : ``)
 export const RESOLVE_COMPONENT = Symbol(__DEV__ ? `resolveComponent` : ``)
@@ -79,6 +80,7 @@ export const helperNameMap: Record<symbol, string> = {
   [POP_SCOPE_ID]: `popScopeId`,
   [WITH_CTX]: `withCtx`,
   [UNREF]: `unref`,
+  [HOIST_LAZY]: `hoistLazy`,
   [IS_REF]: `isRef`,
   [WITH_MEMO]: `withMemo`,
   [IS_MEMO_SAME]: `isMemoSame`,

+ 1 - 1
packages/compiler-core/src/transform.ts

@@ -288,7 +288,7 @@ export function createTransformContext(
       if (isString(exp)) exp = createSimpleExpression(exp)
       context.hoists.push(exp)
       const identifier = createSimpleExpression(
-        `_hoisted_${context.hoists.length}`,
+        `_hoisted_${context.hoists.length}()`,
         false,
         exp.loc,
         ConstantTypes.CAN_HOIST,

+ 2 - 0
packages/compiler-core/src/transforms/hoistStatic.ts

@@ -21,6 +21,7 @@ import { PatchFlags, isArray, isString, isSymbol } from '@vue/shared'
 import { isSlotOutlet } from '../utils'
 import {
   GUARD_REACTIVE_PROPS,
+  HOIST_LAZY,
   NORMALIZE_CLASS,
   NORMALIZE_PROPS,
   NORMALIZE_STYLE,
@@ -70,6 +71,7 @@ function walk(
         : getConstantType(child, context)
       if (constantType > ConstantTypes.NOT_CONSTANT) {
         if (constantType >= ConstantTypes.CAN_HOIST) {
+          context.helper(HOIST_LAZY)
           ;(child.codegenNode as VNodeCall).patchFlag =
             PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
           child.codegenNode = context.hoist(child.codegenNode!)

+ 7 - 1
packages/runtime-core/src/index.ts

@@ -97,7 +97,13 @@ export { getCurrentInstance } from './component'
 // For raw render function users
 export { h } from './h'
 // Advanced render function utilities
-export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode'
+export {
+  createVNode,
+  cloneVNode,
+  mergeProps,
+  isVNode,
+  hoistLazy,
+} from './vnode'
 // VNode types
 export { Fragment, Text, Comment, Static, type VNodeRef } from './vnode'
 // Built-in components

+ 10 - 0
packages/runtime-core/src/vnode.ts

@@ -875,3 +875,13 @@ export function invokeVNodeHook(
     prevVNode,
   ])
 }
+
+export function hoistLazy<T>(fn: () => T): () => T {
+  let cache: T | undefined
+  return (): T => {
+    if (!cache) {
+      cache = fn()
+    }
+    return cache
+  }
+}