在文章 源码学习VUE之响应式原理我们大概描述了响应式的实现流程,主要写了observe,dep和wather的简易实现,以及推导思路。但相应代码逻辑并不完善,今天我们再来填之前的一些坑。

Observe

之前实现的observe函数只能处理一个对象的单个属性,但我们更多的数据是保存在对象中,为了抽象话,我们也封装一个对象Observe,只要传进一个参数,就可以把这个对象进行监听。

对现有所有属性进行监听

var obj = {
a: 1,
b: 2
}

比如一个对象有两个属性 a,b。我们可以尝试写出下面的实现类

class Observe{
constructor(value){
this.value = value //要监听的值。
this.walk();
} walk(){ //通过walk函数,依次处理
const keys = Object.keys(obj);
let self = this;
for (let i = 0; i < keys.length; i++) {
self.defineReactive(obj, keys[i])
}
} defineReactive (data, key, val) {
var dep = new Dep();
Object.defineProperty(obj, a, {
enumerable: true,
configurable: true,
get: function(){
if(Dep.target){
dep.addSub(Dep.target); // Dep.target是Watcher的实例
}
},
set: function(newVal){
if(val === newVal) return
val = newVal;
dep.notify();
}
})
}
}

当然,为了防止重复监听,我们可以给原object设置一个标识符以作辨别。

class Obsever(){
construct(){
this.value = value //要监听的值。
Object.defineProperty(value, "__ob__", {
value: this,
enumerable: false,
writable: true,
configurable: true
})
this.walk();
}
}

监听数组

虽然数组也是一个对象,但是我们队数组的操作却不会触发set,get方法。因此必须对数组特殊处理。
首先需要对操作数组的方法进行改写,如push,pop,shift

//首先拿到Array的原生原型链
const arrayProto = Arrary.prototype;
//为了保证修改不会影响原生方法,我们创建一个新对象
const arrayMethods = Object.create(arrayProto);
//要改写的方法
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']
methodsToPatch.forEach(function (method) {
const original = arrayProto[method] // 先拿到原生方法
def(arrayMethods, method, function mutator (...args) {
// 改写后的方法,都是先拿到原生方法的计算结果
const result = original.apply(this, args)
const ob = this.__ob__
// 拿到插入的值。
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
//Observe插入的值
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})

其实逻辑很简单。对于可以改变array的方法我们都改写一下。只要调用了这些方法,除了返回正确的值,我们都通知观察对象,数据改变了,触发观察者update操作。同时,数组里面可能是个对象,我们不改变数组本身,但是改变数组里面的某个值,这也算是一种改变,因此,除了监听数组本身的改变,也要对数组每个值进行observe。
这涉及到两点,一是observe Array的时候,就要对每个值进行Observe。另外,插入数组的每个值也要observe.第二点就是上面代码中特别关注push,unshift,splice这三个可以插值方法的原因。

class Obsever(){
construct(){
this.value = value //要监听的值。
Object.defineProperty(value, "__ob__", {
value: this,
enumerable: false,
writable: true,
configurable: true
})
if(Array.isArray(value)){
this.observeArray();
}else{
this.walk();
}
}
observeArray(items){
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}, function observe (value) {
let ob
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
// 如果已经observe的对象就不再进行重复的observe操作
ob = value.__ob__
} else {
ob = new Observer(value)
}
return ob
}

优化

实际开发中我们经常会遇到一个很大的数据。如渲染tables时,table的数据很可能很大(一个多多维数组)。如果都进行observe无意会是很大的开销。关键是我们只是需要拿这些数据来渲染,并不关心数据内部的变化。因此可能就存在这种需求,可以不对array或object深层遍历observe。我们可以使用Object.freeze()将这个数据冻结起来。
因此对于冻结的数据我们就不再进行observe。上面的代码可以这么优化

function observe (value) {
let ob
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
// 如果已经observe的对象就不再进行重复的observe操作
ob = value.__ob__
} else if(Object.isExtensible(value)){// 如果数据被冻结,或者不可扩展,则不进行observe操作
ob = new Observer(value)
}
return ob
} defineReactive (data, key, val) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key)
// 如果数据被冻结,或者不可扩展,则改写set,get方法
if (property && property.configurable === false) {
return
}
//传进来的对象可能之前已经被定义了set,get方法,因此我们不能直接拿value
var getter = property && property.get
var setter = property && property.set
Object.defineProperty(obj, a, {
enumerable: true,
configurable: true,
get: function(){
var value = getter ? getter.call(obj) : val;
if(Dep.target){
dep.addSub(Dep.target); // Dep.target是Watcher的实例
}
return value
},
set: function(newVal){
if(val === newVal) return
val = newVal;
dep.notify();
}
})
}

