vue源碼學習一

本篇主要爲閱讀源碼做一些準備,相關代碼參考了網上相關資料。

本篇主要講述數據的動態動態綁定與更新:

 

1:數據的動態綁定與更新核心方法是Object.defineProperty(),下面是相關介紹。

Object.definePropety(obj,property,descriptor)  
/
 *  obj 需要操作的對象
 *  property  需要修改的屬性
 *  descriptor  屬性描述符,屬性描述符有兩種類型的取值 1:數據描述符,2:存取描述符,屬性描述符是這兩者之一,不能同時存在
 *  
    數據描述符和存取描述符均具有的可選鍵有:

      configurable:(true||false) 默認false, 當且僅當該屬性值爲true時,屬性描述符的內容才能被改變
      enumerable: (true||false)  默認false, 當該屬性爲true時,操作的(property)屬性纔是枚舉類型
   
    數據描述符獨有的屬性
       value: 默認爲undefined    值爲property屬性對應的值。
       writable: 默認false        property是否可寫入,僅當爲true時,操作的屬性才能進行賦值操作
		
    存取描述符獨有的屬性

	get: 默認值undefined。給property提供geter方法的屬性,當訪問property時,就會觸發get屬性,	
	
	set: 默認值undefined。 給property提供setter方法的屬性,當修改property時會觸發該方法。


/

2:這裏我們想實現和vue一樣的格式: 

window.onload = function(){
    let app = new Vue ({
    el:'#app', //根節點
    data:{           //數據
        number:1,
        count:1
    },
    methods:{        //方法
        addN: function(){
            this.number+=2
        },
        addC: function(){
            this.count++
        }
    }
})    }

首先我們需要先定義函數Vue()

    //新建Vue函數
    function Vue (options){
         //初始化實例
        this._init(options);  
    };
    Vue.prototype._init = function (options){
        this.$el = document.querySelector(options.el); //根節點
        this.$data = options.data;   //數據
        this.$methods = options.methods;    //方法
        this._var = {};                    //爲動態更新數據設置的變量
        this._obverse(this.$data);        //爲數據設置get set 方法
        this._directive(this.$el);         // 循環遍歷文檔,找尋v指令進行初始化
    };

再看_obverse()方法: 

 Vue.prototype._obverse = function(data){
        //循環數據$data,對每個數據執行Object.defineProperty()
        var _this = this;
        Object.keys(data).forEach((item)=>{
            if(data.hasOwnProperty(item)){
                var value = data[item];
                _this._var.dire= [];
                if(typeof value === 'Object'){
                    this._obverse(value)
                } 
                Object.defineProperty (data,item,{
                    configurable: true,
                    enumerable: true,
                    get(){
                        return value
                    },
                    set(newValue){
                        if(newValue!=value){
                            value = newValue;
                        //需要明白的是input中的數據對應的是實例data的數據,然後通過v指定標記進行恰當的賦值操作
                        //如何讓一個確定的節點與數據發生關係,方法是給實例設置變量,將節點作爲對象添加到變量中,然後
                        //通過某一標記進行循環判斷即可獲得節點。
                        _this._var.dire.forEach(item=>{
                            item._update();
                        })
                        }
                    }
              })
            }
        })
    };

接着看_directive()方法 : 該方法循環遍歷整個文檔節點,然後找出所有v指令,並對各個指令進行邏輯賦值,v-click增加點擊事件,v-model增加input事件, v-bind:進行數據顯示。

//對v指令進行邏輯賦值
    Vue.prototype._directive = function (el) {
        let _this = this;
        let root = el.children
        //childNodes與children的區別
         let node=Array.prototype.slice.call(root);
        node.forEach((item)=>{
            if(item.children.length>0){
                _this._directive(item);
            }
            if(item.hasAttribute('v-click')){
                //v-click觸發點擊事件
                let attrValue = item.getAttribute('v-click');
                 item.onclick = function(){
                     //調用實例的methods方法
                    _this.$methods[attrValue].apply(_this.$data)
                     
                 }
            }
            if(item.hasAttribute('v-model')&&(item.nodeName === 'INPUT'||item.nodeName === 'TEXTAREA')){
                //v-model觸發input事件
                let attrValue = item.getAttribute('v-model'); //自執行函數爲了初始化input值
                item.addEventListener('input',(function(){
                    let value = item.Value;
                  _this._var.dire.push(new Node({
                      content:'value',
                      name: attrValue,
                      node: item,
                      vm: _this
                  }));
                  return function(){
                      _this.$data[attrValue] = this.value==''?0:parseInt(this.value);
                  }
                })(),false)
            }
            if(item.hasAttribute('v-bind')){
                let attrValue = item.getAttribute('v-bind');
                //查詢實例對象的數據對節點進行賦值
                
                _this._var.dire.push(new Node({
                    content:'innerHTML',
                    name: attrValue,
                    node: item,
                    vm: _this
                }))
                // item.innerHTML = _this.$data[attrValue]
            }
        }) 
    }

這裏有一個問題:如何對某一節點進行數據的動態交互呢?

