This article brings you what is the principle of vue responsiveness? The analysis of Vue's responsiveness principle has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
new Vue() => _init() => initState:
function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
Determine whether the vue instance exists props, methods, data, computed, watch Call the corresponding initialization function
The main job is to call defineProperty to mount get on the properties respectively (when this hook is triggered, the dep of the current property will be The instance is pushed into the current Dep.target, which is the deps of the current watcher, that is, the dependencies it subscribes to. Dep.target will be discussed below. And the dep instance will also push the current watcher, the observer, into its subs array), set Method (notify all observers in the dependent subs to call their update method).
Its function is to traverse all the properties in the computed object and give the property a new computed watcher (a dep dependency is defined in the calculated property. Subscribe to the watcher that needs to use the calculated property). Computed will also be mounted with get (get method) and set methods by calling defineProperty (the set method will determine whether it is passed in, and if it is not passed in, it will be set to a noop empty function)
The get method of the computed property is the return value of the following function Function
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
Pay attention to the watcher.depend(). This method allows the watcher observer that uses this attribute to subscribe to the dependency in the watcher, and the calculated attribute watcher will push the watcher that subscribes to it into his In subs (when the calculated attribute value changes, notify the watcher observer who subscribes to it)
watcher.evaluate(), this method is by calling the watcher's get method (it should be noted that the watcher's get method will call pushTarget Push the previous Dep.target instance onto the stack and set Dep.target to be computed
watcher, the responsive property that the calculated property depends on will
The watcher is pushed into its subs, so when the dependent responsive property changes, the computed watcher that subscribes to it will be notified, computed watcher
Then notify the watcher subscribed to the calculated attribute to call the update method), and call the handler function bound to the calculated attribute key in the get method to calculate the value.
This watcher is user watcher (customized by the developer in the component).
The function of initWatch is to traverse the properties in the watch and call the defined $watch for each property monitored by the watch
Vue.prototype.$watch = function ( expOrFn: string | Function, cb: any, options?: Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {} options.user = true // 代表该watcher是用户自定义watcher const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { cb.call(vm, watcher.value) } return function unwatchFn () { watcher.teardown() } }
When calling new Watcher in the code, it will execute the watcher just like render watcher. The get method calls pushTarget to assign the current user watcher to Dep.target. The statement value = this.getter.call(vm, vm) in get() will trigger the get method of the responsive attribute monitored by the custom watcher, and Change the current user watcher pushes into the subs that this attribute depends on, so when user After the property set monitored by the watcher is triggered, the watcher subscribed to the dependency is notified to trigger the update, that is, the handler corresponding to the key bound to the watch is triggered. Then call popTarget to pop the stack and assign it to Dep.target.
The initState initialization work is roughly done here. Next, $mount will be executed to start the rendering work.
The main work of $mount is to create a new rendering Watcher and use updateCompent as a callback. When passing it in and executing the new Watcher among the
updateComponent = () => { vm._update(vm._render(), hydrating) } new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */)
three watchers, only the computed watcher will not execute its get() method at the beginning. The new render watcher in $mount will call the get() method and pushTarget to assign the current render watcher to Dep.target. Next comes the highlight, calling updateComponent, which will execute vm._update(vm._render(), hydrating), in which the render function will trigger the get hook of the responsive attributes used in html. The get hook will cause the dependent instance dep of the responsive property to convert the current render The watcher is pushed into its subs array, so when the dependent responsive properties change, it will traverse the subs to notify the watcher subscribed to it to call update().
Maybe everyone is confused about how to adjust watcher and dep. Let me give you an example.
<div id="app"> <div>{{a}}</div> <div>{{b}}</div> </div> new Vue({ el: "#app", data() { return { a:1, } }, computed:{ b() { return a+1 } }, })
I will start directly from rendering, and only talk about dep and dep. Watcher-related
$mount: vm._update(vm._render(), hydrating) will be triggered when a new rendering watcher (the watcher's get method will assign the rendering watcher to Dep.target), and when rendering, it will Get the responsive attributes used in html. In the above example, a is used first. At this time, the get hook of a will be triggered. Among them, dep.depend() will push the current rendering watcher into the subs array of dep of the a attribute. .
Continue execution and access b (b is the value of the calculated attribute), which will trigger the get method of the calculated attribute. The get method of computed properties is the computedGetter function returned after calling the createComputedGetter function. watcher.depend() is executed in the computedGetter function. Watcher's depend method is reserved exclusively for computed Used by watcher.
As mentioned above, in addition to computed watcher, the other two watchers are in new After that, their get methods will be executed, then computed What does the watcher do after the new is completed? It will create a new dep.
Go back to the method watcher.depend() just mentioned specifically for computed watcher. Its function is to execute this.dep.depend() (the dep defined by computed watcher is used here). this.dep.depend() will cause the current rendering watcher to subscribe to the calculated property dependency. The calculated property will also push the rendering watcher into its own subs ([render watcher]). When the value of the calculated property is modified, it will Notify the watcher in subs to call update(), so the page can be refreshed if the calculated attribute value changes.
Go back to the get hook that triggered the calculated attribute of b mentioned earlier. The get hook will finally execute watcher.evaluate(), and watcher.evaluate() will execute the get() method of computed watcher.
The key point comes at this time. Dep.target (render watcher) will be pushed into the targetStack stack (after being stored so that it can be taken out later for continued use), and then the computed watcher of this calculated attribute will be assigned to Dep .target. In the get method, value = this.getter.call(vm, vm) will execute the handler bound to the computed attribute.
Return a 1 in the above example. If a is used, a's get hook will be triggered, and the get hook will call dep.depend(). dep.depend() will cause the computed watcher to store dep in its deps array, and a's dep will store the current dep. Dep.target (computed watcher) is stored in its subs array. In the current example, the subs of a will be [render watcher, computed watcher], so changes in the value of a will traverse the watchers in the subs of a and call the update() method. The a used in html will be refreshed. When the calculated attribute watcher calls the update() method, it will notify its own subs ([render watcher]) in render When the watcher calls the update method, the calculated attribute b used in HTML will refresh the dom (a reminder here, I am just speaking roughly, it will not necessarily trigger an update after the attribute that the calculated attribute depends on changes, it will compare after the calculation is completed) whether the value changes).
computed The get() method of watcher will finally call popTarget(), pop the previously stored render watcher out of the stack and assign it to Dep.target. At this time, the targetStack in my example becomes an empty array.
The get method of render watcher will pop off the stack at the end of execution, and the value assigned to Dep.target will be empty at this time.
Related recommendations:
Vue data responsiveness principle analysis
Related responsiveness principles in Vue (detailed tutorial)
The above is the detailed content of What is the principle of vue responsiveness? Analysis of vue responsiveness principle. For more information, please follow other related articles on the PHP Chinese website!