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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM