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 ...
随机推荐
- Struts2增删改查(自己思路理解)
1:查询所有: DAO层:把所有的信息都放到list集合中.然后返回. public List<Employee> getEmployees(){ return new ArrayList ...
- 探索Redis设计与实现4:Redis内部数据结构详解——ziplist
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- SAS 读取数据文件
每次读取数据时需要告诉SAS3件事:1:数据存在哪里?2:数据的形式3:创建的数据集的类型(永久/临时) 1 读取SAS数据集 DATA temp; /*temp 为创建的数据集名称*/ INFILE ...
- 【Java架构:基础技术】一篇文章搞掂:Idea
一.使用技巧 1.1.配置Maven 打开File-Settings打开设置界面 1.2.配置JDK JDK可以设置默认版本,也可以设置针对某个项目 分别对应File-Other Setting-De ...
- javaScript 通过location对象获取项目的url
项目中有些要通过jQuery 动态加载,其中需要一些路径,使用相对路径会出现错误,报 $("#t1").html("设置或返回从井号 (#) 开始的 URL(锚)---& ...
- memcached 安装与简单实用使用
一.简介 1.memcache与memcached的区别与联系: memcache是php的拓展,memcached是客户端,复杂的说:Memcache模块提供了于memcached方便的面向过程及面 ...
- Vue实现无痕刷新
一.什么是无痕刷新 在不刷新浏览器的情况下,实现页面的刷新. 传统的刷新页面方式 window.location.reload()原生 js 提供的方法 this.$router.go(0)vue 路 ...
- Spring 源码学习——Aop
Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...
- 把我坑惨的一个MySQL双引号!
来源:For DBA www.fordba.com/mysql-double-quotation-marks-accident.html 一.前言 最近经常碰到开发误删除误更新数据,这不,他们又给我找 ...
- windows下虚拟环境的安装与创建
使用虚拟环境的好处: 可以方便的解决不同项目对类库的依赖问题. 可以在系统中Python解释器中避免包的混乱和版本的冲突. 为每个程序单独创建虚拟环境,可以保证程序只能访问虚拟环境中的包,保持全局解释 ...