深入理解Vue2响应式原理

本文内容是基于《剖析Vue.js内部运行机制》关于响应式系统部分整理而来。

虽然说Vue3已经发布有两三年了,数据响应也已经用Proxy重构了,但无论是为了学习编程思想,或者是为了面试,都有必要了解Vue2响应式原理是什么。不出意外的话,以后我还会写一篇关于Proxy数据代理的文章。

Object.defineProperty

要理解Vue2的响应式原理,就必须要了解Object.defineProperty。我先来说一下它的基本用法。

/* 
    obj: 目标对象
    prop: 需要操作的目标对象的属性名
    descriptor: 描述符

    return value 传入对象
*/
Object.defineProperty(obj, prop, descriptor)

这里举一个例子,更便于理解。

const obj = {
    name: 'ExMaterial'
}

Object.defineProperty(obj, 'name', {
    value: 'EM',            // 设置值
    writable: false,        // 是否可修改
    configurable: false,    // 是否可删除属性
    enumerable: false,      // 是否可枚举
})

console.log(obj.name); // EM 
delete obj.name
console.log(obj.name); // EM 删除操作不生效

如果还是不懂的话,建议移步MDN,上面有详细的介绍。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

实现observer

首先定义一个 cb 函数,这个函数用来模拟视图更新,调用它即表示视图更新。

function cb (val) {
    /* 渲染视图 */
    // 此处省略了如何对数据更新,此处又设计其它知识,如diff算法,静态页面标记
    // 为便于理解,此处简化
    console.log("视图更新啦~");
}

然后,再封装一个响应式函数。

function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            return val
        },
        set: function reactiveSetter(newVal) {
            if (newVal === val) return;
            cb(newVal) // 触发视图更新
        }
    })
}

如果一个对象有多个属性的话,上面这样写是远远不够的,此时就可以在上述基础上封装一个函数,对其传入对象的每个属性进行响应式设置。

function observer (value) {
    if (!value || (typeof value !== 'object')) {
        return;
    }
    
    Object.keys(value).forEach((key) => {
        defineReactive(value, key, value[key]);
    });
}

最后再来封装一个Vue

class Vue {
    constructor(options) {
        this._data = options.data;
        observer(this._data)
    }
}

来测试一下

let vue = new Vue({
    data: {
        test: 'I am test.'
    }
});
vue._data.test = "hello world" // 视图更新了

这里给出详解版以及部分优化之后的版本。

// 模拟响应式数据更新后的操作
function cb(val) {
    console.log("视图更新了~");
}

// 对要响应的数据进行设置的函数
function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            return val
        },
        set: function reactiveSetter(newVal) {
            if (newVal === val) return
            cb(newVal)
        }
    })
}

// 对一个对象多个属性一次设置,全部响应式
// 也可以通过递归,因为一个对象内可能还包括对象。
// 具体代码实现在下面的代码注释中
function observer(val) {
    if (!val || (typeof val !== 'object')) {
        return
    }
    Object.keys(val).forEach((key) => {
        defineReactive(val, key, val[key])
    })

    // Object.keys(val).forEach((key) => {
    //     if (typeof(val[key] === 'object')) {
    //         observer(val[key])
    //     }
    //     defineReactive(val, key, val[key])
    // })
}

class Vue {
    constructor(options) {
        this._data = options.data
        observer(this._data)
    }
}

let vue = new Vue({
    data: {
        test: 'I am test.'
    }
});
vue._data.test = "hello world" // 视图更新了
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值