Vue.js响应式原理详解

本人是Java背景,许多年前刚接触JavaScript时有点怪怪的,因为它没有 getters 和 setters。随着时间的推移,我开始喜欢上这个缺失的特性,因为相比Java大量的 getter 和 setter,它让代码更简洁。例如,我们看看下面的Java代码:

class Person{
   String firstName;
   String lastName;    // 这个Demo中省略了一些构造器代码 :)    public void setFirstName(firstName) {        this.firstName = firstName;
   }    public String getFirstName() {        return firstName;
   }    public void setLastName(lastName) {        this.lastName = lastName;
   }    public String getLastName() {        return lastName;
   }
}// Create instancePerson bradPitt = new Person();
bradPitt.setFirstName("Brad");
bradPitt.setLastName("Pitt");

登录后复制

javascript开发人员永远不会这样做,相反他们会这样:

var Person = function () {};var bradPitt = new Person();
bradPitt.firstName = 'Brad';
bradPitt.lastName = 'Pitt';

登录后复制

这要简洁的多。通常简洁更好,不是吗?

的确如此,但有时我想获取一些可以被修改的属性,但我不用知道这些属性是什么。例如,我们在Java代码中扩展一个新的方法 getFullName():

class Person{    private String firstName;    private String lastName;    // 这个Demo中省略了一些构造器代码 :)    public void setFirstName(firstName) {        this.firstName = firstName;
   }    public String getFirstName() {        return firstName;
   }    public void setLastName(lastName) {        this.lastName = lastName;
   }    public String getLastName() {        return lastName;
   }    public String getFullName() {        return firstName + " " + lastName;
   }
}

Person bradPitt = new Person();
bradPitt.setFirstName("Brad");
bradPitt.setLastName("Pitt");// Prints 'Brad Pitt'System.out.println(bradPitt.getFullName());

登录后复制

在上面例子中, fullName 是一个计算过的属性,它不是私有属性,但总能返回正确的结果。

C# 和隐式的 getter/setters

我们来看看 C# 特性之一:隐式的 getters/setters,我真的很喜欢它。在 C# 中,如果需要,你可以定义 getters/setters,但是并不用这样做,但是如果你决定要这么做,调用者就不必调用函数。调用者只需要直接访问属性,getter/setter 会自动在钩子函数中运行:

立即学习“前端免费学习笔记(深入)”;

public class Foo {    public string FirstName {get; set;}    public string LastName {get; set;}    public string FullName {get { return firstName + " " + lastName }; private set;}
}

登录后复制

我觉得这很酷…

现在,如果我想在JavaScript中实现类似的功能,我会浪费很多时间,比如:

var person0 = {
   firstName: 'Bruce',
   lastName: 'Willis',
   fullName: 'Bruce Willis',
   setFirstName: function (firstName) {        this.firstName = firstName;        this.fullName = `${this.firstName} ${this.lastName}`;
   },
   setLastname: function (lastName) {        this.lastName = lastName;        this.fullName = `${this.firstName} ${this.lastName}`;
   },
};
console.log(person0.fullName);
person0.setFirstName('Peter');
console.log(person0.fullName);

登录后复制

它会打印出:

"Bruce Willis"
"Peter Willis"

登录后复制

但使用 setXXX(value) 的方式并不够’JavaScripty'(是个玩笑啦)。

下面的方式可以解决这个问题:

var person1 = {
   firstName: 'Brad',
   lastName: 'Pitt',
   getFullName: function () {        return `${this.firstName} ${this.lastName}`;
   },
};
console.log(person1.getFullName()); // 打印 "Brad Pitt"

登录后复制

现在我们回到被计算过的 getter。你可以设置 first 或 last name,并简单的合并它们的值:

person1.firstName = 'Peter'person1.getFullName(); // 返回 "Peter Pitt"

登录后复制

这的确更方便,但我还是不喜欢它,因为我们要定义一个叫getxxx()的方法,这也不够’JavaScripty’。许多年来,我一直在思考如何更好的使用 JavaScript。

然后 Vue 出现了

在我的Youtube频道,很多和Vue教程有关的视频都讲到,我习惯响应式开发,在更早的Angular1时代,我们叫它:数据绑定(Data Binding)。它看起来很简单。你只需要在Vue实例的 data() 块中定义一些数据,并绑定到HTML:

