Skip to content

Commit 6a562c4

Browse files
committed
async components for v-repeat
1 parent da9eba3 commit 6a562c4

File tree

1 file changed

+95
-48
lines changed

1 file changed

+95
-48
lines changed

src/directives/repeat.js

Lines changed: 95 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ var transclude = require('../compiler/transclude')
99
var mergeOptions = require('../util/merge-option')
1010
var uid = 0
1111

12+
// async component resolution states
13+
var UNRESOLVED = 0
14+
var PENDING = 1
15+
var RESOLVED = 2
16+
var ABORTED = 3
17+
1218
module.exports = {
1319

1420
/**
@@ -85,6 +91,7 @@ module.exports = {
8591
*/
8692

8793
checkComponent: function () {
94+
this.componentState = UNRESOLVED
8895
var id = _.attr(this.el, 'component')
8996
var options = this.vm.$options
9097
if (!id) {
@@ -106,37 +113,105 @@ module.exports = {
106113
this.inlineTempalte = _.extractContent(this.el, true)
107114
}
108115
var tokens = textParser.parse(id)
109-
if (!tokens) { // static component
110-
var Ctor = this.Ctor = options.components[id]
111-
_.assertAsset(Ctor, 'component', id)
112-
var merged = mergeOptions(Ctor.options, {}, {
113-
$parent: this.vm
114-
})
115-
merged.template = this.inlineTempalte || merged.template
116-
merged._asComponent = true
117-
merged._parent = this.vm
118-
this.template = transclude(this.template, merged)
119-
// Important: mark the template as a root node so that
120-
// custom element components don't get compiled twice.
121-
// fixes #822
122-
this.template.__vue__ = true
123-
this._linkFn = compile(this.template, merged)
124-
} else {
125-
// to be resolved later
116+
if (tokens) {
117+
// dynamic component to be resolved later
126118
var ctorExp = textParser.tokensToExp(tokens)
127119
this.ctorGetter = expParser.parse(ctorExp).get
120+
} else {
121+
// static
122+
this.componentId = id
123+
this.pendingData = null
128124
}
129125
}
130126
},
131127

128+
resolveComponent: function () {
129+
this.componentState = PENDING
130+
this.vm._resolveComponent(this.componentId, _.bind(function (Ctor) {
131+
if (this.componentState === ABORTED) {
132+
return
133+
}
134+
this.Ctor = Ctor
135+
var merged = mergeOptions(Ctor.options, {}, {
136+
$parent: this.vm
137+
})
138+
merged.template = this.inlineTempalte || merged.template
139+
merged._asComponent = true
140+
merged._parent = this.vm
141+
this.template = transclude(this.template, merged)
142+
// Important: mark the template as a root node so that
143+
// custom element components don't get compiled twice.
144+
// fixes #822
145+
this.template.__vue__ = true
146+
this._linkFn = compile(this.template, merged)
147+
this.componentState = RESOLVED
148+
this.realUpdate(this.pendingData)
149+
this.pendingData = null
150+
}, this))
151+
},
152+
153+
/**
154+
* Resolve a dynamic component to use for an instance.
155+
* The tricky part here is that there could be dynamic
156+
* components depending on instance data.
157+
*
158+
* @param {Object} data
159+
* @param {Object} meta
160+
* @return {Function}
161+
*/
162+
163+
resolveDynamicComponent: function (data, meta) {
164+
// create a temporary context object and copy data
165+
// and meta properties onto it.
166+
// use _.define to avoid accidentally overwriting scope
167+
// properties.
168+
var context = Object.create(this.vm)
169+
var key
170+
for (key in data) {
171+
_.define(context, key, data[key])
172+
}
173+
for (key in meta) {
174+
_.define(context, key, meta[key])
175+
}
176+
var id = this.ctorGetter.call(context, context)
177+
var Ctor = this.vm.$options.components[id]
178+
_.assertAsset(Ctor, 'component', id)
179+
return Ctor
180+
},
181+
132182
/**
133183
* Update.
134-
* This is called whenever the Array mutates.
184+
* This is called whenever the Array mutates. If we have
185+
* a component, we might need to wait for it to resolve
186+
* asynchronously.
135187
*
136188
* @param {Array|Number|String} data
137189
*/
138190

139191
update: function (data) {
192+
if (this.componentId) {
193+
var state = this.componentState
194+
if (state === UNRESOLVED) {
195+
this.pendingData = data
196+
// once resolved, it will call realUpdate
197+
this.resolveComponent()
198+
} else if (state === PENDING) {
199+
this.pendingData = data
200+
} else if (state === RESOLVED) {
201+
this.realUpdate(data)
202+
}
203+
} else {
204+
this.realUpdate(data)
205+
}
206+
},
207+
208+
/**
209+
* The real update that actually modifies the DOM.
210+
*
211+
* @param {Array|Number|String} data
212+
*/
213+
214+
realUpdate: function (data) {
140215
data = data || []
141216
var type = typeof data
142217
if (type === 'number') {
@@ -292,7 +367,7 @@ module.exports = {
292367
data = raw
293368
}
294369
// resolve constructor
295-
var Ctor = this.Ctor || this.resolveCtor(data, meta)
370+
var Ctor = this.Ctor || this.resolveDynamicComponent(data, meta)
296371
var vm = this.vm.$addChild({
297372
el: templateParser.clone(this.template),
298373
_asComponent: this.asComponent,
@@ -323,40 +398,12 @@ module.exports = {
323398
return vm
324399
},
325400

326-
/**
327-
* Resolve a contructor to use for an instance.
328-
* The tricky part here is that there could be dynamic
329-
* components depending on instance data.
330-
*
331-
* @param {Object} data
332-
* @param {Object} meta
333-
* @return {Function}
334-
*/
335-
336-
resolveCtor: function (data, meta) {
337-
// create a temporary context object and copy data
338-
// and meta properties onto it.
339-
// use _.define to avoid accidentally overwriting scope
340-
// properties.
341-
var context = Object.create(this.vm)
342-
var key
343-
for (key in data) {
344-
_.define(context, key, data[key])
345-
}
346-
for (key in meta) {
347-
_.define(context, key, meta[key])
348-
}
349-
var id = this.ctorGetter.call(context, context)
350-
var Ctor = this.vm.$options.components[id]
351-
_.assertAsset(Ctor, 'component', id)
352-
return Ctor
353-
},
354-
355401
/**
356402
* Unbind, teardown everything
357403
*/
358404

359405
unbind: function () {
406+
this.componentState = ABORTED
360407
if (this.refID) {
361408
this.vm.$[this.refID] = null
362409
}

0 commit comments

Comments
 (0)