@@ -9,6 +9,12 @@ var transclude = require('../compiler/transclude')
99var mergeOptions = require ( '../util/merge-option' )
1010var 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+
1218module . 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