Ver Fonte

fix(vue): properly cache runtime compilation

Evan You há 6 anos atrás
pai
commit
d3d4fe84cd

+ 6 - 1
packages/compiler-sfc/src/index.ts

@@ -12,5 +12,10 @@ export {
   SFCScriptBlock,
   SFCStyleBlock
 } from './parse'
-
+export {
+  TemplateCompiler,
+  TemplateCompileOptions,
+  TemplateCompileResults
+} from './compileTemplate'
 export { StyleCompileOptions, StyleCompileResults } from './compileStyle'
+export { CompilerOptions } from '@vue/compiler-core'

+ 1 - 1
packages/runtime-core/src/apiOptions.ts

@@ -55,7 +55,7 @@ export interface ComponentOptionsBase<
     ctx: SetupContext
   ) => RawBindings | RenderFunction | void
   name?: string
-  template?: string
+  template?: string | object // can be a direct DOM node
   // Note: we are intentionally using the signature-less `Function` type here
   // since any type with signature will cause the whole inference to fail when
   // the return expression contains reference to `this`.

+ 38 - 1
packages/vue/__tests__/index.spec.ts

@@ -4,7 +4,7 @@ import { mockWarn } from '@vue/runtime-test'
 describe('compiler + runtime integration', () => {
   mockWarn()
 
-  it('should support on-the-fly template compilation', () => {
+  it('should support runtime template compilation', () => {
     const container = document.createElement('div')
     const App = {
       template: `{{ count }}`,
@@ -18,6 +18,43 @@ describe('compiler + runtime integration', () => {
     expect(container.innerHTML).toBe(`0`)
   })
 
+  it('should support runtime template via CSS ID selector', () => {
+    const container = document.createElement('div')
+    const template = document.createElement('div')
+    template.id = 'template'
+    template.innerHTML = '{{ count }}'
+    document.body.appendChild(template)
+
+    const App = {
+      template: `#template`,
+      data() {
+        return {
+          count: 0
+        }
+      }
+    }
+    createApp().mount(App, container)
+    expect(container.innerHTML).toBe(`0`)
+  })
+
+  it('should support runtime template via direct DOM node', () => {
+    const container = document.createElement('div')
+    const template = document.createElement('div')
+    template.id = 'template'
+    template.innerHTML = '{{ count }}'
+
+    const App = {
+      template,
+      data() {
+        return {
+          count: 0
+        }
+      }
+    }
+    createApp().mount(App, container)
+    expect(container.innerHTML).toBe(`0`)
+  })
+
   it('should warn template compilation errors with codeframe', () => {
     const container = document.createElement('div')
     const App = {

+ 23 - 19
packages/vue/src/index.ts

@@ -5,32 +5,36 @@ import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
 import * as runtimeDom from '@vue/runtime-dom'
 import { isString, NOOP } from '@vue/shared'
 
-const idToTemplateCache = Object.create(null)
+const compileCache: Record<string, RenderFunction> = Object.create(null)
 
 function compileToFunction(
   template: string | HTMLElement,
   options?: CompilerOptions
 ): RenderFunction {
-  if (isString(template)) {
-    if (template[0] === '#') {
-      if (template in idToTemplateCache) {
-        template = idToTemplateCache[template]
-      } else {
-        const el = document.querySelector(template)
-        if (__DEV__ && !el) {
-          warn(`Template element not found or is empty: ${template}`)
-        }
-        template = idToTemplateCache[template] = el ? el.innerHTML : ``
-      }
+  if (!isString(template)) {
+    if (template.nodeType) {
+      template = template.innerHTML
+    } else {
+      __DEV__ && warn(`invalid template option: `, template)
+      return NOOP
     }
-  } else if (template.nodeType) {
-    template = template.innerHTML
-  } else {
-    __DEV__ && warn(`invalid template option: `, template)
-    return NOOP
   }
 
-  const { code } = compile(template as string, {
+  const key = template
+  const cached = compileCache[key]
+  if (cached) {
+    return cached
+  }
+
+  if (template[0] === '#') {
+    const el = document.querySelector(template)
+    if (__DEV__ && !el) {
+      warn(`Template element not found or is empty: ${template}`)
+    }
+    template = el ? el.innerHTML : ``
+  }
+
+  const { code } = compile(template, {
     hoistStatic: true,
     cacheHandlers: true,
     ...options
@@ -38,7 +42,7 @@ function compileToFunction(
 
   const render = new Function('Vue', code)(runtimeDom) as RenderFunction
   render.isRuntimeCompiled = true
-  return render
+  return (compileCache[key] = render)
 }
 
 registerRuntimeCompiler(compileToFunction)