Vue2 数据响应式

324 阅读1分钟

Vue2 数据响应式原理

  • 数据响应式,是Vue 最独特的特性之一。"响应式",指当数据(model)发生变化时,页面(view)自动更新。

getter 和 setter

  • 要理解 Vue2 的数据响应式,要先理解 ES6 的 getter 和 setterObject.defineProperty
  • Vue 通过 Object.defineProperty 将数据进行处理,让 Vue 的实例作为数据的代理并监听数据的变化,并通过 getter 和 setter 来获取数据的变化
    • getter 用来读取数据,而 setter 用来更新数据
        let myData = {n:0}
        let data = proxy({data:myData})
        function proxy({data}){
            let value = data.n
            // delete data.n
            Object.defineProperty(data, 'n', {
                get(){
                    return value
                },
                set(newValue){
                    if(newValue<0)return
                    value = newValue
                }
            })

            const obj = {}
            Object.defineProperty(obj, 'n', {
                get(){
                    return data.n
                },
                set(value) {
                    data.n = value
                }
            })
            return obj // obj 就是代理
        }

Vue2 数据响应式的局限性和 Vue 的解决方式

  • Vue 无法监听对象内部的变化。这是 JS 的局限性造成的
    • 如下,点击 setB 并不能使 obj.b 展示在页面中,Vue 无法监听一开始不存在的 obj.b
    new Vue({
        data: {
            obj: {
                a: 0
            }
        },
        template:`
            <div>
                {{obj.b}}
                <button @click="setB">setB</button>
            <div>
        `,
        methods: {
            setB(){
                this.obj.b = 1
            }
        }
    }).$mount('#app')
  • 解决方法:
    1. 将所有属性提前声明好,初始化值为 null 或者 undefined
    2. 使用 Vue.set() / this.$set()
      • this.$set() 会自动新增一个 key,并自动创建代理和监听(如果没有创建过)
    new Vue({
        data: { 
            obj: {
                a: 0
            }
        },
        template:`
            <div>
                {{obj.b}}
                <button @click="setB">setB</button>
            <div>
        `,
        methods: {
            setB(){
                this.$set(obj.b, 'b', 1)
            }
        }
    }).$mount('#app')
  • 数组的变异方法
    • 对数组来说,我们只能使用使用 Vue.set() / this.$set(),因为没有办法提前预知有多少个元素
    • Vue 改造了数组的 api,在原型链中加入了 7 个变异方法,这些变异方法会自动调用 this.$set,并调用原来的数组方法
    • 这些变异方法包括:
      • push()
      • pop()
      • shift()
      • unshift()
      • splice()
      • sort()
      • reverse()
    new Vue({
        data: {
            array: ['a', 'b', 'c']
        },
        template:`
            <div>
                {{array}}
                <button @click="setD">setD</button>
            </div>
        `,
        methods: {
            setD(){
                this.array.push("d") // 并非原生的 push
            }
        }
    }).$mount('#app')