Evan You 10 лет назад
Родитель
Сommit
7a3261115f

+ 5 - 2
flow/compiler.js

@@ -15,8 +15,9 @@ declare type CompilerOptions = {
 
 declare type ModuleOptions = {
   staticKeys?: Array<string>,
-  parse: Function,
-  genData: Function
+  parse: (el: ASTElement) => void,
+  genData: (el: ASTElement) => string,
+  transformElement?: (el: ASTElement, code: string) => string
 }
 
 declare type ASTElementHandler = {
@@ -59,7 +60,9 @@ declare type ASTElement = {
   ns?: string,
 
   component?: string,
+  keepAlive?: boolean,
   inlineTemplate?: true,
+  transitionMode?: string | null,
   slotName?: ?string,
   slotTarget?: ?string,
 

+ 30 - 20
src/compiler/codegen.js

@@ -47,29 +47,39 @@ function genElement (el: ASTElement): string {
     return genRender(el)
   } else if (el.tag === 'slot') {
     return genSlot(el)
-  } else if (el.component) {
-    return genComponent(el)
   } else {
-    const data = genData(el)
-    // if the element is potentially a component,
-    // wrap its children as a thunk.
-    const children = !el.inlineTemplate
-      ? genChildren(el, !el.ns && !isPlatformReservedTag(el.tag) /* asThunk */)
-      : null
-    const code = `_h(_e('${el.tag}'${
-      data ? `,${data}` : el.ns ? ',void 0' : '' // data
-    }${
-      el.ns ? `,'${el.ns}'` : '' // namespace
-    })${
-      children ? `,${children}` : '' // children
-    })`
-    if (el.staticRoot) {
-      // hoist static sub-trees out
-      staticRenderFns.push(`with(this){return ${code}}`)
-      return `_m(${staticRenderFns.length - 1})`
+    // component or element
+    let code
+    if (el.component) {
+      code = genComponent(el)
     } else {
-      return code
+      const data = genData(el)
+      // if the element is potentially a component,
+      // wrap its children as a thunk.
+      const children = !el.inlineTemplate
+        ? genChildren(el, !el.ns && !isPlatformReservedTag(el.tag) /* asThunk */)
+        : null
+      code = `_h(_e('${el.tag}'${
+        data ? `,${data}` : el.ns ? ',void 0' : '' // data
+      }${
+        el.ns ? `,'${el.ns}'` : '' // namespace
+      })${
+        children ? `,${children}` : '' // children
+      })`
+      if (el.staticRoot) {
+        // hoist static sub-trees out
+        staticRenderFns.push(`with(this){return ${code}}`)
+        code = `_m(${staticRenderFns.length - 1})`
+      }
+    }
+    // platform modules
+    for (let i = 0; i < platformModules.length; i++) {
+      const transform = platformModules[i].transformElement
+      if (transform) {
+        code = transform(el, code)
+      }
     }
+    return code
   }
 }
 

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

@@ -313,9 +313,12 @@ function processSlot (el) {
 }
 
 function processComponent (el) {
-  const isBinding = getBindingAttr(el, 'is')
-  if (isBinding) {
-    el.component = isBinding
+  let binding
+  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

+ 5 - 3
src/entries/web-runtime.js

@@ -2,9 +2,10 @@
 
 import Vue from 'core/index'
 import config from 'core/config'
-import { noop } from 'shared/util'
+import { extend, noop } from 'shared/util'
 import { patch } from 'web/runtime/patch'
 import platformDirectives from 'web/runtime/directives/index'
+import platformComponents from 'web/runtime/components/index'
 import { query, isUnknownElement, isReservedTag, mustUseProp } from 'web/util/index'
 
 // install platform specific utils
@@ -12,8 +13,9 @@ Vue.config.isUnknownElement = isUnknownElement
 Vue.config.isReservedTag = isReservedTag
 Vue.config.mustUseProp = mustUseProp
 
-// install platform runtime directives
-Vue.options.directives = platformDirectives
+// install platform runtime directives & components
+extend(Vue.options.directives, platformDirectives)
+extend(Vue.options.components, platformComponents)
 
 // install platform patch function
 Vue.prototype.__patch__ = config._isServer ? noop : patch

+ 14 - 1
src/platforms/web/compiler/modules/transition.js

@@ -12,6 +12,10 @@ function parse (el: ASTElement) {
   if (transition) {
     el.transition = transition
   }
+  const mode = getBindingAttr(el, 'transition-mode')
+  if (mode) {
+    el.transitionMode = mode
+  }
 }
 
 function genData (el: ASTElement): string {
@@ -20,7 +24,16 @@ function genData (el: ASTElement): string {
     : ''
 }
 
+function transformElement (el: ASTElement, code: string): string {
+  return el.transitionMode
+    ? `_h(_e('transition-control',{props:{mode:${
+        el.transitionMode
+      }}}),function(){return [${code}]})`
+    : code
+}
+
 export default {
   parse,
-  genData
+  genData,
+  transformElement
 }

+ 5 - 0
src/platforms/web/runtime/components/index.js

@@ -0,0 +1,5 @@
+import transitionControl from './transition-control'
+
+export default {
+  transitionControl
+}

+ 66 - 0
src/platforms/web/runtime/components/transition-control.js

@@ -0,0 +1,66 @@
+/* flow */
+
+import { warn } from 'core/util/index'
+
+export default {
+  props: {
+    mode: {
+      validator (val) {
+        if (val && val !== 'out-in' && val !== 'in-out') {
+          warn('transition-mode must be either "out-in" or "in-out".')
+          return false
+        }
+        return true
+      }
+    }
+  },
+  render () {
+    const oldChild = this._vnode
+    const newChild = this.$slots.default[0]
+    if (oldChild && oldChild.data && oldChild.tag !== newChild.tag) {
+      if (this.mode === 'out-in') {
+        // return empty node
+        // and queue an update when the leave finishes
+        addHook(oldChild, 'afterLeave', () => {
+          this.$forceUpdate()
+        })
+        return
+      } else {
+        if (this.mode === 'in-out') {
+          let delayedLeave
+          const performLeave = () => { delayedLeave() }
+          addHook(newChild, 'afterEnter', performLeave)
+          addHook(newChild, 'enterCancelled', performLeave)
+          addHook(oldChild, 'delayLeave', leave => {
+            delayedLeave = leave
+          })
+        }
+        return newChild
+      }
+    } else {
+      return newChild
+    }
+  }
+}
+
+function addHook (vnode: VNode, name: string, hook: Function) {
+  if (!vnode.data || !vnode.data.transition) {
+    return
+  }
+  let trans = vnode.data.transition
+  if (typeof trans === 'string') {
+    trans = vnode.data.transition = { name: trans }
+  } else if (typeof trans !== 'object') {
+    trans = vnode.data.transition = { name: 'v' }
+  }
+  if (trans[name]) {
+    const oldHook = trans[name]
+    trans[name] = function (el) {
+      const res = oldHook.apply(this, arguments)
+      hook()
+      return res
+    }
+  } else {
+    trans[name] = hook
+  }
+}

+ 25 - 16
src/platforms/web/runtime/modules/transition.js

@@ -129,7 +129,8 @@ export function leave (vnode: VNodeWithData, rm: Function) {
     beforeLeave,
     onLeave,
     afterLeave,
-    leaveCancelled
+    leaveCancelled,
+    delayLeave
   } = resolveTransition(data, vnode.context)
 
   const expectsCSS = css !== false
@@ -150,22 +151,30 @@ export function leave (vnode: VNodeWithData, rm: Function) {
     el._leaveCb = null
   })
 
-  beforeLeave && beforeLeave(el)
-  if (expectsCSS) {
-    addTransitionClass(el, leaveClass)
-    nextFrame(() => {
-      removeTransitionClass(el, leaveClass)
-      if (!cb.cancelled) {
-        addTransitionClass(el, leaveActiveClass)
-        if (!userWantsControl) {
-          whenTransitionEnds(el, cb)
-        }
-      }
-    })
+  if (delayLeave) {
+    delayLeave(performLeave)
+  } else {
+    performLeave()
   }
-  onLeave && onLeave(el, cb)
-  if (!expectsCSS && !userWantsControl) {
-    cb()
+
+  function performLeave () {
+    beforeLeave && beforeLeave(el)
+    if (expectsCSS) {
+      addTransitionClass(el, leaveClass)
+      nextFrame(() => {
+        removeTransitionClass(el, leaveClass)
+        if (!cb.cancelled) {
+          addTransitionClass(el, leaveActiveClass)
+          if (!userWantsControl) {
+            whenTransitionEnds(el, cb)
+          }
+        }
+      })
+    }
+    onLeave && onLeave(el, cb)
+    if (!expectsCSS && !userWantsControl) {
+      cb()
+    }
   }
 }