国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

了解MVVM及Vue實(shí)現(xiàn)原理,手把手帶你擼源碼。

cooxer / 459人閱讀

摘要:方法實(shí)現(xiàn)將所有屬性?huà)燧d在觀察對(duì)象,將每一項(xiàng)做一個(gè)數(shù)據(jù)劫持就是將中每一項(xiàng)用定義新屬性并返回這個(gè)對(duì)象。當(dāng)和發(fā)生變化時(shí),自動(dòng)會(huì)觸發(fā)視圖更新,獲取得到的也就是最新值。

MVVM及Vue實(shí)現(xiàn)原理

Github源碼地址:https://github.com/wyj2443573...

mvvm 雙向數(shù)據(jù)綁定
數(shù)據(jù)影響視圖,視圖影響數(shù)據(jù)
angular 臟值檢測(cè) vue數(shù)據(jù)劫持+發(fā)布訂閱模式
vue 不兼容低版本 用的是Object.defineProperty
下面涉及涵蓋的知識(shí)點(diǎn)
1. Object.defineProperty
因?yàn)関ue底層是基于Object.defineProperty實(shí)現(xiàn)的,所以對(duì)于這方面不懂的自己先學(xué)習(xí)。

Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性, 并返回這個(gè)對(duì)象。
語(yǔ)法

Object.defineProperty(obj, prop, descriptor)

基本用法

var o = {};

o.a = 1;
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : true,
  configurable : true,
  enumerable : true
});
// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : false,
  configurable : false,
  enumerable : false
});
 let o={}
 Object.defineProperty(o,"a",{
    get(){ //獲取o.a的值時(shí),會(huì)調(diào)用get方法
        return "hello";
    },
    set(value){ //o.a="s" 賦值的時(shí)候觸發(fā)set方法
        console.log(value)
    }
  })
  o.a="s"http://"s"
  o.a //"hello"
2.數(shù)據(jù)劫持Observe

vue基本格式

//html
{{a}}

模仿vue的格式
vue 中有

$options : 存在屬性 data、el、components 等等

_data : Vue實(shí)例參數(shù)中data對(duì)象

接下來(lái)構(gòu)建基本頁(yè)面

index.html




    
    Title
    


{{a}}

mvvm.js

function Vue(options={}){
    this.$options=options;
    //將所有屬性?huà)燧d在$options;
    //this._data
    let data=this._data=this.$options.data;
    //觀察data對(duì)象,將每一項(xiàng)做一個(gè)數(shù)據(jù)劫持;就是將data中每一項(xiàng)用Object.defineProperty定義新屬性并返回這個(gè)對(duì)象。
    observe(data);
}
function Observe(data) { //這里寫(xiě)的是主要邏輯
    for(let key in data){ //把data屬性通過(guò)object.defineProperty的方式 定義屬性
        let val=data[key];
        Object.defineProperty(data,key,{
            enumerable:true, //可以枚舉
            get(){
                return val; //僅僅是將以前的 a:1的方式 轉(zhuǎn)換換位defineProperty的方式
            },
            set(newValue){ //更改值的時(shí)候觸發(fā)
                if(newValue===val){ //如果設(shè)置的值跟之前的值一樣則什么也不做
                    return;
                }
                val=newValue; //將新值賦給val,那么get獲取val的時(shí)候,獲取的就是newValue;
            }
        })
    }
}
//觀察對(duì)象給對(duì)象增加ObjectDefinedProperty
function observe(data){
    return new Observe(data);
}

此時(shí)在控制臺(tái)打印 vue,發(fā)現(xiàn)已經(jīng)在vue._data中映射了正確的數(shù)據(jù)。

接下來(lái)操作: vue._data.a=30

如果此時(shí)data中a是一個(gè)復(fù)雜的對(duì)象類(lèi)型,如下

{{a.a}}

則此時(shí)打印輸出vue._data,只有第一層添加上了defineProperty,第二層的a無(wú)法劫持

那么我們要遞歸 深層添加defineProperty 另外遞歸的時(shí)候注意添加退出條件,當(dāng)value不是對(duì)象的時(shí)候退出。

代碼添加如下