源码学习VUE之Observe的更多相关文章

  1. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

  2. 【Vue源码学习】响应式原理探秘

    最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...

  3. 【Vue源码学习】依赖收集

    前面我们学习了vue的响应式原理,我们知道了vue2底层是通过Object.defineProperty来实现数据响应式的,但是单有这个还不够,我们在data中定义的数据可能没有用于模版渲染,修改这些 ...

  4. Vue源码学习(一):调试环境搭建

    最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...

  5. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

  6. Vue源码学习二 ———— Vue原型对象包装

    Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...

  7. Vue.js 源码学习笔记

    最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...

  8. 如何实现全屏遮罩(附Vue.extend和el-message源码学习)

    [Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习) 在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(饿了么) ...

  9. 最新 Vue 源码学习笔记

    最新 Vue 源码学习笔记 v2.x.x & v3.x.x 框架架构 核心算法 设计模式 编码风格 项目结构 为什么出现 解决了什么问题 有哪些应用场景 v2.x.x & v3.x.x ...

随机推荐

  1. UC接口文档

    UC接口文档 一.功能描述 提供同步登录.退出.注册等相关接口,可以实现用户一个账号,在一处登录,全站通行. 二.测试环境UC地址 http://s1.p5w.net/uc/ 三.相关接口 UC_AP ...

  2. c#声明数组

    声明二维数组时候,出现报错: string[][] dataTable; dataTable = new string[rows][cols]; 无效的秩说明符: 应为","或&q ...

  3. MongoDB查询mgov2的聚合方法

    1.多条表数据累计相加. respCount := struct { Rebatescore int64 //变量命名必须要和查询的参数一样.}{} o := bson.M{"$match& ...

  4. String 对象-->toLowerCase() 方法

    1.定义和用法 将字符串中所有的大写字符转换成小写字符,小写字符不变 返回转换后的结果字符串 语法: string.toLowerCase() 注意:不会改变字符串本身,仅以返回值的形式返回结果 举例 ...

  5. Spire.Cloud 私有化部署教程(二)- Ubuntu 18.04 系统

    本教程主要介绍如何在Ubuntu 18.04系统上实现Spire.Cloud私有化部署.CentOS 7系统部署请参考 这篇教程. 详细步骤如下: 一.环境配置 1.关闭防火墙 1)首先查看防火墙状态 ...

  6. hive常用函数五

    复合类型构建操作 1. Map类型构建: map 语法: map (key1, value1, key2, value2, …) 说明:根据输入的key和value对构建map类型 举例: hive& ...

  7. Visual C++ 6.0踩坑记录---在Win10下安装Visual C++ 6.0安装成功后点击“打开”按钮闪退问题

    前言: 为了更好的学习C及C++,前段时间下载了Microsoft Visual C++ 6.0(以下简称VC6),原因是VC6具有查看反汇编代码.监视内存.寄存器等功能,并且因为本人正在学习滴水逆向 ...

  8. 王者荣耀英雄全皮肤4K高清大图,python爬虫帮你保存下来

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取t.cn ...

  9. python从零开始基础入门——开发环境搭建:Visual Studio Code

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:山海皆可平z PS:如有需要Python学习资料的小伙伴可以加点击下方 ...

  10. H - Bear and Three Balls

    Limak is a little polar bear. He has n balls, the i-th ball has size ti. Limak wants to give one bal ...