摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理源码版今天继续探索源码,废话不
写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧
【Vue原理】Watch - 源码版
今天继续探索 Watch 源码,废话不多说了
带着我的几个疑问开始
1、什么时候初始化 2、怎么确定监听哪些值 3、深度监听怎么回事 4、怎么触发我的函数
这些问题的答案会掺杂在源码的解析中,我发现这几篇的写作套路都差不多.....
也可以看查一下我的白话版
什么时候初始化首先,从这个问题开始我们今天的探索之旅,请看源码
function Vue(){ ... 其他处理 initState(this) ...解析模板,生成DOM 插入页面 } function initState(vm) { ...处理 data,props,computed 等数据 if (opts.watch) { initWatch(this, vm.$options.watch); } }
没错,当你调用 Vue 创建实例过程中,会去处理各种选项,其中包括处理 watch
initWatch处理 watch的方法是 initWatch,下面就呈上 源码
function initWatch(vm, watch) { for (var key in watch) { var watchOpt = watch[key]; createWatcher(vm, key, handler); } }
然后这段源码并没有做什么惊天地泣鬼神的事情,只是简简单单的一个遍历,然后每个watch 都使用一个叫什么 createWatcher 的东西去处理
这个函数到底是干嘛的?暗藏着什么秘密,欢迎来到我们今晚的 《走近科学》《源码解析》
createWatcher 来看看他的真身
function createWatcher( // expOrFn 是 key,handler 可能是对象 vm, expOrFn, handler,opts ) { // 监听属性的值是一个对象,包含handler,deep,immediate if (typeof handler ==="object") { opts= handler handler = handler.handler } // 回调函数是一个字符串,从 vm 获取 if (typeof handler === "string") { handler = vm[handler] } // expOrFn 是 key,options 是watch 的全部选项 vm.$watch(expOrFn, handler, opts) }
大概就这样吧
1、获取到监听回调
2、调用 vm.$watch
1、获取监听回调首先,你传入的 watch 配置可能是这三种(还有更多,差不多,不解释,累死我)
如果配置是个对象,就取handler 字段
如果配置是函数,那么直接就是 监听回调
如果配置是字符串,从实例上获取函数
2、调用 vm.$watch看着这个方法,简直 mmp,还有完没完,我从一个函数进入一个函数,又从这个函数进入到另一个函数,迷宫啊这是.....
显然你不用急,下面还有更多......
好吧,还是先看源码
Vue.prototype.$watch = function( // expOrFn 是 监听的 key,cb 是监听回调,opts 是所有选项 expOrFn, cb, opts ){ // expOrFn 是 监听的 key,cb 是监听的回调,opts 是 监听的所有选项 var watcher = new Watcher(this, expOrFn, cb, opts); // 设定了立即执行,所以马上执行回调 if (opts.immediate) { cb.call(this, watcher.value); } };
看完了吧?这么短,你们肯定看得懂的啦,就两件事
1、判断是否立即执行监听回调如果你设置了 immediate 的话,表示不用等我数据变化,初始化时马上执行一遍,执行的代码就是直接调用 回调,绑定上下文,传入监听值
2、每个 watch 配发 watcher代码从这里开始变得沉重,各位观众,喝口水,恰口饭,屏息观看操作
看看 watcher 的源码
“watcher 的源码之前的文章也讲过很多,但是对于每种选项的涉及的细节是不一样的,所以每次都放上来,但是只放跟本内容相关的部分代码,其他的省去以便我们快速理解”
var Watcher = function (vm, key, cb, opt) { this.vm = vm; this.deep = opt.deep; this.cb = cb; // 这里省略处理 xx.xx.xx 这种较复杂的key this.getter = function(obj) { return obj[key] }; // this.get 作用就是执行 this.getter函数 this.value = this.get(); };
再看看,新建 watcher 的时候 ,传入了什么
1、监听的 key
2、监听回调 (Watch 中的cb)
3、监听配置的options
这里会涉及到三个问题,现在来解释
1、怎么对设置的 key 进行监听?我们要先对 Watch 中的 this.getter 的函数进行理解,他的本质是为了获取对象的key值
然后 getter 是在 watcher.get 中执行的,看下 get 源码
// 对本问题进行了独家简单化的源码 Watcher.prototype.get = function() { var value = this.getter(this.vm); return value };
你能看到,Watch 在结尾会立即执行一次 watcher.get,其中便会执行 getter,便会根据你监听的key,去实例上读取并返回,存放在 watcher.value 上
看到了吗,从实例上读取属性,这句话。
首先,watch 初始化之前,data 应该初始化完毕了,每个 data 数据都已经是响应式的
使用例子来说明一下
当 watch.getter 执行,而读取了 vm.name 的时候,name的依赖收集器就会收集到 watch-watcher
于是 name 变化的时候,会可以通知到 watch,监听就成功了
2、如何进行深度监听?首先,深度监听,是你设置了 deep 的时候,如下
然后,观察上面的 Watch 源码,deep 会保存在watcher 中,以便后用
话锋一转
上一问题说过,在 新建 watcher 的时候,会马上执行一个 get,上个问题的 get 源码简化很多,把 处理深度监听的部分去掉了,这里露出来了
Watcher.prototype.get = function() { Dep.target= this var value = this.getter(this.vm) if (this.deep) traverse(value) Dep.target= null return value };
没错,处理深度监听只有一条语句!
if (this.deep) traverse(value)
value 是 getter 从实例上读取监听key 得到的值,没有疑问
但是 traverse 是何方神圣?come on 让我们深入....
function traverse(val) { var i, keys; // 数组逐个遍历 if (Array.isArray(val)) { i = val.length; // val[i] 就是读取值了,然后值的对象就能收集到 watch-watcher while (i--) { traverse(val[i]) } } else { keys = Object.keys(val); i = keys.length; // val[keys[i]] 就是读取值了,然后值的对象就能收集到 watch-watcher while (i--) { traverse(val[keys[i]]) } } }
你看它这段代码长,其实是个纸老虎,做的就是一个事情,不断递归深入读取对象
他的想法是这样的
因为读取,就可以让这个属性收集到 watch-watcher 的原则
就算是深层级的对象,其中的每个属性也都是响应式的,每个属性都有自己的依赖收集器
通过不断深入的读取每个属性,这样每个属性就都可以收集到 watch-watcher 了
这样不管对象内多深的属性变化,都会通知到 watch-watcher
于是这样就完成了深度监听
3、监听值变化,如何触发监听函数?通过上面的问题,我们已经了解了大部分了
监听的数据变化的时候,就能通知 watch-watcher 更新,所谓通知更新,就是手动调用 watch.update
速度看下 watcher.update 源码
Watcher.prototype.update= function() { var value = this.get(); if (this.deep) { var oldValue = this.value; this.value = value; // cb 是监听回调 this.cb.call(this.vm, value, oldValue); } };
很简单嘛,就是读取一遍值,然后保存新值,接着 调用 监听回调,并传入新值和 旧值
ok,就这样
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105380.html
摘要:而是在初始化时,在读取了监听的数据的值之后,便立即调用一遍你设置的监听回调,然后传入刚读取的值设置了时,如何工作我们都知道有一个选项,是用来深度监听的。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下...
写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Mixins - 源码版 今天探索的是 mixins 的源码,mixins 根据不同的选项类型会做不同的处理 篇幅会有些长,...
摘要:而我觉得现在出一个白话版,是让大家有兴趣去研究源码的时候,可以提前理清一下思路。相当于封装,提取公共部分。显然,今天我不是来教大家怎么用的,怎么用看文档就好了,我是讲解生命的真谛内部的工作原理。而这个不会合并,直接替换掉整个选项 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版...
写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Props - 源码版 今天记录 Props 源码流程,哎,这东西,就算是研究过了,也真是会随着时间慢慢忘记的。 幸好我做...
写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 源码版 之 服务Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白话版 简单了解下...
阅读 1875·2021-11-12 10:36
阅读 2308·2021-09-01 10:29
阅读 2336·2019-08-30 15:56
阅读 1014·2019-08-30 12:56
阅读 2340·2019-08-26 13:58
阅读 2262·2019-08-23 18:38
阅读 1484·2019-08-23 18:32
阅读 2102·2019-08-23 16:53