1. 首页
  2. Vuejs

vue早期源码学习系列之一:如何监听一个对象的变化

2. 新设置的job属性就不在带有自定义的getter和setter了,不再提示“你访问了job"这些字样

前言

我们都知道,要想精通前端领域,研究分析成熟的框架是必不可少的一步。很多人可能都有这样的体会:“很努力地去阅读一些热门框架的源码,但是发现难度太高,花了很多时间却得不到什么,最终不得不放弃。”
我也一直被这个问题困扰,直到我想到了这样的一个方法。
从成熟框架的早期源码开始看起,从作者的第一个commit开始看起,然后逐个的往前翻。这样一开始的代码量不多,多看几遍还是可以理解的。而且在这个过程中,就像电影回放一样,我们可以看到作者先写什么,后写什么,在哪些地方进行了什么样的改良,其中又不小心引入了什么bug,等等。
这真的是一个很好的办法。所以,我就用这个方法来研究vue的源码。

目标

我checkout到的版本是这个位置,在这个版本中,我们可以发现:代码中主要是observer和emitter这些东西,这些是以后实现数据绑定以及$watch的关键基础。所以我们把本篇的学习目标定位为:“如何监听一个对象的变化” 具体要实现效果如下图所示。
demo

注:这个版本build出来的vue代码运行是会报错了,根据错误提示,很容易定位到错误原因是src/internal/init.js文件头部少引了observer和util,添加上就好了。

思路

我们有两个难点需要解决。

第一:当对象的某个属性变化的时候,如何触发自定义的回调函数?
答案:ES5中新添加了一个方法:Object.defineProperty,通过这个方法,可以自定义getter和setter函数,从而在获取对象属性和设置对象属性的时候能够执行自定义的回调函数。

第二:对象往往是一个深层次的结构,对象的某个属性可能仍然是一个对象,这种情况怎么处理?
比如说

let data = {
    user: {
        name: "liangshaofeng",
        age: "24"
    },
    address: {
        city: "beijing"
    }
};

答案:递归算法,也就是下面代码中的walk函数。如果对象的属性仍然是一个对象的话,那么继续new一个Observer,直到到达最底层的属性位置。

下面是实现的具体代码。

代码

// 观察者构造函数
function Observer(data) {
    this.data = data;
    this.walk(data)
}

let p = Observer.prototype;

// 此函数用于深层次遍历对象的各个属性
// 采用的是递归的思路
// 因为我们要为对象的每一个属性绑定getter和setter
p.walk = function (obj) {
    let val;
    for (let key in obj) {
        // 这里为什么要用hasOwnProperty进行过滤呢?
        // 因为for...in 循环会把对象原型链上的所有可枚举属性都循环出来
        // 而我们想要的仅仅是这个对象本身拥有的属性,所以要这么做。
        if (obj.hasOwnProperty(key)) {
            val = obj[key];

            // 这里进行判断,如果还没有遍历到最底层,继续new Observer
            if (typeof val === 'object') {
                new Observer(val);
            }

            this.convert(key, val);
        }
    }
};

p.convert = function (key, val) {
    Object.defineProperty(this.data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            console.log('你访问了' + key);
            return val
        },
        set: function (newVal) {
            console.log('你设置了' + key);
            console.log('新的' + key + ' = ' + newVal)
            if (newVal === val) return;
            val = newVal
        }
    })
};

let data = {
    user: {
        name: "liangshaofeng",
        age: "24"
    },
    address: {
        city: "beijing"
    }
};

let app = new Observer(data);

遗留问题

上面实现的代码还有很多问题。
比如:

  1. 只监听的对象的变化,没有处理数组的变化。
  2. 当你重新set的属性是对象的话,那么新set的对象里面的属性不能调用getter和setter。比如像下图所示,重新设置的job属性就不在带有自定义的getter和setter了,不再提示“你访问了job"这些字样。
    bug

参考资料:

  1. https://segmentfault.com/a/1190000004384515
  2. http://jiongks.name/blog/vue-code-review/

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文来源于网络,其版权属原作者所有,如有侵权,请与小编联系,谢谢!

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:vue早期源码学习系列之一:如何监听一个对象的变化

链接:https://www.javascriptc.com/1402.html

原文链接:https://github.com/youngwind/blog/issues/84

« vue早期源码学习系列之二:如何监听一个数组的变化
Flutter 中文教程资源

相关推荐

QR code