模拟源码深入理解Vue数据驱动原理(2)
2023-09-11 14:19:55 时间
我们说到如果监听的属性是个对象呢?那么这个对象中的其他属性岂不就是监听不了了吗?如下:
倘若user中的name、age属性变化,如何知道它们变化了呢?今儿,就来解决这一问题。
通过走读Vue源码,发现他是利用Observer构造函数为每个对象创建一个Observer对象,来监听数据的,如果数据中的属性又是一个对象,那么就又通过Observer来监听嘛。
其实,核心思想就是树的先序遍历。如我们将上述Demo中的data数据,图形化一下,就更加明白了,如图:
理清了大体思路,下面我们就一起来创建一个Observer。
Observer整体结构如下:
function Observer(data) {
//如若this不是Observer对象,即创建一个
if (!(this instanceof Observer)) {
return new Observer(data);
}
this.data = data;
this.walk(data);
}
let p = Observer.prototype = Object.create(null);
p.walk = function(data) {
/*
TODO:监听data数据中的所有属性,
并查看data中属性值是否为对象,
若为对象,就创建一个Observer实例
*/
}
p.convert = function(key, val) {
//TODO:通过Object.defineProperty监听数据
}
walk
首先,我们在walk方法中实现对data对象中的所有属性监听,如下:
p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
this.convert(key, val);
});
}
由于属性中可能又会是一个对象,那么,我们就有必要监听它们。怎么办呢?如果是个对象,再次利用Observer构造函数,处理它不就完了么。如下:
p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
//如果val为对象,则交给Observer处理
if (typeof val === 'object') {
Observer(val);
}
this.convert(key, val);
});
}
你可能会有这样的疑问,如果直接利用Observer处理对象,那么不就与父对象失去关联了么?
然而并没有,因为JavaScript对于对象是指向地址关系,所以怎么会失去关联呢。
convert
对于convert方法,就比较简单了,一如既往就是利用Object.defineProperty监听数据,如下:
p.convert = function(key, val) {
Object.defineProperty(this.data, key, {
get: () => {
console.log('访问了' + key + ' 值为' + val);
return val;
},
set: (newVal) => {
console.log('设置了' + key + ' 值为' + newVal);
if (newVal !== val) {
val = newVal;
}
}
});
}
完整代码:
function Observer(data) {
//如若this不是Observer对象,即创建一个
if (!(this instanceof Observer)) {
return new Observer(data);
}
this.data = data;
this.walk(data);
}
let p = Observer.prototype = Object.create(null);
p.walk = function(data) {
let keys = Object.keys(data);
keys.forEach(key => {
let val = data[key];
//如果val为对象,则交给Observer处理
if (typeof val === 'object') {
Observer(val);
}
this.convert(key, val);
});
}
p.convert = function(key, val) {
Object.defineProperty(this.data, key, {
get: () => {
console.log('访问了' + key + ' 值为' + val);
return val;
},
set: (newVal) => {
console.log('设置了' + key + ' 值为' + newVal);
if (newVal !== val) {
val = newVal;
}
}
});
}
使用:
<script>
let data = {
user: {
name: 'Monkey',
age: 24
},
lover: {
name: 'Dorie',
age: 23
}
};
Observer(data);
</script>
相关文章
- (尚031)Vue_案例_自定义事件(组件间通信第2种方式:vue自定义事件)
- 【Vue/js】通过localStorage浏览器实现变量和对象的本地缓存(图文+完整源代码)
- vue 的 起手式
- Vue组件定义
- 详解vue全局组件与局部组件使用方法
- vue 3.0 项目搭建移动端 (二) Vue-router: router-link 与 router-view keep-alive
- vue各种插件汇总
- Vue项目 跨域 解决方案与 vue.config.js 配置解析
- 用 vue cli 脚手架搭建单页面 Vue 应用(进阶2)
- 轻松解决【Vue】:Uncaught SyntaxError: Unexpected token ‘<‘
- vue-waves-点击容器出现水波纹效果
- 浅析Vue数据更新了但页面不更新的7种情况及vue异步更新带来的数据响应的误解
- 在 vue项目使用base64加密
- npm 报错之 vue项目中stylus-loader版本兼容问题(stylus(css预处理框架))
- Vue 之 mockjs 结合 axios 在 vue 中的随机数据生成的简单使用
- Vue 之 vue-seamless-scroll 实现简单自动无缝滚动,且添加对应点击事件的简单整理
- spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发
- vue组件参数校验
- Vue.js 监听属性
- 浅谈vue中插件的使用方法Vue.use(xxx),原理及实现
- vue响应式依赖的两种技术之-发布订阅模式的实现
- vue 登录页背景-粒子特效(Vue-Particles)