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 ...
随机推荐
- ThinkPHP示例:图片上传
ThinkPHP示例之图片上传,包括图片上传.数据库保存.缩略图生成以及图片水印功能演示.首先需要下载框架核心,然后把示例解压到Web根目录下面,并修改入口文件中的框架入口文件的位置.导入示例目录下面 ...
- Sqli labs系列-less-5&6 报错注入法(上)
在我一系列常规的测试后发现,第五关和第六关,是属于报错注入的关卡,两关的区别是一个是单引号一个是双引号...当然我是看了源码的.... 基于报错注入的方法,我早就忘的差不多了,,,我记的我最后一次基于 ...
- vue 自定义指令(directive)实例
一.内置指令 1.v-bind:响应并更新DOM特性:例如:v-bind:href v-bind:class v-bind:title v-bind:bb 2.v-on:用于监听DOM事件: 例 ...
- js 中 !!的用法
!!是将表达式强制转化为bool值的运算,运算结果为true或false,表达式是什么值,结果就是对应的bool值,不再取非. 不是取非再取非的意思!!! !!false=false; 要注意f ...
- POJ 3641 Pseudoprime numbers (数论+快速幂)
题目链接:POJ 3641 Description Fermat's theorem states that for any prime number p and for any integer a ...
- Linux(二)—— Linux配置及指令
目录 Linux配置及指令 一.linux中常用软件的安装 二.主机名和网络 1.修改主机名 2.设置网络 三.关闭防火墙 1.检查防火墙是否开启 2.清除策略 3.永久关闭第一个防火墙 4.关闭第二 ...
- 06、python的基础-->编码小知识
1.编码类型ascii A : 00000010 8位 一个字节 unicode A : 00000000 00000001 00000010 00000100 32位 四个字节 中:00000000 ...
- 关于 AfxGetStaticModuleState ()
写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上AFX_MANAGE_STATE (AfxGetStaticModuleState()).一直不明白这样做的 ...
- ActiveX (ocx) 控件 在vs2010 上debug 的方法
1.在项目的属性中配置Debug,设置调试选项卡中的“命令”.“命令参数”.“工作目录”,“命令”为IE的路径,“命令参数”为自己写的htm页面路径(因htm中未配置ocx路径,所以直接把htm放在了 ...
- loj6626 幼儿园唱歌题
题目 不难想到把\(S\)的反串\(S^R\)接到\(S\)后面,这样就可以把\(S[l_1,r_1]\)的前缀转化为\(S^R[n-r_1+1,n-l_1+1]\)的后缀 回文树上两节点的lca就是 ...