关于javascript:vue的双向绑定的原理是什么

vue.js是采纳数据劫持联合发布者-订阅者模式的形式,通过Object.defineProperty()或者Proxy来劫持各个属性的setter,getter,在数据变动时公布音讯给订阅者,触发相应的监听回调。

Vue2.0整体思路

外围:通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目标

Vue遍历data对象所有的属性,并应用 Object.defineProperty 把这些属性全副转为 getter/setter,在属性被拜访和批改时告诉变更。

通过如下代码实现对属性的监听:

var data = {name: 'Pat'};
observe(data);
data.name = 'PatWu'; // 哈哈哈,监听到值变动了 kindeng --> dmq
function observe(data) {
 if (!data || typeof data !== 'object') { return; } // 遍历所有属性 Object.keys(data).forEach(function(key) {
 defineReactive(data, key, data[key]);
 });};
function defineReactive(data, key, val) {
 observe(val); // 递归解决子属性
 Object.defineProperty(data, key, {
 enumerable: true,
 configurable: false,
 get: function() {
 console.log('监听到了取值: ', val);
 return val; }, set: function(newVal) {
 console.log('监听到了值变动: ', val, ' --> ', newVal);
 val = newVal; } });}

在渲染过程中会对 vm 上的数据拜访,这个时候会触发了据对象的 getter。那么每个对象值的 getter 都持有一个 dep,在触发 getter 的时候会调用 dep.depend() 办法,
从而实现依赖的收集:

// ...省略
function defineReactive(data, key, val) {
 observe(val);
 var dep = new Dep(); // 实例化依赖收集对象
 Object.defineProperty(data, key, {
 get: function() {
 // 依赖收集 dep.depend(); // Dep.target.addDep(this) return val; } // ... 省略 });}

Dep.target 渲染过程中曾经被赋值为渲染 watcher,那么就执行到 addDep 办法, 接着把以后的 watcher 订阅到这个数据持有的 dep 的 subs 中,
这个目标是为后续数据变动时候能告诉到哪些 subs 做筹备。

// watcher.js
function addDep(dep) {
 // ...省略 dep.addSub(this)}

收集依赖并监听到变动之后就是怎么告诉订阅者了,数据变动触发数据对象的setter,setter中会执行dep.notify(),再调用订阅者的update办法实现数据更新渲染。

// ... 省略
function defineReactive(data, key, val) {
 observe(val);
 var dep = new Dep(); // 实例化依赖收集对象
 Object.defineProperty(data, key, {
 // ... 省略 set: function(newVal) {
 if (val === newVal) return; console.log('监听到了值变动: ', val, ' --> ', newVal);
 val = newVal; dep.notify(); // 告诉所有订阅者
 } });}
function Dep() {
 this.subs = [];}
Dep.prototype = { addSub: function(sub) {
 this.subs.push(sub);
 }, notify: function() {
 this.subs.forEach(function(sub) {
 sub.update(); // 触发更新渲染
 }); }};

Vue3.0实现形式

Vue 3.0与Vue 2.0的区别仅是数据劫持的形式由Object.defineProperty更改为Proxy代理,其余代码不变。

function observer(data) {
 const that = this; for(var key in data){ that.deps[key] = []; //初始化所有订阅者对象{msg: [订阅者], info: []} } let handler = { get(target, property) {
 return target[property]; }, set(target, key, value) {
 let res = Reflect.set(target, key, value);
 var watchers = that.deps[key]; watchers.map(item => {
 item.update();
 }); return res; } } // 通过Proxy实现对数据的劫持 this.$data = new Proxy(data, handler);
}

Object.defineProperty 和 Proxy的优缺点:
1.Object.defineProperty的毛病
不能监听数组:因为数组没有getter和setter,因为数组长度不确定,如果太长性能累赘太大
只能监听属性,而不是整个对象,须要遍历循环属性
只能监听属性变动,不能监听属性的删减
2.proxy的益处
能够监听数组
监听整个对象不是属性
13种来截办法,弱小很多
返回新对象而不是间接批改原对象;
3.proxy的毛病
兼容性不好,而且无奈用polyfill磨平;

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理