1.0.17
https://jsfiddle.net/fenivana/7jcoogfc/
Click the button in jsfiddle demo
The component rendered without error.
Uncaught TypeError: Cannot read property 'reused' of undefined (vue.js:4116)
Hi fenivana,
I've looked at the code in the reproduction link, i see what you mean about the error which is displayed in the console. I copied the code and set it up exactly on my system and it rendered the component without errors. Furthermore, if you examine the console output, you'll notice that the log messages for 'created' and 'check' are repeated before the error is thrown. On my system, no such thing happens... it simply creates the component and logs the messages in sequence as expected i.e. 'created' followed by 'check'.
My thoughts are that to understand what's really happening we need to deconstruct your code. Taking it from the top..
<button @click="a++">click</button>
<component v-for="item in comps" :is="item.name" :option="item">
What exactly are you trying to achieve with this template? What i can see is that..
With the preceding analysis, i can say that what is happening is the following:
watch: {
a: function() {
this.comps = [{
name: 'foo'
}];
}
}components: {
foo: {
template: 'foo',props: ['option'], created: function() { console.log('created') this.check(); }, methods: { check: function() { console.log('check'); this.$set('option.bar', true); } } }}
- When the 'foo' component is rendered, the 'template' to be inserted simply renders the 'div' with text content of 'foo' as follows:
template: '<div>foo</div>',- When the component is rendered, the 'created' lifecycle hook is triggered and 3 things happen in sequence:
a) 'created' is logged.
b) 'this.check()' method is triggered,
c) 'check' is logged,- 'this.$set('option.bar', true);' is triggered which causes the component to try to create a 'bar' property on the 'option' prop and to set that property to 'true'. This throws an error for the following reasons:
a) The 'option' prop receives it's value from the parent vm, we have already discovered that the value will be 'undefined' since the 'item' property of the parent vm which should give 'option' a value is non existent.
b) Also, the syntax used to set this 'props' is a one way binding syntax from the parent vm to the child component. This means that 'option' can't be mutated by the 'foo' component.
c) Thus when 'this.$set('option.bar', true);' is triggered, it tries to cause Vue to perform an operation (i.e. dynamically setting a previously non existent property on the 'foo' component) on an 'undefined' property (i.e. 'option').
Wheeeew...
I may be wrong in my analysis, but the preceding is what i think is causing the error to be thrown. And maybe because of the nature of 'jsfiddle' the 'issues' with the code are not ignored.
FYI my attempt to replicate the issue on my system is..:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Component v-for error</title>
<script src="vue.js"></script>
</head>
<body>
<button @click="a++">click</button>
<component v-for="item in comps" :is="item.name" :option="item">
<script src="app.js"></script>
</body>
Maybe something about my code is more fault tolerant than 'jsfiddle' and so the 'issues' are simply ignored and the component rendered as expected.
By the way, what exactly are you trying to achieve? Are you trying to dynamicayy generate a set of 'divs' when the button is clicked?
I hope my attempt to explain what's happening is helpful.
Regards.
@fenivana
I changed created to ready and it works
In my opinion, the error was caused by infinite loop.
1)this.comps = changes comps and causes re-rendering of <component>
2) component is created with item as reactive data
3) in created of foo, check() is called
4)in check(), this.$set('option.bar', true); changes item
5) item changed, so go to step 2 ..... and you have infinite loop
how the infinite loop ends up with that error? i have no idea.
Ahhh... i see. Great then.
May i ask why you're using 'v-for' with the 'component' tag? My understanding is that it shoudn't work. I'd appreciate some insight.
Regards.
There is definitely a bug somewhere. Somehow during vFor.diff after button was clicked, this.frags.length gets to be 1 with this.frags[0] being undefined.
Also if bar is defined on the items beforehand, then the issue goes away.
cc @yyx990803.
From diff:
var frags = this.frags = new Array(data.length)frag = this.create(value, alias, i, key)frags[i] = fragDuring initialization, [2] asynchronously recursively calls diff, which lets [3] to set the fragment.
diff (vue.js:4048) <-- IMPORTANT
update (vue.js:4026)
Directive._bind._update (vue.js:8000)
Watcher.run (vue.js:3348)
runBatcherQueue (vue.js:3081)
flushBatcherQueue (vue.js:3057)
nextTickHandler (vue.js:438)
== Mutation (async) ==
timerFunc (vue.js:452)
(anonymous function) (vue.js:468)
pushWatcher (vue.js:3120)
Watcher.update (vue.js:3311) <-- SAME STACK UP TO THIS POINT
Dep.notify (vue.js:2043)
set (vue.js:27)
setPath (vue.js:2823)
(anonymous function) (vue.js:2969)
Vue.$set (vue.js:8582)
Vue.components.foo.created ((index):74)
Vue._callHook (vue.js:7899)
Vue._init (vue.js:2495)
VueComponent (VM8358:2)
build (vue.js:5722)
mountComponent (vue.js:5639)
(anonymous function) (vue.js:5604)
(anonymous function) (vue.js:5619)
cb (vue.js:367)
Vue._resolveComponent (vue.js:8535)
resolveComponent (vue.js:5621)
setComponent (vue.js:5603)
update (vue.js:5577)
Directive._bind (vue.js:8023)
linkAndCapture (vue.js:6599)
compositeLinkFn (vue.js:6575)
Fragment (vue.js:3742)
FragmentFactory.create (vue.js:3959)
create (vue.js:4188) <-- IMPORTANT
diff (vue.js:4091) <-- IMPORTANT
After button is pressed, [2] synchronously recursively calls diff, after [1] has set the length but before [3] has set the elements.
diff (vue.js:4047) <-- IMPORTANT
update (vue.js:4026)
Directive._bind._update (vue.js:8000)
Watcher.run (vue.js:3348)
pushWatcher (vue.js:3110)
Watcher.update (vue.js:3311) <-- SAME STACK UP TO THIS POINT
Dep.notify (vue.js:2043)
set (vue.js:27)
setPath (vue.js:2823)
(anonymous function) (vue.js:2969)
Vue.$set (vue.js:8582)
Vue.components.foo.created ((index):74)
Vue._callHook (vue.js:7899)
Vue._init (vue.js:2495)
VueComponent (VM8358:2)
build (vue.js:5722)
mountComponent (vue.js:5639)
(anonymous function) (vue.js:5604)
(anonymous function) (vue.js:5619)
cb (vue.js:367)
Vue._resolveComponent (vue.js:8535)
resolveComponent (vue.js:5621)
setComponent (vue.js:5603)
update (vue.js:5577)
Directive._bind (vue.js:8023)
linkAndCapture (vue.js:6599)
compositeLinkFn (vue.js:6575)
Fragment (vue.js:3742)
FragmentFactory.create (vue.js:3959)
create (vue.js:4188) <-- IMPORTANT
diff (vue.js:4091) <-- IMPORTANT
@simplesmiler, @yyx990803
I feel what you're saying, but i still want to know how it's possible to use 'v-for' on "
I'd really appreciate an explanation.
Regards.
@oakinogundeji <component v-for="item in items" :is="item.type"> is a common technique for rendering a list of different components driven by an array. You wouldn't do that for <router-view> because it doesn't make sense to have multiple <router-view>s next to each other.
@yyx990803 would not the solution just be the following?
//var frags = this.frags = new Array(data.length)
var frags = new Array(data.length)
// after all fragments are set
this.frags = frags
This is actually an async queue scheduling issue - the change was triggered from a watcher, which leads to it being applied synchronously before the diff finishes - basically, triggering a new diff in the middle of an ongoing diff, thus messing up all the intermediate state.
As a side note, I'd like to point out that mutating object props is not recommended practice...
@yyx990803 I see. I'm still learning how Vue internals are supposed to work.
@yyx990803 I know this issue has been resolved but i have a few questions i'd like clarification on.
My problem isn't with the 'v-for' or ':is' directives in themselves, rather my problem is usage of these directives with the 'component' tag. According to the contents of the docs, the 'component' tag is reserved for use a s a mount point to dynamically switch between multiple components which are bound to the ':is' attribute of the parent vm.
I would like clarification on this issue since i believe that a tool as popular as Vue shouldn't throw these kind of surprises based on the docs. Besides, while i'm relatively new to Vue.js, it is my tool of choice for building client side apps and i promote it in my writings (https://medium.com/@nohkachi).
Regards,
PS
I hope to be able to contribute much more to Vue.js, i have some tutorials and articles i believe may benefit the community. How can i contribute?
@oakinogundeji you're right - this should be in the docs. The phrasing in the current doc about <component> didn't imply that it cannot work in conjunction with v-for. The <component> tag is just a generic placeholder (same as all custom elements merely serving as placeholder for the template of the components). You can in fact use any directive on it. For example, you can do this:
<custom-component v-for="item in items"></custom-component>
Which means you can do this too:
<component v-for="item in items" :is="item.type"></component>
@yyx990803 Thanks Evan, i appreciate the clarification. If i understand you correctly, you are saying that while the 'component' tag has a primary function of serving as a mount point for dynamically rendering child components, we could also use it to generate a 'list' of components just as we would use the 'v-for' directive on any HTML element.
I'd appreciate if you clarified the significance of ':is="item.type" ', what exactly does the '.type' property signify? Are we supposed to assign the elements in the list a 'type' property?
Thanks for the response i appreciate.
Regards.
@simplesmiler
Hi, please how did you access 'vFor.diff' ? I was impressed at the amount of insight you were able to gain. I'd appreciate some pointers.
Regards.
@oakinogundeji probably easier to understand with a demo: https://jsfiddle.net/9c4nwt9a/
If you are interested in Vue internals, you can enable Vue.config.debug = true which will give you stack traces for Vue warnings. You can then add breakpoints in Chrome devtools.
@yyx990803 Thanks man, really appreciate all your help. Yes i am interested in Vue internals and everything Vue. I really appreciate what you've done with it.
May i submit my material for your review? It may be helpful for other beginners like me :-)
Regards.
@oakinogundeji as Evan said, it was Chrome DevTools, Vue source code and some guesswork.
@simplesmiler Thanks man.
Regards.
@yyx990803 我使用<component :is="currentView" :data="data"></component>,currentView为对应的动态组件 ,data为异步获取的数据,请问我通过什么方式能知道对应的动态组件已dom加载完毕,现在我在对应动态组件中 添加ready() this.$nextTick监听,但依旧无法获取动态组件,请问有什么方法吗??麻烦了。。
Most helpful comment
@oakinogundeji
<component v-for="item in items" :is="item.type">is a common technique for rendering a list of different components driven by an array. You wouldn't do that for<router-view>because it doesn't make sense to have multiple<router-view>s next to each other.