写在前面
相信大家都知道,vue实现数据双向绑定无非就是采用数据劫持的方式,结合发布订阅模式,通过Object.defineProperty()来劫持各个属性的setter,getter以监听属性的变动,在数据变动时发布消息给订阅者,以上是vue2.x的实现原理,3.0的话就是proxy替换Object.defineProperty(),其他流程没有改变。总结一下数据相应原理就发布订阅模式(观察者模式)+ 数据劫持(defineProperty or proxy)
什么是观察者模式?
通常又被称为 发布-订阅者模式 或 消息机制,它定义了对象间的一种一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题。
文字繁琐,看名字高大尚,我们举个生活中栗子 平时想了解某个服装品牌的信息,但是又不想老是去官网查看,于是偷偷关注了公众号(订阅者),公众号有最新产品产品上市就推送消息给你(发布者),没错这个就是了
我们工作中也有经常看到,比如:
document.querySelector('#btn').addEventListener('click',function () {
alert('You click this btn');
},false)
简单代码实现
const EventHub = {
_messager: {},
on (type, fn) {
if (this._messager[type] === undefined) {
this._messager[type] = [fn]
} else {
this._messager[type].push(fn)
}
},
emit (type, arv) {
const params = {
type,
arv: arv || {}
}
const len = this._messager[type]
for (let i = 0; i < len.length; i++) {
const fn = len[i];
fn.call(this, params)
}
}
}
EventHub.on('say', function (data) {
console.log(data.arv.text)
})
EventHub.on('say', function (data) {
console.log('hhhhh')
})
EventHub.on('hi', function () {
console.log('hi,小田田')
})
setTimeout(() => {
// 发布
EventHub.emit('say', {text: 'ys大佬们,我要发布信息了'})
EventHub.emit('hi')
}, 3000)
观察者模式使用
最熟悉的莫过于我们每天使用vue框架了,其中数据的双向绑定就是利用 Object.defineProperty() 和观察者模式实现的。
看看简单实现代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="demo"></div>
<input type="text" id="inp">
<script>
var obj = {};
var demo = document.querySelector('#demo')
var inp = document.querySelector('#inp')
Object.defineProperty(obj, 'name', {
get: function() {
return val;
},
set: function (newVal) {//当该属性被赋值的时候触发
inp.value = newVal;
demo.innerHTML = newVal;
}
})
inp.addEventListener('input', function(e) {
// 给obj的name属性赋值,进而触发该属性的set方法
obj.name = e.target.value;
});
obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
</script>
</body>
</html
当然真正的实现过程是复杂的,是这样子的
vue2.x vs vue3 响应式原理对比
vue2.x
Vue.prototype.observe= function(obj){
var value;
var that = this;
for(var key in obj){
value = obj[key];
if(typeof value === 'object'){
this.observe(value);
}else{
Object.defineProperty(this.$data,key,{
get:function(){
return value
},
set:function(newValue){
value = newValue;
self.render();
}
})
}
}
}
vue3.0
Vue.prototype.observe= function(obj){
var value;
var that = this;
// 用proxy就不需要for in了,即监听的颗粒度减小了,拿到的信息更多了。
this.$data = new Proxy(obj,{
get:function(target,key,reveive){
return target[key];// 不需要中间变量了
},
set: function (target, key, newValue,reveive){
target[key] = newValue;
}
})
}
Vue 3.0与Vue 2.0的区别仅是数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变。
Proxy 可以做哪些有意思的事情?
等不及可选链:深层取值(get)
平时取数据的时候,经常会遇到深层数据结构,如果不做任何处理,很容易造成 JS 报错。 为了避免这个问题,也许你会用多个 && 进行处理:
const country = {
name: 'china',
province: {
name: 'guangdong',
city: {
name: 'shenzhen'
}
}
}
const cityName = country.province
&& country.province.city
&& country.province.city.name;
最新的 ES 提案中提供了可选链的语法糖,支持我们用下面的语法来深层取值。
country?.province?.city?.name
proxy的实现
function noop() {}
function get (obj) {
// 注意这里拦截的是 noop 函数
return new Proxy(noop, {
// 这里支持返回执行的时候传入的参数
apply(target, context, [arg]) {
return obj;
},
get(target, prop) {
return get(obj[prop]);
}
})
}
const obj = {
person: {}
}
get(obj)() === obj; // true
get(obj).person.name(); // undefined
管道
在最新的 ECMA 提案中,出现了原生的管道操作符 |>,在 RxJS 和 NodeJS 中都有类似的 pipe 概念。 使用 Proxy 也可以实现 pipe 功能,只要使用 get 对属性访问进行拦截就能轻易实现,将访问的方法都放到 stack 数组里面,一旦最后访问了 execute 就返回结果。
const pipe = (value) => {
const stack = [];
const proxy = new Proxy({}, {
get(target, prop) {
if (prop === 'execute') {
return stack.reduce(function (val, fn) {
return fn(val);
}, value);
}
stack.push(window[prop]);
return proxy;
}
})
return proxy;
}
var double = n => n * 2;
var pow = n => n * n;
pipe(3).double.pow.execute
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!