您好,欢迎访问一九零五行业门户网

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!',
};
}
})<div>{greeting}</div>

显然它会在用户界面打印出 “hello world!”。
现在,如果你改变greeting的值,vue引擎会对此作出反应并相应地更新视图。
methods: {
onsomethingclicked() { this.greeting = "what's up";
},
}

很长一段时间我都在想,它是如何工作的?当某个对象的属性发生变化时会触发某个事件?或者vue不停的调用 setinterval 去检查是否更新?
通过阅读vue官方文档,我才知道,改变一个对象属性将隐式调用getter/setter,再次通知观察者,然后触发重新渲染,如下图,这个例子来自官方的vue.js文档:
但我还想知道:
怎么让数据自带getter/setters?
这些隐式调用内部是怎样的?
第一个问题很简单:vue为我们准备好了一切。当你添加新数据,vue将会通过其属性为其添加 getter/setters。但是我让 foo.bar = 3? 会发生什么?
这个问题的答案出现在我和svg & vue专家sarah drasner的twitter对话中:
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响应式原理详解的详细内容。
其它类似信息

推荐信息