|
|
@@ -1,27 +1,26 @@
|
|
|
var _ = require('../util')
|
|
|
var Watcher = require('../watcher')
|
|
|
|
|
|
-/**
|
|
|
- * Possible permutations:
|
|
|
- *
|
|
|
- * - literal:
|
|
|
- * v-component="comp"
|
|
|
- *
|
|
|
- * - dynamic:
|
|
|
- * v-component="{{currentView}}"
|
|
|
- *
|
|
|
- * - conditional:
|
|
|
- * v-component="comp" v-if="abc"
|
|
|
- *
|
|
|
- * - dynamic + conditional:
|
|
|
- * v-component="{{currentView}}" v-if="abc"
|
|
|
- *
|
|
|
- */
|
|
|
-
|
|
|
module.exports = {
|
|
|
|
|
|
isLiteral: true,
|
|
|
|
|
|
+ /**
|
|
|
+ * Setup. Need to check a few possible permutations:
|
|
|
+ *
|
|
|
+ * - literal:
|
|
|
+ * v-component="comp"
|
|
|
+ *
|
|
|
+ * - dynamic:
|
|
|
+ * v-component="{{currentView}}"
|
|
|
+ *
|
|
|
+ * - conditional:
|
|
|
+ * v-component="comp" v-if="abc"
|
|
|
+ *
|
|
|
+ * - dynamic + conditional:
|
|
|
+ * v-component="{{currentView}}" v-if="abc"
|
|
|
+ */
|
|
|
+
|
|
|
bind: function () {
|
|
|
if (!this.el.__vue__) {
|
|
|
// create a ref anchor
|
|
|
@@ -30,6 +29,8 @@ module.exports = {
|
|
|
_.remove(this.el)
|
|
|
// check v-if conditionals
|
|
|
this.checkIf()
|
|
|
+ // check keep-alive options
|
|
|
+ this.checkKeepAlive()
|
|
|
// if static, build right now.
|
|
|
if (!this._isDynamicLiteral) {
|
|
|
this.resolveCtor(this.expression)
|
|
|
@@ -43,6 +44,12 @@ module.exports = {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Check if v-component is being used together with v-if.
|
|
|
+ * If yes, we created a watcher for the v-if value and
|
|
|
+ * react to its value change in `this.ifCallback`.
|
|
|
+ */
|
|
|
+
|
|
|
checkIf: function () {
|
|
|
var condition = _.attr(this.el, 'if')
|
|
|
if (condition !== null) {
|
|
|
@@ -58,6 +65,13 @@ module.exports = {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Callback when v-if value changes.
|
|
|
+ * Marks the active flag.
|
|
|
+ *
|
|
|
+ * @param {*} value
|
|
|
+ */
|
|
|
+
|
|
|
ifCallback: function (value) {
|
|
|
if (value) {
|
|
|
this.active = true
|
|
|
@@ -68,31 +82,95 @@ module.exports = {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Check if the "keep-alive" flag is present.
|
|
|
+ * If yes, instead of destroying the active vm when
|
|
|
+ * hiding (v-if) or switching (dynamic literal) it,
|
|
|
+ * we simply remove it from the DOM and save it in a
|
|
|
+ * cache object, with its constructor id as the key.
|
|
|
+ */
|
|
|
+
|
|
|
+ checkKeepAlive: function () {
|
|
|
+ // check keep-alive flag
|
|
|
+ this.keepAlive = this.el.hasAttribute('keep-alive')
|
|
|
+ if (this.keepAlive) {
|
|
|
+ this.cache = Object.create(null)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Resolve the component constructor to use when creating
|
|
|
+ * the child vm. If the component id is empty string, the
|
|
|
+ * default 'Vue' constructor will be used.
|
|
|
+ */
|
|
|
+
|
|
|
resolveCtor: function (id) {
|
|
|
- var registry = this.vm.$options.components
|
|
|
- this.Ctor = registry[id]
|
|
|
- if (!this.Ctor) {
|
|
|
- _.warn('Failed to resolve component: ' + id)
|
|
|
+ if (id === '') {
|
|
|
+ this.id = '__vue__'
|
|
|
+ this.Ctor = _.Vue
|
|
|
+ } else {
|
|
|
+ this.id = id
|
|
|
+ this.Ctor = this.vm.$options.components[id]
|
|
|
+ if (!this.Ctor) {
|
|
|
+ _.warn('Failed to resolve component: ' + id)
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Instantiate/insert a new child vm.
|
|
|
+ * If keep alive and has cached instance, insert that
|
|
|
+ * instance; otherwise build a new one and cache it.
|
|
|
+ */
|
|
|
+
|
|
|
build: function () {
|
|
|
- if (this.active && this.Ctor && !this.childVM) {
|
|
|
+ if (!this.active) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.keepAlive) {
|
|
|
+ var vm = this.cache[this.id]
|
|
|
+ if (vm) {
|
|
|
+ this.childVM = vm
|
|
|
+ vm.$before(this.ref)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (this.Ctor && !this.childVM) {
|
|
|
this.childVM = new this.Ctor({
|
|
|
el: this.el.cloneNode(true),
|
|
|
parent: this.vm
|
|
|
})
|
|
|
+ this.cache[this.id] = this.childVM
|
|
|
this.childVM.$before(this.ref)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Teardown the active vm.
|
|
|
+ * If keep alive, simply remove it; otherwise destroy it.
|
|
|
+ *
|
|
|
+ * @param {Boolean} remove
|
|
|
+ */
|
|
|
+
|
|
|
unbuild: function (remove) {
|
|
|
- if (this.childVM) {
|
|
|
+ if (!this.childVM) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.keepAlive) {
|
|
|
+ if (remove) {
|
|
|
+ this.childVM.$remove()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
this.childVM.$destroy(remove)
|
|
|
- this.childVM = null
|
|
|
}
|
|
|
+ this.childVM = null
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Update callback for the dynamic literal scenario,
|
|
|
+ * e.g. v-component="{{view}}"
|
|
|
+ */
|
|
|
+
|
|
|
update: function (value) {
|
|
|
this.unbuild(true)
|
|
|
if (value) {
|
|
|
@@ -101,7 +179,15 @@ module.exports = {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Unbind.
|
|
|
+ * Make sure keepAlive is set to false so that the
|
|
|
+ * instance is always destroyed. Teardown v-if watcher
|
|
|
+ * if present.
|
|
|
+ */
|
|
|
+
|
|
|
unbind: function () {
|
|
|
+ this.keepAlive = false
|
|
|
this.unbuild()
|
|
|
if (this.ifWatcher) {
|
|
|
this.ifWatcher.teardown()
|