vue2源码解析---watch和computed

监听属性watch

监听属性介绍

我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数wach 可以用于异步任务

监听属性的初始化

watch和computed都先走initSate判断传入选项

export function initState(vm) {const opts = vm.$options; // 获取所有的选项if (opts.data) {initData(vm);}if (opts.computed) {initComputed(vm);}if (opts.watch) {initWatch(vm);}
}

接下来initWatch进入此函数

function initWatch(vm){let watch = vm.$options.watch;for(let key in watch){const handler = watch[key]; // 字符串 数组 函数if(Array.isArray(handler)){for(let i = 0; i < handler.length;i++){createWatcher(vm,key,handler[i]);}}else{createWatcher(vm,key,handler);}}}

通过原型方法$watch传入处理参数创建一个观察者收集依赖变化。

function createWatcher(vm,key,handler){// 字符串  函数if(typeof handler === 'string'){handler = vm[handler];}return vm.$watch(key,handler)
}

原型上的$watch函数

export function initStateMixin(Vue) {Vue.prototype.$nextTick = nextTick;// 最终调用的都是这个方法Vue.prototype.$watch = function (exprOrFn, cb) {// firstname// ()=>vm.firstname// firstname的值变化了 直接执行cb函数即可new Watcher(this, exprOrFn, { user: true }, cb)}
}

计算属性computed

计算属性介绍

计算属性是在 Vue 实例的computed选项中定义的,可以是一个函数或具有get和set方法的对象。函数形式的计算属性会在调用时被执行,而对象形式的计算属性则可以提供自定义的get和set方法
计算属性适用于那些依赖其他响应式数据的场景,而不适用于需要进行异步操作或有副作用的场景。对于这些情况,可以使用侦听器(watcher)或使用methods来处理。

计算属性实现过程

计算属性的初始化

1 在initComputed函数中,遍历计算属性对象,为每个计算属性创建一个Watcher实例,并将其存储在vm._computedWatchers中。

export function initState(vm) {const opts = vm.$options; // 获取所有的选项if (opts.data) {initData(vm);}if (opts.computed) {initComputed(vm);}if (opts.watch) {initWatch(vm);}
}
function initComputed(vm) {const computed = vm.$options.computed;const watchers = vm._computedWatchers = {}; // 将计算属性watcher保存到vm上for (let key in computed) {//获取用户定义的计算属性let userDef = computed[key];// 我们需要监控 计算属性中get的变化let fn = typeof userDef === 'function' ? userDef : userDef.get// 如果直接new Watcher 默认就会执行fn, 将属性和watcher对应起来 watchers[key] = new Watcher(vm, fn, { lazy: true })defineComputed(vm, key, userDef);}
}
属性劫持

2 defineComputed 方法主要是重新定义计算属性,其实最主要的是劫持get方法。
为啥要劫持呢? 因为我们需要根据依赖值是否发生变化来判断计算属性是否需要重新计算

function defineComputed(target, key, userDef) {// const getter = typeof userDef === 'function' ? userDef : userDef.get;const setter = userDef.set || (() => { })// 可以通过实例拿到对应的属性Object.defineProperty(target, key, {get: createComputedGetter(key),set: setter})
}

3 createComputedGetter判断计算属性的值是否变化 增加dirty
如果是true执行更新

// 计算属性根本不会收集依赖 ,只会让自己的依赖属性去收集依赖
function createComputedGetter(key) {// 我们需要检测是否要执行这个getterreturn function () {const watcher = this._computedWatchers[key]; // 获取到对应属性的watcherif (watcher.dirty) {// 如果是脏的就去执行 用户传入的函数watcher.evaluate(); // 求值后 dirty变为了false ,下次就不求值了}if (Dep.target) { // 计算属性出栈后 还要渲染watcher, 我应该让计算属性watcher里面的属性 也去收集上一层watcherwatcher.depend();//计算属性watcher收集渲染watcher}return watcher.value; // 最后返回的是watcher上的值}
}

watcher
新增了dirty属性 标识是否需要更新视图
增加了evaluate方法 重新渲染 并且将dirty变成true

// src/observer/watcher.js// import { pushTarget, popTarget } from "./dep";
// import { queueWatcher } from "./scheduler";
// import {isObject} from '../util/index'
// // 全局变量id  每次new Watcher都会自增
// let id = 0;export default class Watcher {constructor(vm, exprOrFn, cb, options) {// this.vm = vm;// this.exprOrFn = exprOrFn;// this.cb = cb; //回调函数 比如在watcher更新之前可以执行beforeUpdate方法// this.options = options; //额外的选项 true代表渲染watcher// this.id = id++; // watcher的唯一标识// this.deps = []; //存放dep的容器// this.depsId = new Set(); //用来去重dep// this.user = options.user; //标识用户watcherthis.lazy = options.lazy; //标识计算属性watcherthis.dirty = this.lazy; //dirty可变  表示计算watcher是否需要重新计算 默认值是true// 如果表达式是一个函数// if (typeof exprOrFn === "function") {//   this.getter = exprOrFn;// } else {//   this.getter = function () {//     //用户watcher传过来的可能是一个字符串   类似a.a.a.a.b//     let path = exprOrFn.split(".");//     let obj = vm;//     for (let i = 0; i < path.length; i++) {//       obj = obj[path[i]]; //vm.a.a.a.a.b//     }//     return obj;//   };// }// 非计算属性实例化就会默认调用get方法 进行取值  保留结果 计算属性实例化的时候不会去调用getthis.value = this.lazy ? undefined : this.get();}get() {pushTarget(this); // 在调用方法之前先把当前watcher实例推到全局Dep.target上const res = this.getter.call(this.vm); //计算属性在这里执行用户定义的get函数 访问计算属性的依赖项 从而把自身计算Watcher添加到依赖项dep里面收集起来popTarget(); // 在调用方法之后把当前watcher实例从全局Dep.target移除return res;}//   把dep放到deps里面 同时保证同一个dep只被保存到watcher一次  同样的  同一个watcher也只会保存在dep一次//   addDep(dep) {//     let id = dep.id;//     if (!this.depsId.has(id)) {//       this.depsId.add(id);//       this.deps.push(dep);//       //   直接调用dep的addSub方法  把自己--watcher实例添加到dep的subs容器里面//       dep.addSub(this);//     }//   }//   这里简单的就执行以下get方法  之后涉及到计算属性就不一样了update() {// 计算属性依赖的值发生变化 只需要把dirty置为true  下次访问到了重新计算if (this.lazy) {this.dirty = true;} else {// 每次watcher进行更新的时候  可以让他们先缓存起来  之后再一起调用// 异步队列机制queueWatcher(this);}}//   计算属性重新进行计算 并且计算完成把dirty置为falseevaluate() {this.value = this.get();this.dirty = false;}depend() {// 计算属性的watcher存储了依赖项的deplet i = this.deps.length;while (i--) {this.deps[i].depend(); //调用依赖项的dep去收集渲染watcher}}//   run() {//     const newVal = this.get(); //新值//     const oldVal = this.value; //老值//     this.value = newVal; //跟着之后  老值就成为了现在的值//     if (this.user) {//       if(newVal!==oldVal||isObject(newVal)){//         this.cb.call(this.vm, newVal, oldVal);//       }//     } else {//       // 渲染watcher//       this.cb.call(this.vm);//     }//   }
}

computed和watch的区别

**相同点:**底层都会创建一个watcher computed定义的属性可以在模板中使用 watch不能在视图中国使用
不同点: computed不会默认执行 只有取值会执行 内部会以一个dirty属性控制依赖的值是否变化
watch默认用户会提供一个回调函数 数据变化就使用用户传入的回调
本周总结
vue2手写部分学习完了 其实感觉收集依赖那一部分还是有点绕 后续应该会多看点别人总结的内容对着自己代码复习复习也学习了基础的webpack
下周主要还是学习一下源码 复习一下js高级啥的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/229964.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

05 Rust 结构体

结构体 Rust 中的结构体&#xff08;Struct&#xff09;与元组&#xff08;Tuple&#xff09;都可以将若干个类型不一定相同的数据捆绑在一起形成整体&#xff0c;但结构体的每个成员和其本身都有一个名字&#xff0c;这样访问它成员的时候就不用记住下标了。 元组常用于非定…

【Hive】——DDL(DATABASE)

1 概述 2 创建数据库 create database if not exists test_database comment "this is my first db" with dbproperties (createdByAllen);3 描述数据库信息 describe 可以简写为desc extended 可以展示更多信息 describe database test_database; describe databa…

技术分享 | 接口测试价值与体系

如果把测试简单分为两类&#xff0c;那么就是客户端测试和服务端测试。移动端的测试包括 UI 测试&#xff0c;兼容性测试等&#xff0c;服务端测试包括接口测试。接口测试检查数据的交换、传递和控制管理过程。它绕过了客户端&#xff0c;直接对服务端进行测试。 接口测试的价值…

链表之带头双向循环链表(C语言版)

我们之前已经介绍过链表的知识了&#xff0c;这里我们直接开始实现带头双向循环链表 数据结构之单链表&#xff08;不带头单向非循环链表&#xff09;-CSDN博客 第一步&#xff1a;定义结构体 //定义结构体 typedef int SLTDateType; typedef struct Listnode {SLTDateType d…

字符设备驱动框架的编写

一. 简介 我们在学习裸机或者 STM32 的时候关于驱动的开发就是初始化相应的外设寄存器&#xff0c;在 Linux 驱动开发中&#xff0c;肯定也是要初始化相应的外设寄存器。 只是在 Linux 驱动开发中&#xff0c; 我们需要按照其规定的框架来编写驱动&#xff0c;所以说学 …

【HTML5、CSS3】新增特性总结!

文章目录 23 HTML5 新增特性23.1 语义化标签23.2 多媒体标签23.2.1 视频<video>标签23.2.2 音频<audio>标签 23.3 input属性值23.4 表单属性 24 CSS3 新增特性24.1 属性选择器24.2 结构伪类选择器24.2.1 选择第n个元素24.2.2 常用的6个结构伪类选择器 24.3 伪元素选…

云计算技术----数据可视化

一. 单选题&#xff08;共9题&#xff0c;63.9分&#xff09; (单选题)下列关于数据可视化的描述,哪个是错误的? A. 数据可视化是指将大型数据集中的数据以图形图像形式表示 B. 利用数据分析和开发工具发现其中未知信息的处理过程 C. 数据可视化技术的基本思想是将数据库中每一…

如何用Python向图像中加入噪声

我们在做机器视觉项目的过程中&#xff0c;有的时候需要向图像中加入噪声。Pytorch本身不支持类似的功能&#xff0c;如果自己写的话&#xff0c;不但麻烦&#xff0c;而且容易出错。好在skimage支持这个功能。代码如下&#xff1a; import skimage import matplotlib.pyplot …

抚琴成一快-布鲁斯

布鲁斯 0.理论1.音阶1.大调布鲁斯音阶2.小调布鲁斯音阶 1.基础1.shuffle节奏制音2.十二小节3.和弦4.小调五声音阶 2.演奏手法1.Lamp and Lamp1.基础和声进行2.进阶和声进行1.quick change2. call and respond:3.回旋句 2.Box1.基础和声进行2.进阶和声进行 3.Boogie1.基础节奏2.…

【教学类-06-19】20231217 (按“列”正序题)X-Y之间“加法题+题”(1页最多0-13。填满115空格)

作品展示&#xff1a;按列排序&#xff0c;从小到大正序&#xff08;没有大量空格&#xff09; 1.会有空格做分割线&#xff0c;上面部分是所有的小到大正序加法&#xff0c;下面的部分就是正序题目的不重复随机抽取题目&#xff08;乱序题&#xff09; 2、包含分割空格&…

html懒人加载实现

在HTML中&#xff0c;懒加载&#xff08;Lazy Load&#xff09;是一种延迟加载图片或其他资源的技术&#xff0c;它可以提高页面的加载速度和性能。下面是一种实现懒加载的方法&#xff1a; 设置默认占位图片&#xff1a;在HTML中&#xff0c;为要延迟加载的图片设置一个默认的…

react-next. 配置title信息

文档&#xff1a;元数据 1、静态数据 import type { Metadata } from nextexport const metadata: Metadata {title: ...,description: ..., }export default function Page() {} 2、动态数据 import type { Metadata, ResolvingMetadata } from nexttype Props {params:…

力扣190. 颠倒二进制位

位运算 思路&#xff1a; 逐位颠倒&#xff0c;第 i 位二进制移动到 (31 - i) 位上 result | (n & 1) << (31 - i); class Solution { public:uint32_t reverseBits(uint32_t n) {uint32_t result 0;for (int i 0; i < 32 && n > 0; i) {result | (…

实验记录:深度学习模型收敛速度慢有哪些原因

深度学习模型收敛速度慢有哪些原因&#xff1f; 学习率设置不当&#xff1a; 学习率是算法中一个重要的超参数&#xff0c;它控制模型参数在每次迭代中的更新幅度。如果学习率过大&#xff0c;可能会导致模型在训练过程中的振荡&#xff0c;进而影响到收敛速度&#xff1b;如果…

【Windows】windows11右键默认显示更多选项的办法

Windows11系统的右键菜单显示&#xff0c;需要多点一次“显示更多选项”才能看到所有菜单内容&#xff0c;按下面步骤简单设置一下就能恢复成Windows经典的右键菜单显示。 1. 2.输入命令【reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a…

React与AJAX

大家好&#xff0c;欢迎来到 《React与AJAX》 课程。在这一课中&#xff0c;我们将学习如何在 React 中使用 AJAX。 什么是 AJAX&#xff1f; AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;是一种使用 JavaScript 在浏览器和服务器之间进行异步通信的技术。A…

Redis - RDB与AOF持久化技术

Redis 持久化技术 RDB 是默认持久化方式&#xff0c;但 Redis 允许 RDB 与 AOF 两种持久化技术同时 开启&#xff0c;此时系统会使用 AOF 方式做持久化&#xff0c;即 AOF 持久化技术的优先级要更高。同样的道 理&#xff0c;两种技术同时开启状态下&#xff0c;系…

css的元素显示模式(有单行文字垂直居中,侧边栏等案例)

目录 1. 什么是元素的显示模式 2. 元素显示模式的类型 块元素 行内元素 行内块元素 3. 元素显示模式的转换 4.文字垂直居中 5.具体实现案例 1. 什么是元素的显示模式 定义&#xff1a;元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;…

Mysql 压测

目录 1、mysql查看数据大小 2、mysql配置优化 3、mysql压力测试 4、mysql主从复制原理 5、mysql主从延迟如何解决 6、主从切换操作 1、mysql查看数据大小 怎样查询总数据量 SELECT table_schema AS Database,CONCAT(ROUND(SUM((data_length index_length) / (1024 * 102…

HarmonyOS鸿蒙应用开发——数据持久化Preferences封装

文章目录 数据持久化简述基本使用与封装测试用例参考 数据持久化简述 数据持久化就是将内存数据通过文件或者数据库的方式保存到设备中。HarmonyOS提供两两种持久化方案&#xff1a; Preferences&#xff1a;主要用于保存一些配置信息&#xff0c;是通过文本的形式存储的&…