Vue源码------------- 数据响应系统的基本思路
在 Vue 中,我们可以使用 $watch 观测一个字段,当字段的值发生变化的时候执行指定的观察者,如下:
var vm = new Vue({
data: {
num:1
}
})
vm.$watch('num',function() {
console.log('num被修改')
})
这时候,当我们去修改 num 数值的时候,就会打印出来 'num被修改'。这个到底是如何实现,怎么打印出来的呢?
现在我们先以另一种方式,讲解期中的道理。关键一个知识点: Object.definePropert; 不了解的先打开这先看下
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
假设我们 有下边的数据
var data = {
num: 1
}
我们还有一个叫做 $watch 的函数,同时函数接受两个参数;第一个参数是要观测的字段,第二个参数是当该字段的值发生变化后要执行的函数,如下:
function $watch () {...}
$watch('num', () => {
console.log('修改了 num')
})
下边通过 Object.defineProperty 实现下边的功能:
Object.defineProperty(data, 'num', {
set () {
console.log('设置了num')
},
get () {
console.log('读取了 num')
}
})
通过 Object.defineProperty 我们可以轻松知道 num 被设置,和读取了。但问题是如何让$watch 方法知道,同时通知第二个参数函数呢?
有了上边的想法,我们就可以大胆地思考一些事情,比如: 能不能在获取属性 num 的时候收集依赖,然后在设置属性 num的时候触发之前收集的依赖呢?
// dep 数组就是我们所谓的“筐”
const dep = []
Object.defineProperty(data, 'num', {
set () {
// 当属性被设置的时候,将“筐”里的依赖都执行一次
dep.forEach(fn => fn())
},
get () {
// 当属性被获取的时候,把依赖放到“筐”里
dep.push(fn)
}
})
上边的 fn 来自哪里? 又是在什么时候出发num 属性的get() 呢?
接下来需要在$watch()上下手:
// fn 是全局变量
let fn= null
function $watch (exp, callback) {
// 将 fn 的值设置为 callback
fn = callback
// 读取字段值 exp,触发 get 函数
data[exp]
}
通过上边调用$watch 方法,先给全局变量fn 设置为回调函数,然后读取data的属性,num属性的get方法中,收集callback, 这样当num 变化时候可以通知callback方法;
上边的方法还有几个问题需要思考:
1. 实现多个属性监听;2. data 某个属性字段是对象时,3. 确定属性值发生变化,才去出发回调;
要解决上述问题又要怎么去做呢? 下边封装一个方法:
function observe(data) {
for (let key in data) {
const dep = []
let val = data[key]
// 如果 val 是对象,递归调用 observe 函数将其转为访问器属性
const nativeString = Object.prototype.toString.call(val)
if (nativeString === '[object Object]') {
observe(val)
}
Object.defineProperty(data, key, {
set:function setter (newVal) {
if (newVal === val) return
val = newVal
dep.forEach(fn => fn())
},
get:function getter () {
dep.push(fn)
return val
}
})
}
}
observe(data)
Vue中$watch方法第一个参数可以是 data 中的某个属性,function, 以及data属性中 对象的属性 ; 那么这个watch是如何实现呢? 下边我们改变下$watch();
function $watch (exp, callback) {
fn= fcallback
let pathArr,
obj = data
if (typeof exp === 'function') {
exp()
return
}
// 检查 exp 中是否包含 .
if (/\./.test(exp)) {
// 将字符串转为数组,例:'a.b' => ['a', 'b']
pathArr = exp.split('.')
// 使用循环读取到 data.a.b
pathArr.forEach(p => {
obj = obj[p]
})
return
}
data[exp]
}
先判断第一个参数 时候为function ,如果为function,则直接调用第一个参数;如果为obj.a 等形式;则进行split分割一层层出发,收集fn;
最后完整版下如下:
var fn = null;
var data = {names:"xiaoming", age:19,obj: {a:1,b:2,c:{c:1,d:2}}}
function observe (data) {
for (let key in data) {
const dep = []
let val = data[key]
// 如果 val 是对象,递归调用 observe 函数将其转为访问器属性
const nativeString = Object.prototype.toString.call(val)
if (nativeString === '[object Object]') {
observe(val)
}
Object.defineProperty(data, key, {
set: setter(newVal) {
if (newVal === val) return
val = newVal
dep.forEach(fn => fn())
},
get: getter() {
dep.push(fn)
return val
}
})
}
} observe(data) function $watch (exp, callback) {
fn = callback
let pathArr,
obj = data
if (typeof exp === 'function') {
exp()
return
}
// 检查 exp 中是否包含 .
if (/\./.test(exp)) {
// 将字符串转为数组,例:'a.b' => ['a', 'b']
pathArr = exp.split('.')
// 使用循环读取到 data.a.b
pathArr.forEach(p => {
obj = obj[p]
})
return
}
data[exp]
} $watch('names',function() {
console.log('name change')
})
运行:在改变 data.names = '小明';
结果:
当然Vue实现肯定不会如此简单,接下来有空慢慢细讲,(*^▽^*)
Vue源码------------- 数据响应系统的基本思路的更多相关文章
- vue源码之响应式数据
分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...
- Vue源码--解读vue响应式原理
原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...
- 学习 vue 源码 -- 响应式原理
概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...
- [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅
有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...
- vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]
其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...
- Vue 源码解读(3)—— 响应式原理
前言 上一篇文章 Vue 源码解读(2)-- Vue 初始化过程 详细讲解了 Vue 的初始化过程,明白了 new Vue(options) 都做了什么,其中关于 数据响应式 的实现用一句话简单的带过 ...
- Vue源码解析---数据的双向绑定
本文主要抽离Vue源码中数据双向绑定的核心代码,解析Vue是如何实现数据的双向绑定 核心思想是ES5的Object.defineProperty()和发布-订阅模式 整体结构 改造Vue实例中的dat ...
- vue 源码自问自答-响应式原理
vue 源码自问自答-响应式原理 最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然. 所以打算把自己掌握的知识,试着组织成自己的语言表达出来 不打算平铺直叙的写清 ...
- 大白话Vue源码系列(05):运行时鸟瞰图
阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...
随机推荐
- Spring解决循环依赖
1.Spring解决循环依赖 什么是循环依赖:比如A引用B,B引用C,C引用A,它们最终形成一个依赖环. 循环依赖有两种 1.构造器循环依赖 构造器注入导致的循环依赖,Spring是无法解决的,只能抛 ...
- hdu1059&poj1014 Dividing (dp,多重背包的二分优化)
Problem Description Marsha and Bill own a collection of marbles. They want to split the collection a ...
- 大碗宽面Beta迭代阶段博客目录
大碗宽面Beta迭代阶段博客目录 Githhub:https://github.com/rz-2000/Course-Evaluation 一.Scrum Meeting 1. [第十周会议记录]ht ...
- CDN技术之--全局负载均衡(GSLB)
负载均衡就是智能调度全局负载均衡(GSLB)的负载均衡主要是在多个节点之间进行均衡,其结果可能直接终结负载均衡过程,也可能将用户访问交付下一层次的(区域或本地)负载均衡系统进行处理.GSLB最通用的是 ...
- zabbix历史数据全部清楚
#这种方法会出现 监控项不可用的情况 1.停掉zabbix_server 2.重重名表 RENAME TABLE history to history_20180117; RENAME TABLE h ...
- java动态代理的原理
在许多mvc框架中,经常用到注解来实现面向切面(aop)的编程.面向切面编程,可以对业务逻辑中各部分进行分离,提高程序的重用性,降低各逻辑业务部分的耦合度. jdk中利用反射原理使用Proxy类对对象 ...
- Vue的安装和使用详解(一)
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与 ...
- 97、PageRank算法学习
最近由于.......你懂得,需要一些搜索方面的知识,于是乎我重新复习了一下上半年读的那本书<数学之美>Dr吴军老师写的. 感觉读完这种书还是写一下比较好,因为将来说不定就会忘记了. 接下 ...
- 使用JQuery对页面进行绑值
使用JQuery对页面进行绑值 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&g ...
- spring boot找不到或无法加载主类 io.renren.RenrenApplication
spring boot找不到或无法加载主类 io.renren.RenrenApplication 出现问题: spring boot 项目以前一直是好好的,用mvn clean package 打包 ...