var vm = new Vue({
   data() {
       return {
       greeting: 'Hello world!',
       };
   }
})
{greeting}

登录后复制

显然它会在用户界面打印出 “Hello world!”。

现在,如果你改变greeting的值,Vue引擎会对此作出反应并相应地更新视图。

methods: {
   onSomethingClicked() {        this.greeting = "What's up";
   },
}

登录后复制

很长一段时间我都在想,它是如何工作的?当某个对象的属性发生变化时会触发某个事件?或者Vue不停的调用 setInterval 去检查是否更新?

通过阅读Vue官方文档,我才知道,改变一个对象属性将隐式调用getter/setter,再次通知观察者,然后触发重新渲染,如下图,这个例子来自官方的Vue.js文档:

vue-r-1.png

但我还想知道:

怎么让数据自带getter/setters?

这些隐式调用内部是怎样的?

第一个问题很简单:Vue为我们准备好了一切。当你添加新数据,Vue将会通过其属性为其添加 getter/setters。但是我让 foo.bar = 3? 会发生什么?

这个问题的答案出现在我和SVG & Vue专家Sarah Drasner的Twitter对话中:

vue-r-2.png

Timo: foo.bar=value;是怎么做到实时响应的?Sarah: 这个问题很难在Twitter说清楚,可以看这篇文章Timo: 但这篇文章并没有解释上面提到的问题。Timo: 它们就像:分配一个值->调用setter->通知观察者,不理解为什么在不使用setInterval和Event的情况下,setter/getter就存在了。Sarah: 我的理解是:你获取的所有数据都在Vue实例data{}中被代理了。

显然,她也是参考的官方文档,之前我也读过,所以我开始阅读Vue源码,以便更好的理解发生了什么。过了一会我想起在官方文档看到一个叫 Object.defineProperty() 的方法,我找到它,如下:

/**
* 给对象定义响应的属性
*/export function defineReactive (
   obj: Object,
   key: string,
   val: any,
   customSetter?: ?Function,
   shallow?: boolean
) {    const dep = new Dep()    const property = Object.getOwnPropertyDescriptor(obj, key)    if (property && property.configurable === false) {        return
   }    // 预定义getter/setters    const getter = property && property.get    const setter = property && property.set    let childOb = !shallow && observe(val)
   Object.defineProperty(obj, key, {
       enumerable: true,
       configurable: true,        get: function reactiveGetter () {            const value = getter ? getter.call(obj) : val            if (Dep.target) {
               dep.depend()                if (childOb) {
                   childOb.dep.depend()
               }                if (Array.isArray(value)) {
                   dependArray(value)
               }
           }            return value
       },        set: function reactiveSetter (newVal) {            const value = getter ? getter.call(obj) : val            /* 禁用eslint 不进行自我比较 */            if (newVal === value || (newVal !== newVal && value !== value)) {                return
           }            /* 开启eslint 不进行自己比较 */            if (process.env.NODE_ENV !== 'production' && customSetter) {
               customSetter()
           }            if (setter) {
               setter.call(obj, newVal)
           } else {
               val = newVal
           }
           childOb = !shallow && observe(newVal)
           dep.notify()
       }
   })
}

登录后复制

所以答案一直存在于文档中:

把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

我只想简单的了解 Object.defineProperty() 做了什么,所以我用一个例子简单的给你讲解一下:

var person2 = {
   firstName: 'George',
   lastName: 'Clooney',
};
Object.defineProperty(person2, 'fullName', {
   get: function () {        return `${this.firstName} ${this.lastName}`;
   },
});
console.log(person2.fullName); // 打印 "George Clooney"

登录后复制

还记得文章开头C#的隐式 getter 吗?它们看起来很类似,但ES5才开始支持。你需要做的是使用 Object.defineProperty() 定义现有对象,以及何时获取这个属性,这个getter被称为响应式——这实际上就是Vue在你添加新数据时背后所做的事。

Object.defineProperty()能让Vue变的更简化吗?

学完这一切,我一直在想,Object.defineProperty() 是否能让Vue变的更简化?现今越来越多的新术语,是不是真的有必要把事情变得过于复杂,变的让初学者难以理解(Redux也是同样):

Mutator:或许你在说(隐式)setter

