Skip to content

Commit 5331df5

Browse files
author
Evan You
committed
clean up v-repeat buildItem()
1 parent 85496cf commit 5331df5

File tree

3 files changed

+139
-134
lines changed

3 files changed

+139
-134
lines changed

src/directives/if.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ module.exports = {
66
bind: function () {
77
this.parent = this.el.parentNode || this.el.vue_if_parent
88
this.ref = document.createComment(config.prefix + '-if-' + this.key)
9-
if (this.el.vue_if_ref) {
10-
this.parent.insertBefore(this.ref, this.el.vue_if_ref)
9+
var detachedRef = this.el.vue_if_ref
10+
if (detachedRef) {
11+
this.parent.insertBefore(this.ref, detachedRef)
1112
}
1213
this.el.vue_if_ref = this.ref
1314
},

src/directives/repeat.js

Lines changed: 134 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -92,35 +92,6 @@ var mutationHandlers = {
9292
}
9393
}
9494

95-
/**
96-
* Convert an Object to a v-repeat friendly Array
97-
*/
98-
function objectToArray (obj) {
99-
var res = [], val, data
100-
for (var key in obj) {
101-
val = obj[key]
102-
data = utils.typeOf(val) === 'Object'
103-
? val
104-
: { $value: val }
105-
def(data, '$key', key)
106-
res.push(data)
107-
}
108-
return res
109-
}
110-
111-
/**
112-
* Find an object or a wrapped data object
113-
* from an Array
114-
*/
115-
function indexOf (arr, obj) {
116-
for (var i = 0, l = arr.length; i < l; i++) {
117-
if (arr[i] === obj || (obj.$value && arr[i].$value === obj.$value)) {
118-
return i
119-
}
120-
}
121-
return -1
122-
}
123-
12495
module.exports = {
12596

12697
bind: function () {
@@ -178,8 +149,7 @@ module.exports = {
178149
// force a compile so that we get all the bindings for
179150
// dependency extraction.
180151
if (!this.initiated && (!collection || !collection.length)) {
181-
this.buildItem()
182-
this.initiated = true
152+
this.dryBuild()
183153
}
184154

185155
// keep reference of old data and VMs
@@ -205,17 +175,7 @@ module.exports = {
205175
}
206176

207177
// destroy unused old VMs
208-
if (oldVMs) {
209-
var i = oldVMs.length, vm
210-
while (i--) {
211-
vm = oldVMs[i]
212-
if (vm.$reused) {
213-
vm.$reused = false
214-
} else {
215-
vm.$destroy()
216-
}
217-
}
218-
}
178+
if (oldVMs) destroyVMs(oldVMs)
219179
this.old = this.oldVMs = null
220180
},
221181

@@ -236,6 +196,20 @@ module.exports = {
236196
})
237197
},
238198

199+
/**
200+
* Run a dry buildItem just to collect bindings
201+
*/
202+
dryBuild: function () {
203+
new this.Ctor({
204+
el: this.el.cloneNode(true),
205+
compilerOptions: {
206+
repeat: true,
207+
parentCompiler: this.compiler
208+
}
209+
}).$destroy()
210+
this.initiated = true
211+
},
212+
239213
/**
240214
* Create a new child VM from a data object
241215
* passing along compiler options indicating this
@@ -246,103 +220,92 @@ module.exports = {
246220
var ctn = this.container,
247221
vms = this.vms,
248222
col = this.collection,
249-
el, i, existing, ref, item, primitive, detached
223+
el, oldIndex, existing, item, nonObject
224+
225+
// get our DOM insertion reference node
226+
var ref = vms.length > index
227+
? vms[index].$el
228+
: this.ref
229+
230+
// if reference VM is detached by v-if,
231+
// use its v-if ref node instead
232+
if (!ref.parentNode) {
233+
ref = ref.vue_if_ref
234+
}
250235

251-
// append node into DOM first
252-
// so v-if can get access to parentNode
253-
// TODO: logic here is a total mess.
254-
if (data) {
236+
// check if data already exists in the old array
237+
oldIndex = this.old ? indexOf(this.old, data) : -1
238+
existing = oldIndex > -1
255239

256-
if (this.old) {
257-
i = indexOf(this.old, data)
258-
}
259-
existing = i > -1
260-
261-
if (existing) { // existing, reuse the old VM
262-
263-
item = this.oldVMs[i]
264-
// mark, so it won't be destroyed
265-
item.$reused = true
266-
el = item.$el
267-
// existing VM's el can possibly be detached by v-if.
268-
// in that case don't insert.
269-
detached = !el.parentNode
270-
271-
} else { // new data, need to create new VM
272-
273-
el = this.el.cloneNode(true)
274-
// process transition info before appending
275-
el.vue_trans = utils.attr(el, 'transition', true)
276-
el.vue_anim = utils.attr(el, 'animation', true)
277-
el.vue_effect = utils.attr(el, 'effect', true)
278-
// wrap primitive element in an object
279-
if (utils.typeOf(data) !== 'Object') {
280-
primitive = true
281-
data = { $value: data }
282-
}
240+
if (existing) {
283241

284-
}
242+
// existing, reuse the old VM
243+
item = this.oldVMs[oldIndex]
244+
// mark, so it won't be destroyed
245+
item.$reused = true
285246

286-
ref = vms.length > index
287-
? vms[index].$el
288-
: this.ref
289-
290-
// if ref VM's el is detached by v-if
291-
// use its v-if ref node instead
292-
if (!ref.parentNode) {
293-
ref = ref.vue_if_ref
294-
}
247+
} else {
295248

296-
if (existing) {
297-
// existing node
298-
// if not detached, just re-insert to new location
299-
// else re-insert its v-if ref node
300-
ctn.insertBefore(detached ? el.vue_if_ref : el, ref)
301-
} else {
302-
// new node, prepare it for v-if
303-
el.vue_if_parent = ctn
304-
el.vue_if_ref = ref
249+
// new data, need to create new VM.
250+
// there's some preparation work to do...
251+
252+
// first clone the template node
253+
el = this.el.cloneNode(true)
254+
// then we provide the parentNode for v-if
255+
// so that it can still work in a detached state
256+
el.vue_if_parent = ctn
257+
el.vue_if_ref = ref
258+
// wrap non-object value in an object
259+
nonObject = utils.typeOf(data) !== 'Object'
260+
if (nonObject) {
261+
data = { $value: data }
305262
}
306-
// set index so vm can init with it
307-
// and do not trigger stuff early
263+
// set index so vm can init with the correct
264+
// index instead of undefined
308265
data.$index = index
309-
}
310-
311-
item = item || new this.Ctor({
312-
el: el,
313-
data: data,
314-
compilerOptions: {
315-
repeat: true,
316-
parentCompiler: this.compiler
317-
}
318-
})
319-
item.$index = index
320-
321-
if (!data) {
322-
// this is a forced compile for an empty collection.
323-
// let's remove it...
324-
item.$destroy()
325-
} else {
326-
vms.splice(index, 0, item)
327-
328-
// for primitive values, listen for value change
329-
if (primitive) {
266+
// initialize the new VM
267+
item = new this.Ctor({
268+
el: el,
269+
data: data,
270+
compilerOptions: {
271+
repeat: true,
272+
parentCompiler: this.compiler
273+
}
274+
})
275+
// for non-object values, listen for value change
276+
// so we can sync it back to the original Array
277+
if (nonObject) {
330278
item.$compiler.observer.on('set', function (key, val) {
331279
if (key === '$value') {
332280
col[item.$index] = val
333281
}
334282
})
335283
}
336284

337-
// new instance and v-if doesn't want it detached
338-
// good to insert.
339-
if (!existing && el.vue_if !== false) {
285+
}
286+
287+
// put the item into the VM Array
288+
vms.splice(index, 0, item)
289+
// update the index
290+
item.$index = index
291+
292+
// Finally, DOM operations...
293+
el = item.$el
294+
if (existing) {
295+
// we simplify need to re-insert the existing node
296+
// to its new position. However, it can possibly be
297+
// detached by v-if. in that case we insert its v-if
298+
// ref node instead.
299+
ctn.insertBefore(el.parentNode ? el : el.vue_if_ref, ref)
300+
} else {
301+
if (el.vue_if !== false) {
340302
if (this.compiler.init) {
341-
// do not transition on initial compile.
342-
ctn.insertBefore(item.$el, ref)
303+
// do not transition on initial compile,
304+
// just manually insert.
305+
ctn.insertBefore(el, ref)
343306
item.$compiler.execHook('attached')
344307
} else {
345-
// transition in...
308+
// give it some nice transition.
346309
item.$before(ref)
347310
}
348311
}
@@ -401,8 +364,6 @@ module.exports = {
401364
var key = vm.$key,
402365
val = vm.$value || vm.$data
403366
if (action > 0) { // new property
404-
// make key ienumerable
405-
delete vm.$data.$key
406367
obj[key] = val
407368
Observer.convert(obj, key)
408369
} else {
@@ -412,22 +373,65 @@ module.exports = {
412373
}
413374
},
414375

415-
reset: function (destroyAll) {
376+
reset: function (destroy) {
416377
if (this.childId) {
417378
delete this.vm.$[this.childId]
418379
}
419380
if (this.collection) {
420381
this.collection.__emitter__.off('mutate', this.mutationListener)
421-
if (destroyAll) {
422-
var i = this.vms.length
423-
while (i--) {
424-
this.vms[i].$destroy()
425-
}
382+
if (destroy) {
383+
destroyVMs(this.vms)
426384
}
427385
}
428386
},
429387

430388
unbind: function () {
431389
this.reset(true)
432390
}
391+
}
392+
393+
// Helpers --------------------------------------------------------------------
394+
395+
/**
396+
* Convert an Object to a v-repeat friendly Array
397+
*/
398+
function objectToArray (obj) {
399+
var res = [], val, data
400+
for (var key in obj) {
401+
val = obj[key]
402+
data = utils.typeOf(val) === 'Object'
403+
? val
404+
: { $value: val }
405+
def(data, '$key', key)
406+
res.push(data)
407+
}
408+
return res
409+
}
410+
411+
/**
412+
* Find an object or a wrapped data object
413+
* from an Array
414+
*/
415+
function indexOf (arr, obj) {
416+
for (var i = 0, l = arr.length; i < l; i++) {
417+
if (arr[i] === obj || (obj.$value && arr[i].$value === obj.$value)) {
418+
return i
419+
}
420+
}
421+
return -1
422+
}
423+
424+
/**
425+
* Destroy some VMs, yeah.
426+
*/
427+
function destroyVMs (vms) {
428+
var i = vms.length, vm
429+
while (i--) {
430+
vm = vms[i]
431+
if (vm.$reused) {
432+
vm.$reused = false
433+
} else {
434+
vm.$destroy()
435+
}
436+
}
433437
}

src/utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ var utils = module.exports = {
2121
/**
2222
* get an attribute and remove it.
2323
*/
24-
attr: function (el, type, noRemove) {
24+
attr: function (el, type) {
2525
var attr = attrs[type],
2626
val = el.getAttribute(attr)
27-
if (!noRemove && val !== null) el.removeAttribute(attr)
27+
if (val !== null) el.removeAttribute(attr)
2828
return val
2929
},
3030

0 commit comments

Comments
 (0)