在 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源码------------- 数据响应系统的基本思路的更多相关文章

  1. vue源码之响应式数据

    分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...

  2. Vue源码--解读vue响应式原理

    原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...

  3. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  4. [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅

    有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...

  5. vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...

  6. Vue 源码解读(3)—— 响应式原理

    前言 上一篇文章 Vue 源码解读(2)-- Vue 初始化过程 详细讲解了 Vue 的初始化过程,明白了 new Vue(options) 都做了什么,其中关于 数据响应式 的实现用一句话简单的带过 ...

  7. Vue源码解析---数据的双向绑定

    本文主要抽离Vue源码中数据双向绑定的核心代码,解析Vue是如何实现数据的双向绑定 核心思想是ES5的Object.defineProperty()和发布-订阅模式 整体结构 改造Vue实例中的dat ...

  8. vue 源码自问自答-响应式原理

    vue 源码自问自答-响应式原理 最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然. 所以打算把自己掌握的知识,试着组织成自己的语言表达出来 不打算平铺直叙的写清 ...

  9. 大白话Vue源码系列(05):运行时鸟瞰图

    阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...

随机推荐

  1. PHP curl_multi_select函数

    curl_multi_select — 等待所有cURL批处理中的活动连接 说明 int curl_multi_select ( resource $mh [, float $timeout = 1. ...

  2. (转)MAVEN的安装和Myeclipse+maven的配置和使用

    转:https://blog.csdn.net/shikongshengzhu/article/details/51779159 MAVEN的安装和Myeclipse+maven的配置和使用 步骤如下 ...

  3. python selenium模拟登陆163邮箱。

    selenium是可以模拟浏览器操作. 有些爬虫是异步加载的,通过爬取网页源码是得不到需要的内容.所以可以模拟浏览器去登陆该网站进行爬取操作. 需要安装selenium通过pip install xx ...

  4. Java学习之单例模式

    单例设计模式:解决一个类在内存中只存在一个对象思想:1.为了避免过多创建类的对象,禁止此类以外创建对象(构造方法私有化)2.为了类可以被使用就必须创建此类对象,只好在本类中,创建对象3.在此类中创建的 ...

  5. 监控软件munin安装设置

    准备工作需要web环境需要设置epel源 wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-6.repo 服务器端安装设置 y ...

  6. ng -v 不是内部或外部命令

    输入 npm install -g @angular/cli ng new my-app 提示ng -v 不是内部或外部命令 解决方法 1.安装过程中是否出错 原因可能是npm install -g  ...

  7. 数学&计算机工程常用希腊字母表及其发音

  8. 5.如何使用jmeter参数话

    参数化:简单的来理解一下,我们录制了一个脚本,这个脚本中有登录操作,需要输入用户名和密码,假如系统不允许相同的用户名和密码同时登录,或者更好的模拟多个用户来登录系统.这个时候就需要对用户名和密码进行参 ...

  9. java部署:CentOS 7下Tomcat安装与配置教程(Tomcat开机启动)

    一.前言 1.本教程主要内容 Tomcat安装与基础配置 Tomcat开机启动配置 2.本教程适用范围与环境信息 适用范围 软件/工具 版本说明 CentOS CentOS 7 Tomcat Tomc ...

  10. 硬RAID与软RAID的区别

    什么是RAID? RAID是英文Redundant Array of Independent Disks的缩写,翻译成中文即为独立磁盘冗余阵列,或简称磁盘阵列.简单的说,RAID是一种把多块独立的硬盘 ...