要想解決該問題可以將某一節點設置數據需要的必要條件保存下來,然後供vue數據使用,這也是Node函數存在的理由。

 //設置節點對象
    function Node(opt){
        this.$content = opt.content; //內容輸出到哪裏  innerHTML  或者 value 
        this.$name = opt.name;      //節點對應的屬性值   v-bind= 'name'
        this.$node = opt.node;     //節點本身
        this._this = opt.vm    // Vue實例this
        this._update();
    }
    Node.prototype._update = function(){
        //通過該函數對節點內容進行賦值,由該句話可知我們所需要的節點信息
        this.$node[this.$content] = this._this.$data[this.$name]
    }

最後說明一下大致流程:

文檔加載完畢 > 執行_init()函數進行初始化>>首先執行_obverse()方法遍歷整個data數據,爲每個數據添加get與set方法,注意

set方法內循環保存有節點信息的數組來對含有v-bind指令的節點進行數據展示。>>接着執行_directive()方法,該方法遍歷整個文檔節點,當發現節點存在v-click就綁定onclick事件與vue實例方法相關聯,當發現v-model就綁定input事件,注意該句柄是一個

自執行函數,目的爲了使該節點初始時就有值,之後又返回一個函數作爲input事件該函數是爲了觸發數據的set方法,

當發現v-bind時,將節點信息保存到數組中同時會運行update()初始化節點的值。

 

最後附上完整代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue源碼一</title>
</head>
<body>
   <div id="app">
        <form>
            <input type="text"  v-model="number" class="in">
            <button type="button" v-click="addN">增2</button>
        </form>
            <h3 v-bind="number"></h3>
        <form>
            <input type="text"  v-model="count" class="in">
            <button type="button" v-click="addC">增1</button>
        </form>
            <h3 v-bind="count"></h3>
   </div>
    <script>
        //新建Vue函數
    function Vue (options){
        this._init(options);
    };
    Vue.prototype._init = function (options){
        this.$el = document.querySelector(options.el);
        this.$data = options.data;
        this.$methods = options.methods;
        //定義觀察者函數
        this._var = {};
        this._obverse(this.$data);
        this._directive(this.$el);
    };
    Vue.prototype._obverse = function(data){
        //循環數據$data
        var _this = this;
        Object.keys(data).forEach((item)=>{
            console.log(item)
            if(data.hasOwnProperty(item)){
                var value = data[item];
                _this._var.dire= [];
                if(typeof value === 'Object'){
                    this._obverse(value)
                } 
                Object.defineProperty (data,item,{
                    configurable: true,
                    enumerable: true,
                    get(){
                        return value
                    },
                    set(newValue){
                        if(newValue!=value){
                            value = newValue;
                        //編寫代碼對h3中的數據進行實時監控
                        //需要明白的是input中的數據對應的是實例data的數據,然後通過v指定標記進行恰當的賦值操作
                        //如何讓一個確定的節點與數據發生關係,方法是給實例設置變量,將節點作爲對象添加到變量中,然後
                        //通過某一標記進行循環判斷即可獲得節點。
                        _this._var.dire.forEach(item=>{
                            item._update();
                        })
                        }
                    }
              })
            }
        })
    };
    //對v指令進行邏輯賦值
    Vue.prototype._directive = function (el) {
        let _this = this;
        let root = el.children
        //childNodes與children的區別
         let node=Array.prototype.slice.call(root); //抓化爲真正的數組
        node.forEach((item)=>{
            if(item.children.length>0){
                _this._directive(item);
            }
            if(item.hasAttribute('v-click')){
                //v-click觸發點擊事件
                let attrValue = item.getAttribute('v-click');
                 item.onclick = function(){
                    console.log(1111111111)
                     //調用實例的methods方法
                    _this.$methods[attrValue].apply(_this.$data)
                     
                 }
            }
            if(item.hasAttribute('v-model')&&(item.nodeName === 'INPUT'||item.nodeName === 'TEXTAREA')){
                //v-model觸發input事件
                let attrValue = item.getAttribute('v-model'); //自執行函數爲了初始化input值
                item.addEventListener('input',(function(){
                    let value = item.Value;
                  _this._var.dire.push(new Node({
                      content:'value',
                      name: attrValue,
                      node: item,
                      vm: _this
                  }));
                  return function(){
                      _this.$data[attrValue] = this.value==''?0:parseInt(this.value);
                  }
                })(),false)
            }
            if(item.hasAttribute('v-bind')){
                let attrValue = item.getAttribute('v-bind');
                //查詢實例對象的數據對節點進行賦值
                
                _this._var.dire.push(new Node({
                    content:'innerHTML',
                    name: attrValue,
                    node: item,
                    vm: _this
                }))
                // item.innerHTML = _this.$data[attrValue]
            }
        }) 
    }
    //設置節點對象
    function Node(opt){
        this.$content = opt.content; //內容輸出到哪裏  innerHTML  或者 value 
        this.$name = opt.name;      //節點對應的屬性值   v-bind= 'name'
        this.$node = opt.node;     //節點本身
        this._this = opt.vm    // Vue實例this
        this._update();
    }
    Node.prototype._update = function(){
        //通過該函數對節點內容進行賦值
        this.$node[this.$content] = this._this.$data[this.$name]
    }
   window.onload = function(){
    let app = new Vue ({
    el:'#app',
    data:{
        number:1,
        count:1
    },
    methods:{
        addN: function(){
            this.number+=2
        },
        addC: function(){
            this.count++
        }
    }
})    
   }
    </script>
</body>
</html>














 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章