function Observe(data) { //這里寫(xiě)的是主要邏輯
    for(let key in data){ //把data屬性通過(guò)object.defineProperty的方式 定義屬性
        let val=data[key];
        observe(val); //遞歸 劫持
        Object.defineProperty(data,key,{
            enumerable:true, //可以枚舉
            get(){
                return val; //僅僅是將以前的 a:1的方式 裝換位defineProperty的方式
            },
            set(newValue){ //更改值的時(shí)候觸發(fā)
                if(newValue===val){ //如果設(shè)置的值跟之前的值一樣則什么也不做
                    return;
                }
                val=newValue; //將新值賦給val,那么get獲取val的時(shí)候,獲取的就是newValue;
            }
        })
    }
}
function observe(data){
    if(typeof data!="object"){  //如果非對(duì)象,則退出遍歷遞歸
        return;
    }
    return new Observe(data);
}

此時(shí)內(nèi)層的a也同樣得到劫持

如果我們給a設(shè)置新值的時(shí)候vue._data.a={b:3} ,會(huì)發(fā)現(xiàn)內(nèi)層b并沒(méi)有被數(shù)據(jù)劫持。那么在賦新值的時(shí)候,也應(yīng)該通過(guò)defineProperty去定義。

在set中用defineProperty定義新屬性

 set(newValue){ //更改值的時(shí)候觸發(fā)
     if(newValue===val){ //如果設(shè)置的值跟之前的值一樣則什么也不做
         return;
     }
     val=newValue; //將新值賦給val,那么get獲取val的時(shí)候,獲取的就是newValue;
     observe(newValue)
  }

分析到這里,我們已經(jīng)能夠?qū)崿F(xiàn)了深度的數(shù)據(jù)觀察

3.數(shù)據(jù)代理

上面的代碼如果想要訪問(wèn)a的屬性需要通過(guò) vue._data.a 這樣的寫(xiě)法獲得,這種寫(xiě)法過(guò)于繁瑣。我們接下來(lái)改善一下:用vue.a的方式直接訪問(wèn)到a(用vue 代理 vue._data
-- 1.首先用this代理this._data; 讓數(shù)據(jù)中的每一項(xiàng)都用defineProperty代理。

function Vue(options={}){
    this.$options=options;
    //將所有屬性?huà)燧d在$options;
    //this._data
    let data=this._data=this.$options.data;
    //觀察data對(duì)象,將每一項(xiàng)做一個(gè)數(shù)據(jù)劫持;就是將data中每一項(xiàng)用Object.defineProperty定義新屬性并返回這個(gè)對(duì)象。
    observe(data);
    for(let key in data){
        Object.defineProperty(this,key,{  //this 代理this._data;
            enumerable:true,
            get(){
                return this._data[key]; //相當(dāng)于 this.a={a:1}
            },
            set(newVal){
                
            }
        })
    }
}

到這一步我們已經(jīng)實(shí)現(xiàn)了數(shù)據(jù)代理的初級(jí)版,vue.a 可以直接獲取值而非vue._data.a

-- 2.get方法很容易理解,這里較為重點(diǎn)的是在set中設(shè)置。首先思考:如果直接設(shè)置this.a={name:1} ,this.a 與this._data.a 它們的值同步改變嗎?

set(newVal){
    this[key]=newVal; //?可以這樣做嗎?我們來(lái)實(shí)踐下
}

很顯然兩者是不能夠同步改變的。

方法實(shí)現(xiàn)

 function Vue(options={}){
    this.$options=options;
    //將所有屬性?huà)燧d在$options;
    //this._data
    let data=this._data=this.$options.data;
    //觀察data對(duì)象,將每一項(xiàng)做一個(gè)數(shù)據(jù)劫持;就是將data中每一項(xiàng)用Object.defineProperty定義新屬性并返回這個(gè)對(duì)象。
    observe(data);
    //this 代理this._data;
    for(let key in data){
        Object.defineProperty(this,key,{
            enumerable:true,
            get(){
                return this._data[key]; //相當(dāng)于 this.a={a:1}
            },
            set(newVal){ //如果直接更改this[key]="XXX",那么this._data[key]的值是不會(huì)被同步改變的。
                // 我們可以通過(guò)給this._data[key]=value賦值,從而調(diào)取Observe方法中的set,賦予this._data[key]新值。
                // get(){return this._data[key]},獲取到的值即是調(diào)取Observe方法中g(shù)et方法return的值
                // 也就是根源上的改變是this._data[key];這樣不管是this._data[key]還是this[key]隨便哪一個(gè)被賦予新值,兩者都是同步變化的
                this._data[key]=newVal;
            }
        })
    }
}

下面來(lái)分析一下思路

1.我們可以在set中設(shè)置this._data[key]=newValue,如果此時(shí)vue.a={name:1}它調(diào)取是Observe方法中的set,賦予this._data[key]新值。
設(shè)置值的時(shí)候相當(dāng)于走的是這一步

set(newValue){ //更改值的時(shí)候觸發(fā)
             if(newValue===val){ //如果設(shè)置的值跟之前的值一樣則什么也不做
                 return;
             }
             val=newValue; //將新值賦給val,那么get獲取val的時(shí)候,獲取的就是newValue;
             observe(newValue)
         }

2.如果此時(shí)我們獲取vue.a 的值,即通過(guò)get方法獲取return this._data[key],得到的就是最新值

這里解釋說(shuō)明一下

學(xué)到這里我們應(yīng)該了解到:

vue特點(diǎn)不能新增不存在的屬性 因?yàn)椴淮嬖诘膶傩詻](méi)有g(shù)et 和 set,它就不會(huì)監(jiān)控?cái)?shù)據(jù)的變化

