Vue-如何实现响应式

写在前面:总算在今天仔仔细细的看了一遍双向数据绑定的帖子,其实之前也看了很多,都是自己理解能力不太够,再一个静不下心(哭)。看完之后进行一下总结。如有理解不到位的欢迎指正,谢谢。

Vue的响应式:其实就是通过数据的改变去驱动DOM视图的变化。这里是Vue最核心的内容。初始化时通过Object.defineProperty进行绑定,设置通知的机制。当编译生成的渲染函数被实际渲染的时候,就会触发getter进行依赖收集,在数据变化的时候,触发setter进行更新。

首先一个小小入门例子。假设<p>标签的内容会随着obj中数据的改变而变化.。

1 <div id="app">
2         <p id="name"></p>
3     </div>
4     <script>
5         var obj={}
6         obj.name="软软"//操作Object.defineProperty来使得name数据的改变影响p标签
7     </script>

通过Object.defineProperty(obj, prop, descriptor)来实现。 obj:要在其上定义属性的对象。prop:要定义或修改的属性的名称。descriptor:将被定义或修改的属性描述符。 

 1 <div id="app">
 2         <p id="name"></p>
 3 </div>
 4 <script>
 5      var obj={};
 6      Object.defineProperty(obj,'name',{
 7             get(){
 8                 return document.querySelector('#name').innerHTML;
 9             },
10             set(newVal){
11                 document.querySelector('#name').innerHTML=newVal
12             }
13       })
14    obj.name="软软"//操作Object.defineProperty来使得name数据的改变影响p标签
15 </script>

接下来创建一个新的newVue.js。通过使用new NewVue(.....)来实现vue的响应式

 1 class NewVue{
 2     constructor(options){//接收所想要配置的对象就像是new Vue({data:{...}})
 3         this.$options=options;//先缓存一下options一会其他的类要用到
 4         //数据响应化
 5         this.$data=options.data;//拿出{data:{}}的数据
 6         this.observe(this.$data);//对data中的数据进行观察.
 7     }
 8     observe(value){
 9         //为了语句健壮性,先判断是否存在这个value,不存在就返回了
10         if(!value||typeof value !='object'){
11             return;
12         }
13         //遍历该对象
14         Object.keys(value).forEach(key=>{
15             
16             this.defineReactive(value,key,value[key])
17         })
18     }
19     //定义数据响应式函数
20     defineReactive(obj,key,val){
21         this.observe(val)//为了递归实现类似{data:{foo.bar:"xxx"}}中的foo.bar这种数据嵌套的问题
22         Object.defineProperty(obj,key,{
23             get(){
24                 return val;
25             },
26             set(newVal){
27                 if(newVal==val){
28                     return;
29                 }
30                 val=newVal;
31                 console.log(`${key}属性更新了:${val}`)
32             }
33         })
34     }
35 }

创建一个index.html实现这个响应式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="newVue.js"></script>
    <script>
        const app=new NewVue({
            data:{
                test:"fjs是粑粑",
                foo:{
                    bar:"bar"
                }
            }
        });
        app.$data.test="hello fjs";
        app.$data.foo.bar="oh bar"
    </script>
</body>
</html>

最终实现的结果:

 依赖收集

 1 class NewVue{
 2     constructor(options){
 3         this.$options=options;
 4         this.$data=options.data;
 5         this.observe(this.$data);
 6         //模拟一下watcher创建
 7         new Watcher();
 8         this.$data.test;
 9         new Watcher();
10         this.$data.foo.bar;
11     }
12     observe(value){
13         if(!value||typeof value !='object'){
14             return;
15         }
16         Object.keys(value).forEach(key=>{
17             this.defineReactive(value,key,value[key])
18         })
19     }
20 
21     defineReactive(obj,key,val){
22         this.observe(val)
23         const dep=new Dep()
24         Object.defineProperty(obj,key,{
25             get(){
26                 Dep.target&&dep.addDep(Dep.target)
27                 return val;
28             },
29             set(newVal){
30                 if(newVal==val){
31                     return;
32                 }
33                 val=newVal;
34                 dep.notify();//通知所有的watcher进行更新
35             }
36         })
37     }
38 }
39 
40 class Dep{//订阅器
41     constructor(){
42         this.deps=[];//在deps中存放若干依赖,也就是watcher(订阅者).这个依赖其实就是属性值发生改变的属性
43     
44     }
45     addDep(dep){//增加依赖
46         this.deps.push(dep)
47     }
48     notify(){//通知所有的依赖(watcher)去做更新
49         this.deps.forEach(dep=>dep.update())
50     }
51 }
52 
53 class Watcher{//订阅者其实就是用来做具体更新的那个对象
54     constructor(){
55         //将当前这个Watcher实例指定到Dep的静态属性target
56         Dep.target=this;//这个target只有一个,当有第二个watcher出现时就会覆盖成第二个watcher
57     }
58     update(){
59         console.log('属性更新了!!!')
60     }
61 }
原文地址:https://www.cnblogs.com/zx-fjs/p/13342061.html