Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to package the data into reactive in Vue to achieve MDV effect

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

Vue how to package the data into reactive to achieve MDV effect, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain in detail for you, people with this need can come to learn, I hope you can get something.

Let's first explain what reactive is. To put it simply, it wraps the data into an observable type that we can perceive when the data changes.

The relevant implementation code of Vue is all in the core/observer directory, and if you want to read it yourself, it is recommended to start from core/instance/index.js.

Before we start talking about the specific implementation of reactive, let's talk about a few objects: Watcher, Dep, and Observer.

Watcher

Watcher is an object used to observe data implemented by vue, which is implemented in core/observer/watcher.js.

This class is mainly used to observe changes to the data referenced in the method / expression (the data needs to be reative, that is, data or props), and deal with it when the change is made. First, take a look at the input parameters of the Watcher class:

Vm: Component,expOrFn: string | Function,cb: Function,options?: Object

Explain what these participants are for:

Vm: the VueComponent to which the current watcher belongs.

ExpOrFn: the method / expression to listen for. For example: VueComponent's render function, or computed's getter method, or a string of this type abc.bbc.aac (because vue's parsePath method uses split ('.') Abc ['bbc'] is not supported because of the attribute segmentation that can be done by. If expOrFn is a method, it assigns a value directly to the getter attribute of watcher, and if it is an expression, it is converted into a method and then given to getter.

Cb: this callback is triggered when the data referenced in the getter changes.

Options: additional parameters. The parameters you can pass in include deep and user,lazy,sync. These values default to false.

If deep is true, it will do another deep traversal of the object returned by getter for further dependency collection.

User is used to mark whether the listener is called by the user through $watch.

Lazy is used to mark whether watcher is lazy execution. This attribute is used for computed data. When the value in data is changed, the getter is not calculated immediately to get a new value, but the watcher is marked as dirty. When the computed data is referenced, the new computed data is returned, thus reducing the amount of computation.

Sync indicates whether the watcher updates the data synchronously when the value in data changes. If it is true, the value will be updated immediately, otherwise it will be updated in nextTick.

Once you know what the input parameter is for, you basically know what the Watcher object has done.

Dep

Dep is a dependency object implemented by vue. In core/observer/dep.js, the amount of code is very small and easy to understand.

Dep mainly plays the role of a link, that is, to connect reactive data and watcher, each reactive data creation, along with the creation of a dep instance. See the defineReactive method in observer/index.js. The simplified defineReactive method is as follows.

Function defineReactive (obj, key, value) {const dep = new Dep (); Object.defineProperty (obj, key, {get () {if (Dep.target) {dep.depend ();} return value} set (newValue) {value = newValue; dep.notify ();}})

After the dep instance is created, the logic of getter and setter is injected into the data. When the data is referenced, the getter is triggered, and when will the data be referenced? It is when watcher executes getter, and when watcher executes getter, watcher is stuffed into Dep.target, and then by calling the dep.depend () method, the dep of this data creates a connection with watcher.

After the connection is created, the setter logic is triggered when the data is changed. You can then notify all the watcher associated with the dep through dep.notify (). To get each watcher to respond.

For example, I watch a data and reference the same data in a computed data. At the same time, I also explicitly refer to this data in template, so at this time, there are three watcher associated with the dep of the data, one is the watcher of render function, one is the watcher of computed, and the other is the watcher created by the user calling the $watch method. When the data changes, the dep of the data notifies the three watcher to deal with it accordingly.

Observer

Observer can turn a plainObject or array into a reactive. There is very little code, just traversing plainObject or array, calling the defineReactive method for each key value.

Process flow

After the introduction of the above three classes, basically we should have a vague understanding of the implementation of vue reactive. Next, we will talk about the whole process with an example.

When vue is instantiated, initData is called first, then initComputed is called, and finally mountComponent is called to create the watcher of render function. In order to complete a VueComponent data reactive.

InitData

The initData method is in core/instance/state.js, and most of this method makes some judgments, such as preventing names in data from being duplicated with those in methods. The core is actually one line of code:

Observe (data, true)

What the observe method does is create an Observer object, and the Observer object, as I said above, traverses the data and calls the defineReactive method.

An Observer object is created using the data node, and then all the data under the data is processed by reactive in turn, that is, the defineReactive method is called. When the defineReactive method is executed, each property in the data is injected with getter and setter logic, and the dep object is created. At this point, the initData execution is complete.

InitComputed

Then there is the initComputed method. This method is to process the data under the computed node in vue, iterate through the computed node, get key and value, and create a watcher object. If value is a method, the input parameter expOrFn of instantiating watcher is value, otherwise it is value.get.

Function initComputed (vm: Component, computed: Object) {. Const watchers = vm._computedWatchers = Object.create (null) for (const key in computed) {const userDef = computed [key] let getter = typeof userDef = = 'function'? UserDef: userDef.get... Watchers [key] = new Watcher (vm, getter, noop, {lazy: true}) if (! (key in vm)) {defineComputed (vm, key, userDef)} else if (process.env.NODE_ENV! = = 'production') {.}}

We know that expOrFn can be either a method or a string. Therefore, through the above code, we found a usage that is not stated in the official documentation, for example, my data structure is as follows

{obj: {list: [{value: '123'}]}}

If we want to use the value attribute value of the first node in list in template, write a computed:

Computed: {value: {get: 'obj.list.0.value'}}

Then use {{value}} directly when you use it in template, so that even if list is empty, you can guarantee that there will be no error, similar to the use of lodash.get. OK, go too far and get back to the point.

After creating the watcher, mount the key of the computed to the vm through Object.defineProperty. And add the following logic to getter

If (watcher.dirty) {watcher.evaluate ()} if (Dep.target) {watcher.depend ()} return watcher.value

As I said earlier, the watcher of computed data is lazy. When the data referenced in computed data is changed, it will not recalculate the value immediately, but just mark dirty as true, and then when the computed data is referenced, the above getter logic will determine whether watcher is dirty, and if so, recalculate the value.

And the latter paragraph, watcher.depend. Is to collect the dependencies of the data used in the computed data, so that when the data referenced in the computed data changes, it can also trigger the re-execution of the render function.

Depend () {let I = this.deps.length while (iMurray -) {this.deps[ I] .depend ()} mountComponent

After initializing data and computed, create a watcher for render function. The logic is as follows:

Export function mountComponent (vm: Component, el:? Element, hydrating?: boolean): Component {vm.$el = el... CallHook (vm, 'beforeMount') let updateComponent... UpdateComponent = () = > {vm._update (vm._render (), hydrating)}. Vm._watcher = new Watcher (vm, updateComponent, noop) if (vm.$vnode = = null) {vm._isMounted = true callHook (vm, 'mounted')} return vm}

As you can see, the input parameter expOrFn when creating the watcher is the updateComponent method, while the render function is executed in the updateComponent method. The watcher is not lazy, so when the watcher is created, the render function is executed immediately, when the render function is executed. If data is used in template, the getter logic of data is triggered, and then dep.depend () is executed to collect dependencies, and if there is a parameter in template that uses computed, the getter logic of computed is also triggered to collect the dependencies of data referenced in the method of computed. Finally complete the collection of all dependencies.

Finally, let me give an example:

{{test}}

Export default {data () {return {name: 'cool'}}, computed: {test () {return this.name +' test';} initialization process:

Process name as reactive and create an instance of dep

Bind test to vm, create watcher instance watch2 of test, and add getter logic.

Create a watcher instance of render function, watcher2, and execute render function immediately.

When render function is executed, the getter logic to test is triggered, and both watcher1 and watcher2 create a mapping relationship with dep.

Update process after changing the value of name:

Iterate through the bound watcher list and execute watcher.update ().

Watcher1.dirty is set to true.

Watcher2 re-executes render function, triggering the getter to test, because watcher1.dirty is true, so the value of test is recalculated and the value of test is updated.

Re-render view

Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report