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

Lines changed: 39 additions & 27 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 39 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy