Explorar el Código

improve keep-alive: use component-based API

Evan You hace 9 años
padre
commit
cdc0bc9cd4

+ 0 - 1
flow/compiler.js

@@ -82,7 +82,6 @@ declare type ASTElement = {
   ns?: string;
 
   component?: string;
-  keepAlive?: boolean;
   inlineTemplate?: true;
   transitionMode?: string | null;
   slotName?: ?string;

+ 0 - 4
src/compiler/codegen/index.js

@@ -67,10 +67,6 @@ function genElement (el: ASTElement): string {
     for (let i = 0; i < transforms.length; i++) {
       code = transforms[i](el, code)
     }
-    // check keep-alive
-    if (el.keepAlive) {
-      code = `_h("KeepAlive",{props:{child:${code}}})`
-    }
     return code
   }
 }

+ 0 - 3
src/compiler/parser/index.js

@@ -345,9 +345,6 @@ function processComponent (el) {
   if ((binding = getBindingAttr(el, 'is'))) {
     el.component = binding
   }
-  if (getAndRemoveAttr(el, 'keep-alive') != null) {
-    el.keepAlive = true
-  }
   if (getAndRemoveAttr(el, 'inline-template') != null) {
     el.inlineTemplate = true
   }

+ 14 - 16
src/core/components/keep-alive.js

@@ -1,32 +1,30 @@
 import { callHook } from 'core/instance/lifecycle'
-import { getRealChild } from 'core/vdom/helpers'
+import { getFirstComponentChild } from 'core/vdom/helpers'
 
 export default {
   name: 'keep-alive',
   abstract: true,
-  props: {
-    child: Object
-  },
   created () {
     this.cache = Object.create(null)
   },
   render () {
-    const rawChild = this.child
-    const realChild = getRealChild(this.child)
-    if (realChild && realChild.componentOptions) {
-      const opts = realChild.componentOptions
-      // same constructor may get registered as different local components
-      // so cid alone is not enough (#3269)
-      const key = opts.Ctor.cid + '::' + opts.tag
+    const vnode = getFirstComponentChild(this.$slots.default)
+    if (vnode && vnode.componentOptions) {
+      const opts = vnode.componentOptions
+      const key = vnode.key == null
+        // same constructor may get registered as different local components
+        // so cid alone is not enough (#3269)
+        ? opts.Ctor.cid + '::' + opts.tag
+        : vnode.key
       if (this.cache[key]) {
-        const child = realChild.child = this.cache[key].child
-        realChild.elm = this.$el = child.$el
+        const child = vnode.child = this.cache[key].child
+        vnode.elm = this.$el = child.$el
       } else {
-        this.cache[key] = realChild
+        this.cache[key] = vnode
       }
-      realChild.data.keepAlive = true
+      vnode.data.keepAlive = true
     }
-    return rawChild
+    return vnode
   },
   destroyed () {
     for (const key in this.cache) {

+ 2 - 9
src/core/vdom/helpers.js

@@ -56,15 +56,8 @@ function applyNS (vnode, ns) {
   }
 }
 
-// in case the child is also an abstract component, e.g. <transition-control>
-// we want to recrusively retrieve the real component to be rendered
-export function getRealChild (vnode: ?VNode): ?VNode {
-  const compOptions = vnode && vnode.componentOptions
-  if (compOptions && compOptions.Ctor.options.abstract) {
-    return getRealChild(compOptions.propsData && compOptions.propsData.child)
-  } else {
-    return vnode
-  }
+export function getFirstComponentChild (children: ?Array<any>) {
+  return children && children.filter(c => c && c.componentOptions)[0]
 }
 
 export function mergeVNodeHook (def: Object, key: string, hook: Function) {

+ 19 - 3
src/platforms/web/runtime/components/transition.js

@@ -5,7 +5,7 @@
 
 import { warn } from 'core/util/index'
 import { camelize, extend } from 'shared/util'
-import { getRealChild, mergeVNodeHook } from 'core/vdom/helpers'
+import { mergeVNodeHook, getFirstComponentChild } from 'core/vdom/helpers'
 
 export const transitionProps = {
   name: String,
@@ -21,6 +21,17 @@ export const transitionProps = {
   appearActiveClass: String
 }
 
+// in case the child is also an abstract component, e.g. <keep-alive>
+// we want to recrusively retrieve the real component to be rendered
+function getRealChild (vnode: ?VNode): ?VNode {
+  const compOptions = vnode && vnode.componentOptions
+  if (compOptions && compOptions.Ctor.options.abstract) {
+    return getRealChild(getFirstComponentChild(compOptions.children))
+  } else {
+    return vnode
+  }
+}
+
 export function extractTransitionData (comp: Component): Object {
   const data = {}
   const options = comp.$options
@@ -86,8 +97,13 @@ export default {
     // use getRealChild() to ignore abstract components e.g. keep-alive
     const child = getRealChild(rawChild)
     /* istanbul ignore if */
-    if (!child) return
-    child.key = child.key || `__v${child.tag + this._uid}__`
+    if (!child) {
+      return rawChild
+    }
+
+    child.key = child.key == null
+      ? `__v${child.tag + this._uid}__`
+      : child.key
     const data = (child.data || (child.data = {})).transition = extractTransitionData(this)
     const oldRawChild = this._vnode
     const oldChild: any = getRealChild(oldRawChild)

+ 16 - 12
test/unit/features/component/component-keep-alive.spec.js

@@ -43,7 +43,13 @@ describe('Component keep-alive', () => {
 
   it('should work', done => {
     const vm = new Vue({
-      template: '<div v-if="ok"><component :is="view" keep-alive></component></div>',
+      template: `
+        <div v-if="ok">
+          <keep-alive>
+            <component :is="view"></component>
+          </keep-alive>
+        </div>
+      `,
       data: {
         view: 'one',
         ok: true
@@ -82,11 +88,9 @@ describe('Component keep-alive', () => {
       const vm = new Vue({
         template: `<div>
           <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <component
-              :is="view"
-              class="test"
-              keep-alive>
-            </component>
+            <keep-alive>
+              <component :is="view" class="test"></component>
+            </keep-alive>
           <transition>
         </div>`,
         data: {
@@ -169,11 +173,9 @@ describe('Component keep-alive', () => {
       const vm = new Vue({
         template: `<div>
           <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <component
-              :is="view"
-              class="test"
-              keep-alive>
-            </component>
+            <keep-alive>
+              <component :is="view" class="test"></component>
+            </keep-alive>
           </transition>
         </div>`,
         data: {
@@ -266,7 +268,9 @@ describe('Component keep-alive', () => {
       const vm = new Vue({
         template: `<div>
           <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <component :is="view" class="test" keep-alive></component>
+            <keep-alive>
+              <component :is="view" class="test"></component>
+            </keep-alive>
           </transition>
         </div>`,
         data: { view: 'one' },