深度響應(yīng): 因?yàn)槊看钨x予一個(gè)新對(duì)象時(shí)會(huì)給這個(gè)新對(duì)象增加數(shù)據(jù)劫持。

4.編譯模板Compile
這一步我們要做的目的是將目標(biāo)元素內(nèi){{xx}} 花括號(hào)中的xx替換成對(duì)應(yīng)的值。

第一步、代碼實(shí)現(xiàn)如下

function Vue(options={}){
    /*代碼承接上面*/
    new Compile(options.el,this) //實(shí)例化Compile
}
function Compile(el){
    //el表示替換哪個(gè)元素范圍內(nèi)的模板
    let replacePart=document.querySelector(el);
    let fragment=document.createDocumentFragment(); 
    while(child = replacePart.firstChild){ //將app中的內(nèi)容移至內(nèi)存中
        fragment.appendChild(child);
    }
    replace() //我們?cè)诖艘龅氖峭ㄟ^(guò)replace方法,將代碼片段中的{{a.a}}的a.a替換為data中對(duì)應(yīng)的值。
    replacePart.appendChild(fragment);
}
function replace(){

}

第二步、replace方法先找到所有要替換的地方,代碼如下:

{{A}}

{{a.a}}

{{b}}

{{c}}

{hphlfvt}

 function Compile(el,vm){
    //el代表替換的范圍
    let replacePart=document.querySelector(el);
    let fragment=document.createDocumentFragment();
    while(child = replacePart.firstChild){ //將app中的內(nèi)容移至內(nèi)存中
        fragment.appendChild(child);
    }
    replace(fragment) //我們?cè)诖艘龅氖峭ㄟ^(guò)replace方法,將代碼片段中的{{a.a}}的a.a替換為data中對(duì)應(yīng)的值。
    replacePart.appendChild(fragment);
    function replace(fragment){
        Array.from(fragment.childNodes).forEach(function(node){
            let text=node.textContent;
            let reg=/{{(.*)}}/;
            if(node.nodeType===3&& reg.test(text)){ //nodeType:3 文本節(jié)點(diǎn)
                console.log(RegExp.$1); // A、 a.a 、b 等等
            }
            if(node.childNodes){
                replace(node)  //如果當(dāng)前node存在子節(jié)點(diǎn),遞歸找到所有需要替換的地方
            }
        })
    }
}

這一步我們能夠找到所有要替換的目標(biāo)了

第三步、replace方法中 用對(duì)應(yīng)值替換掉需要替換掉的地方,代碼如下:

 function replace(fragment){
        Array.from(fragment.childNodes).forEach(function(node){
            let text=node.textContent;
            let reg=/{{(.*)}}/;
            if(node.nodeType===3&®.test(text)){ //nodeType:3 文本節(jié)點(diǎn)
                console.log(RegExp.$1);
                let arr=RegExp.$1.split(".") // [A] [a,a] [b] ...
                let val=vm; //val:{a:{a:1}}
                arr.forEach(function(k){
                    val=val[k]  //舉例 第一次循環(huán)  val=val.a   val賦值后val:{a:1} ;第二次循環(huán)  val=val.a  val賦值后為1
                })
                node.textContent=text.replace(reg,val)
            }
            if(node.childNodes){
                replace(node)  //如果當(dāng)前node存在子節(jié)點(diǎn),遞歸替換
            }
        })
    }

替換結(jié)果如下

5.發(fā)布訂閱模式

不明白發(fā)布訂閱模式的朋友先去學(xué)習(xí)
參考鏈接:https://segmentfault.com/a/11...

代碼一:

 //發(fā)布訂閱模式  先訂閱 再發(fā)布
