vue學(xué)習篇6之響應(yīng)式原理

一努酸、響應(yīng)式原理
我們在設(shè)計屬性值的時候罢浇,頁面數(shù)據(jù)更新
用到的技巧就是Object.defineProperty()

Object.defineProperty(對象, '設(shè)置什么屬性名', {
  writable  //設(shè)置可不可以改
  configurable  //可不可以配置
  enumerable  //控制屬性是否可枚舉乒验,是不是可以被for-in循環(huán)取出來
  value  //值
  set () {}  //是一個函數(shù)搪缨,在賦值的時候觸發(fā)
  get () {}  //是一個函數(shù)逆趋,取值的時候觸發(fā)
})

最重要的就是里面的get和set方法

//Vue中的響應(yīng)式做法
//模擬簡化的版本
let o = {
  name: '張三',
  age: 19,
  gender: '男'
}
function defineReactive (target, key, value, enumerable) {
  //函數(shù)內(nèi)部就是一個局部作用域哮伟,這個value就只在函數(shù)累使用的變量(閉包)
  Object.defineProperty(target, key, {
    configurable: true,
    enumerable: !!enumerable,
    get () {
      return value
    },
    set (newVal) {
      value = newVal
    }
  })
}
//將對象的屬性轉(zhuǎn)換為響應(yīng)式
let keys = Object.keys(o)
for (let i = 0; i < keys.length; i++) {
    defineReactive(o, keys[i], o[keys[i]], true)
}

上面的簡化版是只有一層遍歷枉昏,只能處理let o = {name:'張三'}陈肛。沒有處理到深層次的 比如let o = {user:{name:'張三'}, data: [{},{}])
下面通過遞歸來實現(xiàn)深層的響應(yīng)式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewpoort" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>text</title>
</head>
<body>
    <script>
        let data = {
            name: '張三',
            age: 19,
            course: [
                {name: '語文'},
                {name: '數(shù)學(xué)'},
                {name: '英語'}
            ]
        }
        //將對象o響應(yīng)式話
        function reactify (o) {
            let keys = Object.keys(o)
            for (let i = 0; i < keys.length; i++) {
                let key = keys[i] //屬性名
                let value = o[key]
                //判斷這個屬性是不是引用類型,判斷是不是數(shù)組
                //如果是引用類型就需要遞歸凶掰,如果不是就不用遞歸
                //如果是數(shù)組就需要循環(huán)數(shù)組燥爷,然后將數(shù)組里面的元素進行響應(yīng)式化
                if (Array.isArray(value)) {
                    //數(shù)組類型
                    for (let j = 0; j < value.length; j++) {
                        reactify(value[j]) //遞歸
                    }

                } else {
                    //對象或值類型
                    defineReactive(o, key, value, true)
                }
            }
        }

        function defineReactive (target, key, value, enumerable) {
            //函數(shù)內(nèi)部就是一個局部作用域,這個value就只在函數(shù)累使用的變量(閉包)
            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
                //判斷value是非數(shù)組的引用類型
                reactify(value)  //遞歸
            }
            Object.defineProperty(target, key, {
                configurable: true,
                enumerable: !!enumerable,
                get () {
                    return value
                },
                set (newVal) {
                    value = newVal
                }
            })
        }

        reactify(data)
    </script>
</body>
</html>

上面的代碼還有一些缺陷懦窘,比如數(shù)組的push方法前翎,在數(shù)組中push一個對象,數(shù)組的其他對象都是響應(yīng)式的畅涂,而push的那個對象不是響應(yīng)式的港华。
數(shù)組需要處理幾個特殊方法:
push、pop午衰、shift立宜、unshift、reverse臊岸、sort橙数、splice
1、在改變數(shù)組的數(shù)據(jù)的時候帅戒,要發(fā)出通知
2灯帮、加進來的元素也要變成響應(yīng)式的

<script>
    /*
    如果一個函數(shù)已經(jīng)定義了,但是我們需要擴展其功能逻住,我們一般的處理辦法是:
    1.使用一個臨時的函數(shù)名存儲函數(shù)
    2.重新定義原來的函數(shù)
    3.定義擴展的功能
    4.調(diào)用臨時的那個函數(shù)
    */
    function func () {
        console.log('原始的功能')
    }
    //  1
    let _tmpFn = func

    //  2
    func = function () {
        //4
        _tmpFn()

        //  3
        console.log('新的擴展的功能')
    }

    func()  //1.打印出:原始的功能
            //2.打印出:新的擴展功能
</script>

擴展數(shù)組的push和pop
1钟哥、直接修改prototype不行,會讓所有數(shù)組都改變瞎访,不想變成響應(yīng)式的也成了響應(yīng)式
2腻贰、修改要進行響應(yīng)式化的數(shù)組的原型

//只需要之前響應(yīng)式代碼的基礎(chǔ)上加上及修改以下代碼
let ARRAY_METHOD = [
  'push',
  'pop',
  'shift',
  'unshift',
  'reverse',
  'sort',
  'splice'
]

//思路:原型式繼承,修改原型鏈的結(jié)構(gòu)
//let arr = []
//繼承關(guān)系:arr=>Array.prototype=>Object.prototype...
//改成:arr=>改寫的方法=>Array.prototype=>Object.prototype...

