As you know, for each data property, there's a new Dep class created. Each Dep class has subscribers of watchers.
I looked through vue.js source code and for each component, there's only ONE watcher class being created which also holds the render function(template of the component).
1) Could you describe the situation when and why data property of one of the components might have the Dep class which has more than one watcher class?
2) So can I sum up something like this: if we have a component which has 5 data properties. each of these 5 data properties has different Dep class instance. and each of those Dep class has the same Watcher and that watcher holds the component's render function. If state changes, one of those 5 Dep class's notify gets run and that notify runs its watcher's render function?
You may find this introduction useful:
https://medium.com/dailyjs/tracing-or-debugging-vue-js-reactivity-the-computed-tree-9da0ba1df5f9
As you've mentioned, each data property will have its own instance of Dep. Each Dep has a subs property that holds an array of subscribers. The subscribers in that array will all be instances of the Watcher class. If a data property changes then the corresponding Dep will notify each Watcher in its subs.
Each Watcher also holds references back to its dependencies, in a property called deps. This is just an array of the Dep objects to which the Watcher is subscribed.
You'll be able to see this in your browser developer tools. If you log a Vue instance you'll find a property called _watchers that holds all the watchers related to that component. Expanding their deps will lead you to the Dep objects, though it can be tricky to tell exactly which data property each Dep represents.
The rendering process has a Watcher that it uses to track its data dependencies. You'll only get one of those per component instance.
A Watcher is also created if you use watch or $watch. Again you'll be able to see these in _watchers.
Computed properties have one Watcher each. Those will be present in the _watchers array but it's easier to see them in _computedWatchers.
A key thing to note is that dependencies are flattened. If you use a computed property you'll actually just get a dependency on all the data properties that contributed to it. You can't form a direct dependency on the computed property itself.
So to go back to your original question:
Rendering, computed properties and watch will all contribute to the subs of a Dep. Computed properties will often contribute more subscribers than you might expect due to the flattening of the dependencies.
Almost. The render function isn't called directly. When the Watcher for rendering is notified of a data change it will actually just add the component to a queue. That rendering queue won't be processed until the start of the next tick.
Related
I am trying to create sub components under a main component and sending props from main to sub components. I would like to have possibility if user makes changes on sub component it will not affect main component. I mean one way binding.
to see full repository https://github.com/saidakyuz/test-vue-props-wih-cypress
As a solution SubComp3 returns individual element of reference types, but I would like to have possibility to return full array or object in case I need to use it.
Can someone help me to find out a solution?
I found a good way copying reference objects without it's reference connection. So that main component won't be affected from changes on child component.
Changes I made(shallow);
from:
var cObjectString=this.pObjectString
to:
var cObjectString={...this.pObjectString}
But shallow doesn't work for array object. For that I have used deepclone as following;
from:
var cObjectArray =this.pObjectArray
to:
var cObjectArray = cloneDeep(this.pObjectArray)
In this article you can get more information about shallow or deep clone reference types
I have a relatively complex API request object I need to make, with a large number of UI components responsible for updating different properties of the object.
I'm passing the basic request model as a prop from a parent component to its children, which pass it on to theirs (down several "generations").
At the parent level, I have a computed property that returns a field of this data model, and a watch on that computed property.
When a child component updates the property on the model, it successfully updates everywhere that has a reference to it, but the computed property on the parent fails to recalculate, and resultantly the watch never activates.
I'm guessing I've missed the point somewhere along here, but I can't think about how else to update without resorting to long event chains through the UI.. How should I be approaching this instead?
To anyone with a similar question - from my research it seems that modifying reference values on props is not the intended approach for VueJS. Which is a shame, because initially it seemed like quite a neat pattern.
I've implemented vuex now, which is working well, and avoids long lines of events going back to the original owner of the prop data.
IF you wanted to press it, then modifying references on the object itself will force updates down the chain. So (e.g.) if you wanted to update an array property of the prop data, then instead of "pushing" to it, you would replace the whole array object (causing other components with computed properties on that array property to recalculate). But again, not recommended.
I'm losing track of reactivity overhead best practices in a Vue 2 Component. I need to generate a one time string with genId() and assign it to the component' id attribute. It seems like overkill to have any watching going on after that.
Is :id="myID" the right way to insert this into the html id?
And then when setting up the source where do I put the non-reactive data? I had 3 thoughts:
Add property myID: genId() to data. But doesn't that add it to the watchlist automatically even though it won't change? Causing overhead?
I read on Vue Forum from a year old answer that myID: genId() should go in the created hook. Are using hooks for this kind of thing a best practice? I thought hooks were discouraged.
Or I could put it in the component methods and then call it directly with :id="genId()
Is there a Vue way to do this?
Use method 2 for non-reactive data (and you use that component many many times on the page that the small overhead of adding the change listeners has any impact.)
created() {
this.myId = genId()
}
The 3 methods behave differently:
Method 1: data
This will call the genId() when the data object is created and add change listeners.
Method 2: created hook
This will call the genId() when the component object is created and doesn't add change listeners.
Method 3: method
This will call the genId() every time the template re-renders. Which happens every time a change is detected on a variable the view is listening to or a $forceUpdate() is called.
Ps. Vue objects already have a unique id: this._uid
but this is a private property and might change in a future version of Vue.
I always wanted to ask the following questions regarding the Vue Reactivity System.
I have read in Vue docs that it is recommended to keep the data of components as plain JS objects over being class objects. Same goes for individual properties of components data.
Why is this recommendation?
What is the problem with having data as instances of class objects?
What edge cases are caused by using getters/setters and methods inside of component data? (this is my main question here)
I have also another question about Vue Reactivity System.
Can I directly change component computed getters/setters and methods at runtime without having problems with Vue Reactivity System?
Are changed computed getters/setters still be cached and optimized?
Are changed methods going to be accessible from component template?
Can I add new computed getters/setters and methods at runtime? How?
Thank you very much!
Vue reactivity system (but i suppose i can extend it to all other reactive framework/lib/whatever) is just an implementation of the observer pattern . In the specific case, the observer function is fired when a property value (data, computed getter result, vuex state) changes. if the property is an integer and its value is 2, is easy for vue to check out that at some point of time it has become '3', after a comparison, fired with a mutation, a vdom change etc...and this is same for other primitive types. When structured object come into play, the comparison between values is still made, but this time the value will be a reference to the object. If the reference doesn't change, vue cannot know that something has changed, so just doesn't react....
if you set val 3 = 'foo' before and val 3 = 'bar' after, the 'val' array is still the same object in the same heap area, so you should clone it, or better use Vue.set()
I have an external library with TypeScript class instances in observed variable of my app's store:
class Store:
#observable active_obj:ExternalClass;
This instance has a method which, when called, updates its internal state leaving the object as is:
store.active_obj.change()
Now, since Mobx is not observing this object itself nor its (I assume) properties, only changes happening directly on the field active_obj (like new value assigned to it). Since instances of MyClass are provided by external library and ultimately get rendered inside this library's components I can't add observables and observers to its class structure nor React components. Mind you, this is only what I think is the reason that changing the object's properties doesn't trigger re-render...
I had to cheat a bit by using other, observed variable I change directly with nonsense data at the same time I'm calling to unobserved instance for change. Adding references to this variable on components higher up the tree, I can trigger re-render that produces updates on the (unobserving) components of the external library.
My question is, how best to make Mobx aware of change so it can notify observers of store.active_obj instance?
I think this part of Mobx documentation warns about this, but there's no workarounds or solutions for it:
** If likes where objects instead of strings, and if they were rendered by their own Like component, the Likes component would not rerender for changes happening inside a specific like.
from here, at end of the page
Edit
As per #mweststrate's question, I provide some context:
My app controls its data, but it has to create external class' instances from that
Instance's data is encapsulated and mutated in place, but it's done by asking from my app's side through user triggered events (meaning, I know when data is updated)
Basically class uses app's data to provide different views into data based on user selection and renders it with its React components
I also use this changed data elsewhere in the app in components I control
Changed data is part of external class' internals and I can't depend on it
Since Mobx tracks mutations it can see, using Observable doesn't directly work
Some possible solutions I thought:
manually notify observers that observable active_object has changed when I have called the instance it references to change
have a container object that Mobx can track and when I change its sentinel property, that update is noticed and actual instance with it