// 訂閱就是往事件池里面扔函數(shù)   發(fā)布的就是事件池中的函數(shù)依次執(zhí)行

//我們假設(shè)subs中每個(gè)方法中都有update屬性,
function Dep(){
    this.subs=[]
}
Dep.prototype.addSub=function(sub){ //訂閱
    this.subs.push(sub)
}
Dep.prototype.notify=function(){
    this.subs.forEach(sub=>{
        sub.update();
    })
}
function Watcher(fn){ //watch是一個(gè)類(lèi) 通過(guò)這個(gè)類(lèi)創(chuàng)建的實(shí)例都擁有update方法
    this.fn=fn
}
Watcher.prototype.update=function(){
    this.fn();
}
let watcher=new Watcher(function(){console.log(1)}); //監(jiān)聽(tīng)函數(shù)
let dep=new Dep();
dep.addSub(watcher); //將watcher放在數(shù)組中
dep.addSub(watcher);
dep.addSub(watcher);
dep.notify() // 數(shù)組關(guān)系

代碼二(然后我們將代碼二的這個(gè)發(fā)布訂閱的模板放到我們的mvvm.js最下面)

function Dep(){
    this.subs=[]
}
Dep.prototype.addSub=function(sub){ //訂閱
    this.subs.push(sub)
}
Dep.prototype.notify=function(){
    this.subs.forEach(sub=>{
        sub.update();
    })
}
function Watcher(fn){ 
    this.fn=fn
}
Watcher.prototype.update=function(){
    this.fn();
}
6.連接視圖與數(shù)據(jù)
那么我們接下來(lái)的目的是:當(dāng)數(shù)據(jù)變化的時(shí)候,我們需要重新刷新視圖,將頁(yè)面中的{{a.a}}雙括號(hào)中的a.a也能夠被實(shí)時(shí)的替換掉。這就用到了上面介紹的發(fā)布訂閱模式。

方法:我們得先將node.textContent=text.replace(reg,val)訂閱一下,當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候,執(zhí)行node.textContent=text.replace(reg,val)此操作。

我們先寫(xiě)要訂閱的事件

這里思考當(dāng)數(shù)據(jù)變化的時(shí)候會(huì)產(chǎn)生新的值,我們需要用newValue替換原有的值。要想取到新的值,我們需要用到當(dāng)前實(shí)例vm與正則的捕獲到的RegExp.$1,從而獲取類(lèi)似this.a.a的最新值。

 new Watcher(vm,RegExp.$1,function(newValue){ //訂閱的事件  函數(shù)里需要接受新的值
     node.textContent=text.replace(reg,newValue);
 });

此時(shí)Watcher類(lèi)也應(yīng)該改動(dòng)下,不懂沒(méi)關(guān)系,可以順著看下面的解析。

function Watcher(vm,exp,fn){
    this.fn=fn;
    this.vm=vm;
    this.exp=exp;
    //我們要將fn添加到訂閱中
    Dep.target=this;
    console.log(Dep.target)
    let val=vm;
    let arr=exp.split(".");
    arr.forEach(function(k){
        val=val[k];
    })
    Dep.target=null;
}

Dep.target為

下面我們來(lái)分析代碼
這個(gè)邏輯相對(duì)復(fù)雜,不明白的話(huà)多看幾遍

上述代碼中,我們可以看到執(zhí)行了這一步
    let val=vm;
    let arr=exp.split(".");
    arr.forEach(function(k){  
        val=val[k];  //獲取this.a.a的時(shí)候就會(huì)觸發(fā)get方法
    })

這一步遍歷 arr 取val[k]的時(shí)候相當(dāng)于獲取this.a,會(huì)觸發(fā)了默認(rèn)的get方法。也就是觸發(fā)這里的get方法

在get方法中我們需要訂閱事件:

上述代碼中,取值的時(shí)候會(huì)調(diào)取get方法,Dep.target值是存在的,此時(shí)將Dep.target放到我們的事件池中。

當(dāng)我們set的時(shí)候,觸發(fā)事件池中的事件

此時(shí)update的方法我們得更改下,賦予新值

Watcher.prototype.update=function(){
    let val=this.vm;
    let arr=this.exp.split(".");
    arr.forEach(function(k){ //this.a.a
        val=val[k];
    })
    this.fn(val); //newValue
}

此時(shí)我們就能得到我們想要的結(jié)果了,當(dāng)數(shù)據(jù)發(fā)生改變時(shí),視圖也會(huì)更新。

7.雙向數(shù)據(jù)綁定的實(shí)現(xiàn)