let array_method = Object.create(Array.prototype)
  ARRAY_METHOD.forEach(method => {
    array_method[method] = function () {
      //將數(shù)據(jù)進行響應(yīng)式化
      console.log('檢測到數(shù)據(jù)')
      //將數(shù)據(jù)進行響應(yīng)式化
      for (let i = 0; i < arguments.length; i++) {
        reactify(arguments[i])
      }
      //調(diào)用原來的方法
      let res = Array.prototype[method].apply(this, arguments)
        return res
      }
})

//修改reactify方法扒秸,value.__proto__ = array_method
//將對象o響應(yīng)式化
 function reactify (o) {
  let keys = Object.keys(o)
  for (let i = 0; i < keys.length; i++) {
    let key = keys[i] //屬性名
    let value = o[key]
    //判斷這個屬性是不是引用類型播演,判斷是不是數(shù)組
    //如果是引用類型就需要遞歸,如果不是就不用遞歸
    //如果是數(shù)組就需要循環(huán)數(shù)組伴奥,然后將數(shù)組里面的元素進行響應(yīng)式化
    if (Array.isArray(value)) {
      //數(shù)組類型
      value.__proto__ = array_method  //數(shù)組就響應(yīng)式了
      for (let j = 0; j < value.length; j++) {
        reactify(value[j]) //遞歸
       }
     } else {
        //對象或值類型
        defineReactive(o, key, value, true)
     }
  }
}

二宾巍、數(shù)據(jù)代理
我們在使用Vue的時候,獲得屬性渔伯。賦值屬性都是直接使用的Vue實例

//像Vue中new一個Vue實例
const app = new vue({
  data: {
    name: 'feifei'
  }
})
//訪問name屬性
app.name  也可以是 app._data.name

代理方法就是要將app._data中的成員給映射到app上
由于需要在更新數(shù)據(jù)的時候,更新頁面的內(nèi)容肄程,所以app._data訪問的成員與app訪問的成員應(yīng)該是同一成員锣吼。
由于app._data 已經(jīng)是響應(yīng)式的對象了选浑,所以只需要讓app訪問的成員去訪問app._data的對應(yīng)成員就可以了。

//將_data上的屬性代理到實例上
let keys = Object.keys(this._data)
//數(shù)據(jù)代理
for (let i = 0; i < keys.length; i++) {
  //this._data[keys[i]]映射到this[keys[i]]上
  //就是要讓this提供keys[i]這個屬性
  //在訪問這個屬性的時候相當于在訪問thid._data上的屬性
  Object.defineProperty(this, keys[i], {
    enumerable: true,
    configurable: true,
    get () {
      return this._data[keys[i]]
    },
    set (newVal) {
      this._data[keys[i]] = newVal
    }
  })
}

在Vue中不僅僅只有_data 還會有methods玄叠、props等等古徒,Vue封裝了一個proxy方法

//將_data上的屬性代理到實例上
let keys = Object.keys(this._data)
//數(shù)據(jù)代理
for (let i = 0; i < keys.length; i++) {
  //this._data[keys[i]]映射到this[keys[i]]上
  //就是要讓this提供keys[i]這個屬性
  //在訪問這個屬性的時候相當于在訪問thid._data上的屬性
  proxy(this, '_data', keys[i])
}

function proxy (app, prop, key) {
  Object.defineProperty(app, key, {
    enumerable: true,
    configurable: true,
    get () {
      return app[prop][key]
    },
    set (newVal) {
      app[prop][key] = newVal
    }
  })
}
//proxy(實例, '_data', 屬性名)
//proxy(實例, '_props', 屬性名)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市读恃,隨后出現(xiàn)的幾起案子隧膘,更是在濱河造成了極大的恐慌,老刑警劉巖寺惫,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疹吃,死亡現(xiàn)場離奇詭異,居然都是意外死亡西雀,警方通過查閱死者的電腦和手機萨驶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艇肴,“玉大人腔呜,你說我怎么就攤上這事≡俚浚” “怎么了核畴?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冲九。 經(jīng)常有香客問我谤草,道長,這世上最難降的妖魔是什么娘侍? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任咖刃,我火速辦了婚禮,結(jié)果婚禮上憾筏,老公的妹妹穿的比我還像新娘嚎杨。我一直安慰自己,他們只是感情好氧腰,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布枫浙。 她就那樣靜靜地躺著,像睡著了一般古拴。 火紅的嫁衣襯著肌膚如雪箩帚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天黄痪,我揣著相機與錄音紧帕,去河邊找鬼。 笑死,一個胖子當著我的面吹牛是嗜,可吹牛的內(nèi)容都是我干的愈案。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼鹅搪,長吁一口氣:“原來是場噩夢啊……” “哼站绪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丽柿,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤恢准,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甫题,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體馁筐,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年幔睬,在試婚紗的時候發(fā)現(xiàn)自己被綠了眯漩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡麻顶,死狀恐怖赦抖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辅肾,我是刑警寧澤队萤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站矫钓,受9級特大地震影響要尔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜新娜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一赵辕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧概龄,春花似錦还惠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衰粹,卻和暖如春锣光,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铝耻。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工誊爹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓替废,卻偏偏與公主長得像箍铭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椎镣,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容