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 ...
 
随机推荐
- POJ 3468 A Simple Problem with Integers(线段树区间修改及查询)
			
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...
 - Android中软键盘弹出时关于布局的问题
			
当在Android的layout设计里面如果输入框过多,则在输入弹出软键盘的时候,下面的输入框会有一部分被软件盘挡住,从而不能获取焦点输入. 解决办法: 方法一:在你的activity中的oncre ...
 - LUOGU P2617 Dynamic Rankings(树状数组套主席树)
			
传送门 解题思路 动态区间第\(k\)大,树状数组套主席树模板.树状数组的每个位置的意思的是每棵主席树的根,维护的是一个前缀和.然后询问的时候\(log\)个点一起做前缀和,一起移动.时空复杂度\(O ...
 - 前端每日实战:73# 视频演示如何用纯 CSS 创作一只卡通狐狸
			
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/OEKZed 可交互视频 此视频是可 ...
 - Redis入门很简单之一【简介与环境搭建】
			
Redis入门很简单之一[简介与环境搭建] 博客分类: NoSQL/Redis/MongoDB redisnosqlmemcached缓存中间件 [Redis简介] <一>. NoSQL ...
 - Python 工程师技能图谱skill-map
			
# **Python 工程师技能图谱** ## **入门**- 笨办法学Py- 官方教程- Hitchhiker- coursera * codeskulptor.org ## **进阶/脚本** # ...
 - ios 最全的常用字符串操作
			
1.将NSData / NSString转化 1 2 3 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; // 将字符串 ...
 - Stimulsoft_Report纯代码实现数据绑定
			
Stimulsoft_Report纯代码实现数据绑定 根据穿的参数动态绑定显示报表,涉及多表查询. 一.前台代码: [code] <%@ Page Title="" Lan ...
 - LightOJ 1248 Dice (III) (期望DP / 几何分布)
			
题目链接:LightOJ - 1248 Description Given a dice with n sides, you have to find the expected number of t ...
 - qcom_IMS_conference_call小结
			
1conference call建立 用QXDM抓qcom log: 1.拨号盘*#*#825364#*#* , 进入抓log UI 2.Connectivity-->USB Port ...