Skip to content

Commit efedd6e

Browse files
committed
improve v-repeat strategy check (fix #802)
1 parent 563b0b9 commit efedd6e

File tree

3 files changed

+82
-27
lines changed

3 files changed

+82
-27
lines changed

src/directives/repeat.js

+39-27
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ module.exports = {
3939
this.template = this.el.tagName === 'TEMPLATE'
4040
? templateParser.parse(this.el, true)
4141
: this.el
42+
// check if we need to use diff instead of inplace
43+
// updates
44+
this.checkUpdateStrategy()
4245
// check other directives that need to be handled
4346
// at v-repeat level
4447
this.checkIf()
@@ -49,7 +52,38 @@ module.exports = {
4952
this._checkParam('track-by') ||
5053
this._checkParam('trackby') // 0.11.0 compat
5154
this.cache = Object.create(null)
52-
this.checkUpdateStrategy()
55+
},
56+
57+
/**
58+
* Check what strategy to use for updates.
59+
*
60+
* If the repeat is simple enough we can use in-place
61+
* updates which simply overwrites existing instances'
62+
* data. This strategy reuses DOM nodes and instances
63+
* as much as possible.
64+
*
65+
* There are two situations where we have to use the
66+
* more complex but more accurate diff algorithm:
67+
* 1. We are using components with or inside v-repeat.
68+
* The components could have private state that needs
69+
* to be preserved across updates.
70+
* 2. We have transitions on the list, which requires
71+
* precise DOM re-positioning.
72+
*/
73+
74+
checkUpdateStrategy: function () {
75+
var components = Object.keys(this.vm.$options.components)
76+
var matcher
77+
if (components.length) {
78+
matcher = new RegExp(
79+
components.map(function (name) {
80+
return '<' + name + '(>|\\s)'
81+
}).join('|') + '|' + config.prefix + 'component'
82+
)
83+
}
84+
this.needDiff =
85+
(matcher && matcher.test(this.template.outerHTML)) ||
86+
this.el.hasAttribute(config.prefix + 'transition')
5387
},
5488

5589
/**
@@ -90,8 +124,10 @@ module.exports = {
90124
var id = _.attr(this.el, 'component')
91125
var options = this.vm.$options
92126
if (!id) {
93-
this.Ctor = _.Vue // default constructor
94-
this.inherit = true // inline repeats should inherit
127+
// default constructor
128+
this.Ctor = _.Vue
129+
// inline repeats should inherit
130+
this.inherit = true
95131
// important: transclude with no options, just
96132
// to ensure block start and block end
97133
this.template = transclude(this.template)
@@ -130,30 +166,6 @@ module.exports = {
130166
}
131167
},
132168

133-
/**
134-
* Check what strategy to use for updates.
135-
*
136-
* If the repeat is simple enough we can use in-place
137-
* updates which simply overwrites existing instances'
138-
* data. This strategy reuses DOM nodes and instances
139-
* as much as possible.
140-
*
141-
* There are two situations where we have to use the
142-
* more complex but more accurate diff algorithm:
143-
* 1. We are using components with or inside v-repeat.
144-
* The components could have private state that needs
145-
* to be preserved across updates.
146-
* 2. We have transitions on the list, which requires
147-
* precise DOM re-positioning.
148-
*/
149-
150-
checkUpdateStrategy: function () {
151-
this.needDiff =
152-
this.asComponent ||
153-
this.el.hasAttribute(config.prefix + 'transition') ||
154-
this.template.querySelector('[' + config.prefix + 'component]')
155-
},
156-
157169
/**
158170
* Update.
159171
* This is called whenever the Array mutates.

src/instance/init.js

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ exports._init = function (options) {
5858
// attached/detached hooks on them.
5959
this._transCpnts = null
6060

61+
// props used in v-repeat diffing
62+
this._new = true
63+
this._reused = false
64+
6165
// merge options.
6266
options = this.$options = mergeOptions(
6367
this.constructor.options,

test/unit/specs/directives/repeat_spec.js

+39
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,45 @@ if (_.inBrowser) {
718718
}
719719
})
720720

721+
it('should use diff when block contains component', function (done) {
722+
var spy = jasmine.createSpy()
723+
var obj = { a: 1, b: 2 }
724+
var vm = new Vue({
725+
el: el,
726+
template:
727+
'<div v-repeat="list">' +
728+
'<test-foo v-with="foo: parentFoo"></test-foo>' +
729+
'<div v-component="test-foo" v-with="foo: parentFoo"></div>' +
730+
'</div>',
731+
data: {
732+
list: [1,2]
733+
},
734+
compiled: function () {
735+
this.$set('parentFoo', obj)
736+
},
737+
components: {
738+
'test-foo': {
739+
template: '{{foo.a}}',
740+
watch: {
741+
foo: spy
742+
}
743+
}
744+
}
745+
})
746+
747+
_.nextTick(function () {
748+
expect(spy).toHaveBeenCalledWith(obj, undefined)
749+
expect(spy.calls.count()).toBe(4)
750+
expect(el.innerHTML).toBe([1,2].map(function () {
751+
return '<div>' +
752+
'<test-foo>1</test-foo><!--v-component-->' +
753+
'<div>1</div><!--v-component-->' +
754+
'</div>'
755+
}).join('') + '<!--v-repeat-->')
756+
done()
757+
})
758+
})
759+
721760
})
722761
}
723762

0 commit comments

Comments
 (0)