上一章 Vue2计算属性原理,我们介绍了计算属性是如何实现的?计算属性缓存原理?以及洋葱模型是如何应用的?

本章目标

  • 监听器是如何实现的?
  • 监听器选项 - immediate、deep 内部实现

初始化

在 Vue初始化实例的过程中,如果用户 options选项中存在侦听器,则初始化侦听器

// 初始化状态
export function initState(vm) {
const opts = vm.$options // 获取所有的选项 // 初始化监听器
if (opts.watch) { initWatch(vm) }
}

watch

类型:{ [key: string]: string | Function | Object | Array }

一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。

监听器初始化时,需要兼容 watch回调的各种类型。底层还是去调用vm.$watch创建一个监听器watch

function initWatch(vm) {
let watch = vm.$options.watch
for (let key in watch) {
const handler = watch[key] // handler有可能是 (字符串/对象/函数) 或 数组
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
} // handler 有可能是字符串、对象、函数
function createWatcher(vm, key, handler) {
let options = {}
// 兼容字符串
if (typeof handler === 'string') {
handler = vm[handler]
}
// 兼容对象
else if (Object.prototype.toString.call(handler) === '[object Object]') {
options = handler
handler = handler.handler
}
return vm.$watch(key, handler, options)
}

vm.$watch

vm.$watch( expOrFn, callback, [options] )

参数:{string | Function} expOrFn

观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值

我们会创建一个 侦听器watcher,标识user: true。如果 immediate选项值为 true,则立即执行一次callback回调

注:在 options选项中使用的侦听器,最终也是调用此方法

Vue.prototype.$watch = function (exprOrFn, cb, options = {}) {
options.user = true
// exprOrFn 可能是字符串firstname or 函数()=>vm.firstname
const watcher = new Watcher(this, exprOrFn, options, cb) // 立即执行
if(options.immediate){
cb.call(this, watcher.value, undefined)
}
}

侦听器Watcher

在初始化Vue实例时,我们会给每个侦听器都创建一个对应watcher(我们称之为侦听器watcher,除此之外还有 渲染watcher计算属性watcher ),他有一个 value 属性用于缓存侦听器观察的表达式的值

默认标识 user: true,用户的,代表侦听器watcher

exprOrFn,需要观察的表达式或者一个函数的计算结果,需要兼容字符串和函数两种类型

cb,侦听器回调函数。当观察的对象发生变化时,会触发dep.notify派发更新 并 调用 update 方法,然后再 run 方法中调用 cb 回调,其参数为新值和旧值

deep,侦听器选项之一,代表深度监听。我们需要在 get 方法中递归访问每一个子属性,这样就会保证所有子属性都会收集此侦听器watcher,这里我用了一个取巧的方法 - JSON.stringify ,源码在此

class Watcher {
constructor(vm, exprOrFn, options, cb) {
if (typeof exprOrFn === 'string') {
this.getter = function () {
return vm[exprOrFn]
}
} else {
this.getter = exprOrFn // getter意味着调用这个函数可以发生取值操作
} // 监听器watcher 用到的属性
this.user = options.user // 标识是否是用户自己的watcher
this.deep = options.deep
this.cb = cb
this.value = this.get() // 存储 get返回值
} get() {
pushTarget(this)
let value = this.getter.call(this.vm)
this.deep && JSON.stringify(value) // 深度监听
popTarget()
return value
} // 重新渲染
update() {
queueWatcher(this) // 把当前的watcher 暂存起来,异步队列渲染
} // queueWatcher 内部执行 run 方法
run() {
let oldValue = this.value
let newValue = this.value = this.get()
if (this.user) {
this.cb.call(this.vm, newValue, oldValue)
}
}
}

【Vue2.x源码系列07】监听器watch原理的更多相关文章

  1. 事件机制-Spring 源码系列(4)

    事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...

  2. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  3. SpringCloud 源码系列(6)—— 声明式服务调用 Feign

    SpringCloud 源码系列(1)-- 注册中心 Eureka(上) SpringCloud 源码系列(2)-- 注册中心 Eureka(中) SpringCloud 源码系列(3)-- 注册中心 ...

  4. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  5. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  6. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  7. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  8. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  9. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  10. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

随机推荐

  1. python.字典方法。第三天

    当需要为字典中的键设置默认值时,当该键没有任何值时使用它.代码如下 spam={'name':'Pooka','age':'5'} if 'color' not in sapm; spam['colo ...

  2. What is RSS

    What is RSS?RSS (Rich Site Summary) is a format for delivering regularly changing web content. Many ...

  3. 小米盒子TV变装魔法

    最近从一位台湾的朋友那里白嫖了一个 v2 节点, 恰好家里有一台家用的 小米盒子, 就寻思着能不能折腾一下, 共享上网 先将小米盒子开启adb调试, 参照这里: https://www.jb51.ne ...

  4. 搭建简单nfs共享

    1.查看是否安装nfs,rpcbind包 rpm -q nfs-utils rpm -q rpcbind   2.安装包 服务端和客户端 yum -y install nfs-utils 3.修改配置 ...

  5. uniapp使用阿里oss上传

    可以删除重新上传,点击图片大图等功能. <view class="uploadView"> <view class="imageView" v ...

  6. vulnhub靶场之HARRYPOTTER: ARAGOG (1.0.2)

    准备: 攻击机:虚拟机kali.本机win10. 靶机:HarryPotter: Aragog (1.0.2),下载地址:https://download.vulnhub.com/harrypotte ...

  7. 利用Intent在两个页面之间进行传值操作的具体实现

    不知道为什么,我本来使用的呼声最高的Bundle发送,但是我使用它会显示不出来,由于时间问题,我今天就先不找了,先放一下,先以完成任务为己任哈! 我们都清楚,我们基本上都是用的Intent实现的页面之 ...

  8. 11.4 显示窗口(harib08d)11.5 小实验(hearib08e) 11.6 高速计数器(harib08f)

    11.4 显示窗口(harib08d) 书P206 11.5 小实验(hearib08e) 书P208 11.6 高速计数器(harib08f) 书P209

  9. 【CS231n assignment 2022】 vscode 环境配置

    文章目录 前言 一.安装 Python 二.安装 vscode 三. cs231n 作业下载 四.配置 vscode 五.虚拟环境包安装 六.数据集 前言 今年成功考上了本校的研究生,要开始进入研究生 ...

  10. Django笔记一之运行系统、创建视图并访问

    从这一篇笔记开始将根据 Django 的官方文档,阅读整理之后,出一系列笔记教程,用作新手入门教程或者自己做查阅. 此次 Django 的版本为 3.2,且之后的一系列笔记都将以这个版本为基础,做功能 ...