This time I will bring you an analysis of the principles of Vue data responsiveness. What are the precautions of Vue data responsiveness? The following is a practical case, let’s take a look.
Preface
Vue’s data response mainly relies on Object.defineProperty(), so what is the whole process like? Taking the path of Vue with our own ideas actually means taking the principles of Vue as the end point. Let's reverse the implementation process.
The code in this article is a low-configuration version, and many places are not rigorous. For example, if(typeof obj === 'object') is to determine whether obj is an object, although obj may also be Array and other types of data, but for the sake of simplicity, this article will write it directly to represent the judgment object. For arrays, use Array.isArray().
Transform data
Let’s first try to write a function for transforming objects:
Why should we write this first? What about functions? Because transforming data is the most basic and important step, all subsequent steps will depend on this step.
// 代码 1.1 function defineReactive (obj,key,val) { Object.defineProperty(obj,key,{ enumerable: true, configurable: true, get: function () { return val; }, set: function (newVal) { //判断新值与旧值是否相等 //判断的后半段是为了验证新值与旧值都为NaN的情况 NaN不等于自身 if(newVal === val || (newVal !== newVal && value !== value)){ return ; } val = newVal; } }); }
For example, const obj = {}, and then call the defineReactive(obj,'a',2) method. At this time, within the function, val=2, and then every time the value of obj.a is obtained They all get the value of val. When setting obj.a, they also set the value of val. (Every time defineReactive is called, a closure will be generated to save the value of val);
Process Discussion
After verification, this function was found It can indeed be used. Then let’s discuss the response process:
Input data
Transform data (defineReactive() )
If the data changes=> trigger event
Let’s look at the third step. How does the data change trigger subsequent events? Think about it carefully, if you want to change the data, you must set the data first, then we can just add the method to set() and it will be ok.
Then there is another important question:
Dependency collection
How do we Do you know what event will be triggered after the data changes? In Vue:
Use data => View; Data is used to render the view, so it is the best time to collect dependencies when obtaining data. Vue generates a Dep instance when transforming the data attributes. Used to collect dependencies.
// 代码 1.2 class Dep { constructor(){ //订阅的信息 this.subs = []; } addSub(sub){ this.subs.push(sub); } removeSub (sub) { remove(this.subs, sub); } //此方法的作用等同于 this.subs.push(Watcher); depend(){ if (Dep.target) { Dep.target.addDep(this); } } //这个方法就是发布通知了 告诉你 有改变啦 notify(){ const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update(); } } } Dep.target = null;
Code 1.2 is part of the code of Dep. For the time being, you only need to know the functions of the two methods
depend() --- can be understood as collecting dependencies event, without considering other aspects, the function is equivalent to addSub()
notify() --- This method is more intuitive and executes all dependent update() methods. Just change the view later and so on.
This article mainly discusses the process of data response and does not discuss the Watcher class in depth, so you only need to know the functions of the methods in Dep.
Then the code is changed to 1.1
//代码 1.3 function defineReactive (obj,key,val) { const dep = new Dep(); Object.defineProperty(obj,key,{ enumerable: true, configurable: true, get: function () { if(Dep.target){ //收集依赖 等同于 dep.addSub(Dep.target) dep.depend() } return val; }, set: function (newVal) { if(newVal === val || (newVal !== newVal && val !== val)){ return ; } val = newVal; //发布改变 dep.notify(); } }); }
There is a doubt in this code, what is Dep.target? Why do we need Dep.target to collect dependencies?
Dep is a class, and Dep.target is an attribute of the class, not an attribute of the dep instance.
The Dep class is available globally, so Dep.target can be accessed globally and its value can be changed arbitrarily.
get This method is very common to use, and it is impossible to call dep.depend() every time you use it to obtain data values.
dep.depend() is actually dep.addSub(Dep.target).
The best way is to set Dep.target to an object before use, and set Dep.target = null after the subscription is completed.
Verification
It’s time to verify the availability of a wave of codes
//代码 1.4 const obj = {};//这一句是不是感觉很熟悉 就相当于初始化vue的data ---- data:{obj:{}}; //低配的不能再低配的watcher对象(源码中是一个类,我这用一个对象代替了) const watcher = { addDep:function (dep) { dep.addSub(this); }, update:function(){ html(); } } //假装这个是渲染页面的 function html () { document.querySelector('body').innerHTML = obj.html; } defineReactive(obj,'html','how are you');//定义响应式的数据 Dep.target = watcher; html();//第一次渲染界面 Dep.target = null;
此时浏览器上的界面是这样的
然后在下打开了控制台开始调试,输入:
obj.html = 'I am fine thank you'
然后就发现,按下回车的那一瞬间,奇迹发生了,页面变成了
结尾
Vue数据响应的设计模式和订阅发布模式有一点像,但是不同,每一个dep实例就是一个订阅中心,每一次发布都会把所有的订阅全部发布出去。
Vue的响应式原理其实还有很大一部分,本文主要讨论了Vue是如何让数据进行响应,但是实际上,一般的数据都是很多的,一个数据被多处使用,改变数据之后观察新值,如何观察、如何订阅、如何调度,都还有很大一部分没有讨论。主要的三个类Dep(收集依赖)、Observer(观察数据)、Watcher(订阅者,若数据有变化通知订阅者),都只提了一点点。
之前写有一篇Vue响应式----数组变异方法,针对Vue中对数组的改造进行讨论。当然之后有更多其他的文章,整个数据响应流程还有很多内容,三个主要的类都还没有讨论完。
其实阅读源码不仅仅是为了知道源码是如何工作的,更重要的是学习作者的思路与方法,我写的文章都不长,希望自己能够每次专注一个点,能够真真实实领悟到这一个点的原理。当然也想控制阅读时间,免得大家看到一半就关闭了。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
The above is the detailed content of Analysis of Vue data responsiveness principle. For more information, please follow other related articles on the PHP Chinese website!