思路: 先找到v-model這個(gè)屬性,取對(duì)應(yīng)的屬性值s,然后用vue.s 替換input中value值
在Compile方法中寫(xiě)邏輯

打印結(jié)果如下

此時(shí)已經(jīng)能夠?qū)?duì)應(yīng)的s值賦值給輸入框

接下來(lái)我們需要做的是,每次更改輸入框中的值的時(shí)候,對(duì)應(yīng)的s也會(huì)跟著改變

操作結(jié)果如下

8.實(shí)現(xiàn)computed

官網(wǎng)上有兩種computed的基礎(chǔ)用法
用法一

var vm = new Vue({
  el: "#example",
  data: {
    message: "Hello"
  },
  computed: {
    // 一個(gè) computed 屬性的 getter 函數(shù)
    reversedMessage: function () {
      // `this` 指向 vm 實(shí)例
      return this.message.split("").reverse().join("")
    }
  }
})

用法二

computed: {
  fullName: {
    // getter 函數(shù)
    get: function () {
      return this.firstName + " " + this.lastName
    },
    // setter 函數(shù)
    set: function (newValue) {
      var names = newValue.split(" ")
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

思路: computed實(shí)現(xiàn)相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,只是把數(shù)據(jù)掛在vm上

1.html頁(yè)面如下

 

s的值:{{s}}

computed的值:{{hello}}

{{A}}

{{a.a}}

{{b}}

{{c}}

{d7vx5xl}

2.將hello屬性?huà)燧d在當(dāng)前實(shí)例上,先初始化computed

initComputed 函數(shù)實(shí)現(xiàn)
function initComputed(){
    let vm=this;
    let computed=this.$options.computed; //Object.keys [name:"tom",age:2]=>[name,age]
    Object.keys(computed).forEach(function(key){
        Object.defineProperty(vm,key,{ //computed[key]
            get:typeof computed[key]==="function"? computed[key]:computed[key].get,
            set(){

            }
        })
    })
}
分析以上代碼,我們能得知this.hello的變化 只依賴(lài)于this.s 和 this.c 。當(dāng)s和c發(fā)生變化時(shí),自動(dòng)會(huì)觸發(fā)視圖更新,獲取this.hello得到的也就是最新值。

++++++++++++到此已經(jīng)完成了分享,如有相關(guān)的問(wèn)題和建議還望不吝提出++++++++++++

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/103195.html

相關(guān)文章

  • 設(shè)計(jì)架構(gòu)

    摘要:先來(lái)看一張系統(tǒng)前后端架構(gòu)模型圖。一種接口的約定本文用于定義一種統(tǒng)一的接口設(shè)計(jì)方案,希望具有參考價(jià)值。,和都是常見(jiàn)的軟件架構(gòu)設(shè)計(jì)模式,它通過(guò)分離關(guān)注點(diǎn)來(lái)改進(jìn)代碼的組織方式。 如何無(wú)痛降低 if else 面條代碼復(fù)雜度 相信不少同學(xué)在維護(hù)老項(xiàng)目時(shí),都遇到過(guò)在深深的 if else 之間糾纏的業(yè)務(wù)邏輯。面對(duì)這樣的一團(tuán)亂麻,簡(jiǎn)單粗暴地繼續(xù)增量修改常常只會(huì)讓復(fù)雜度越來(lái)越高,可讀性越來(lái)越差,有沒(méi)...

    graf 評(píng)論0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件開(kāi)發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開(kāi)發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....

    izhuhaodev 評(píng)論0 收藏0
  • javascript知識(shí)點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類(lèi)型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話(huà)題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫(xiě)代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評(píng)論0 收藏0
  • 把手你擼一個(gè)cli工具

    摘要:最近,筆者就在為組里的框架去做一套基本的工具。通過(guò)這邊文章,筆者希望大家都能簡(jiǎn)單的去實(shí)現(xiàn)一個(gè)屬于自己的腳手架工具。我們?cè)谙滦略鑫募@個(gè)文件導(dǎo)出一個(gè)的類(lèi)。結(jié)語(yǔ)到此,一個(gè)簡(jiǎn)單的就制作完成了,大家可以參考等優(yōu)秀的適當(dāng)?shù)臄U(kuò)展自己的工具。 你有沒(méi)有遇到過(guò)在沒(méi)有vue-cli、create-react-app這樣子的腳手架的時(shí)候一個(gè)文件一個(gè)文件的去拷貝老項(xiàng)目的配置文件。最近,筆者就在為組里的框架...

    xingqiba 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<