Getters:为什么不用 Object.defineProperty() 替换成(隐式)getter

store.commit():为什么不简化成 foo = bar,而是 store.commit(“setFoo”, bar);?

你是怎么认为的?Vuex必须是复杂的还是可以像 Object.defineProperty() 一样简单?

相关推荐:

javascript用rem来做响应式开发实例分享

详解前端响应式布局、响应式图片,与自制栅格系统

响应式和自适应有什么区别

以上就是Vue.js响应式原理详解的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2786598.html

(0)
上一篇 2025年3月8日 17:47:11
下一篇 2025年3月5日 02:12:14

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • javascript中的隐式调用详解

    所谓的隐式调用简单来说就是自动调用一些方法,而这些方法像钩子一样可以在外部修改,从而改变既定行为。 下面我会列举一些最近看到的隐式调用,例子都是点到即止,欢迎补充 数据类型转换 toSting 和 valueOf var obj = { a…

    编程技术 2025年3月8日
    200
  • js之trim函数实现删除两端空格

    本文主要和大家介绍了js自定义trim函数实现删除两端空格功能,结合实例形式分析了javascript基于正则替换实现类似trim函数删除字符串两端空格的相关操作技巧,希望能帮助到大家。 兼容IE低版本浏览器,以及其他一些低版本脚本的浏览器…

    编程技术 2025年3月8日
    200
  • 详解JavaScript的运行原理

    本文主要给大家从理论详细分析了javascript运行原理以及知识点分享,对此有兴趣的学习下吧。 JavaScript是一种基于对象的动态、弱类型脚本语言(以下简称JS),是一种解释型语言,和其他的编程语言不同,如java/C++等编译型语…

    编程技术 2025年3月8日
    200
  • JavaScript比较同一天的时间大小

    在项目开发的过程中,有时候会遇到同一天内的时间大小比较,一般来说选择时间是通过插件实现的,但并不排除客户要求手动输入的情况。那么,在应客户要求手动输入时间,并且需要进行时间大小比较的时候该如何实现呢?以下简单介绍我实现的方法步骤: 1.首先…

    编程技术 2025年3月8日
    200
  • js正则表达式验证时间格式实例

    在项目中,我们经常会遇到时间问题,有时候是使用时间插件,让用户选择,但有时应客户要求,能够自行输入时间,那么,我们如何来确定用户输入的时间格式是否正确,输入的时间是否合法呢?这个时候需要用到正则表达式。本文主要和大家介绍了详解js正则表达式…

    编程技术 2025年3月8日
    200
  • JavaScript事件解析

    事件是在编程时系统内发生的动作或者发生的事情.系统通过它来告诉编程者,在编程者愿意的情况下,编程者一某种方式对它做出回应.本文主要和大家分享JavaScript事件解析,希望能帮助到大家。 添加事件方式 元素属性 var btn = doc…

    2025年3月8日
    200
  • JS实现图片轮播的实例

    本文主要为大家带来一篇使用js实现图片轮播的实例(前后首尾相接),代码参考了一位已经写好了图片轮播功能的(再次表示感谢),但是没有首尾相接的功能,本人在此基础上增加了首尾相接功能。 效果如下: nbsp;html PUBLIC “-//W3…

    2025年3月8日
    200
  • JavaScript数组进化与性能分析实例

    在使用 javascript 前,我对 c、c++、c# 这些已经颇为熟悉。与许多 c/c++ 开发者一样,javascript 给我的第一印象并不好。本文主要和大家介绍了javascript 数组的进化与性能分析,本文讲得更多的是内存、优…

    2025年3月8日
    200
  • js高效率服务器时间同步实例

    本文主要和大家分享一个js倒计时的功能代码,首先说一下,为什么要服务器时间同步, 因为服务器时间和本地电脑时间存在一定的时间差。有些对时效性要求非常高的应用,例如时时彩开奖,是不能容忍这种时间差存在的。 方案1:每次倒计时去服务端请求时间 …

    编程技术 2025年3月8日
    200
  • js中Date()日期函数浏览器兼容问题如何解决

    一般直接new date() 是不会出现兼容性问题的,而 new date(datetimeformatstring) 常常会出现浏览器兼容性问题,为什么,datetimeformatstring中的某些格式浏览器不兼容。本文主要和大家介绍…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论