摘要:要想理解原理就得看源码,最近网上也找了好多初始化方法个恶魔。。。因为也是循序渐进的理解,对计算属性的初始化有几处看得不是很明白,网上也都是含糊其辞的要想深入必须深入。。。
要想理解原理就得看源码,最近网上也找了好多vue初始化方法(8个init恶魔。。。)
因为也是循序渐进的理解,对initComputed计算属性的初始化有几处看得不是很明白,网上也都是含糊其辞的(要想深入必须深入。。。),所以debug了好几天,才算是有点头绪,现在写出来即帮自己再次理下思路,也可以让大佬指出错误
首先,基本的双向绑定原理就不说了,可以去搜下相关教程,还是要先理解下简单的例子
进入正题,先来看下initComputed的源码结构,这之前还是先放一个例子也好说明function initComputed (vm, computed) { console.log("%cinitComputed","font-size:20px;border:1px solid black") var watchers = vm._computedWatchers = Object.create(null); // computed properties are just getters during SSR var isSSR = isServerRendering(); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === "function" ? userDef : userDef.get; if ("development" !== "production" && getter == null) { warn( ("Getter is missing for computed property "" + key + ""."), vm ); } //minxing---console console.log("%cinitComputed 定义watchers[key]=new Watcher(vm getter noop computedWatcherOptions)","color:white;padding:5px;background:black"); if (!isSSR) { // create internal watcher for the computed property. /** * 熟悉的newWatcher,创建一个订阅者,为了之后收集依赖 * 将例子中的num、lastNum和计算属性comNum进行绑定 * 也就是说在一个deps中有两个dep,其中的subs分别是 * dep1.subs:[watcher(num),watcher(comNum)] * dep2.subs:[watcher(lastNum),watcher(comNum)] * dep3........ * 请看前面的例子,页面html中并没有渲染{{lastNum}};按理说就不会执行lastNum的getter * 从而就不会和计算属性进行关联绑定,如果更改lastNum就不会触发dep2的notify()发布 * 自然也就不会在页面看到comNum有所变化,但是运行后却不是这样,为什么呢 * 这就引出这个initComputed的下面方法了---依赖收集(watcher.prototype.depend)! * 当时也是看了好久才知道这个depend方法的作用,后面再说 * 首先先来提个头,就是下面代码中watcher中这个getter * 其实就是function comNum() {return this.num+"-computed-"+this.lastNum;}} * 而这个getter什么时候执行呢,会在Watcher.prototype.evaluate()方法中执行 * 所以watcher中的evaluate()与depend()两个方法都与initComputed相关 */ watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ); } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. // 经过判断后定义计算属性---(关联到vm的data上面) if (!(key in vm)) { defineComputed(vm, key, userDef); } else { if (key in vm.$data) { warn(("The computed property "" + key + "" is already defined in data."), vm); } else if (vm.$options.props && key in vm.$options.props) { warn(("The computed property "" + key + "" is already defined as a prop."), vm); } } } }defineComputed方法
这个方法比较简单主要就是将计算属性绑定到vm上,重要的下面的createComputedGetter方法
function defineComputed ( target, key, userDef ) { console.log("%cdefineComputed","font-size:20px;border:1px solid black") var shouldCache = !isServerRendering(); if (typeof userDef === "function") { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef; sharedPropertyDefinition.set = noop; } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop; sharedPropertyDefinition.set = userDef.set ? userDef.set : noop; } if ("development" !== "production" && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( ("Computed property "" + key + "" was assigned to but it has no setter."), this ); }; } Object.defineProperty(target, key, sharedPropertyDefinition); }
function createComputedGetter (key) { console.log("createComputedGetter key",key); return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { watcher.evaluate(); } if (Dep.target) { watcher.depend(); } return watcher.value } } }createComputedGetter方法,主要做了两件事 1 求计算属性的值---利用上面说的调用watcher.evalute()方法,执行watcher中的getter 2 依赖收集绑定,这点最重要,就是上面说过的如果没有在html中对计算属性相关联的data属性(lastNum)进行页面渲染的话,watcher.depend()此方法就会执行这个依赖收集绑定的作用dep.subs[watcher(计算属性),watcher(计算关联属性1),...],这样的话当你更改lastNum就会触发对应的dep.notify()方法发布通知订阅者执行update,进行数据更新了,而如果将watcher.depend()方法注释掉,而页面中将lastNum渲染({{lastNum}}),此时watcher.evalute()会执行watcher.get从而将此计算watcher推入dep.target中,而渲染lastNum执行getter的时候就会将此watcher加入依赖,所以也会将lastNum和计算属性关联到dep中
function createComputedGetter (key) { console.log("createComputedGetter key",key); return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { console.log("createComputedGetter watcher evaluate==========="); //求值 watcher.evaluate(); } if (Dep.target) { console.log("createComputedGetter watcher depend==========="); //依赖收集 watcher.depend(); } console.log("%ccreateComputedGetter watcher.value is","color:blue;font-size:40px",watcher.value); return watcher.value } } }
为了更好的说明下,截两张图(都是基于最上面的html配置哦)
图组一注释掉watcher.depend()方法,此时deps中没有dep:id4
其实dep:id4在内存中已经定义好了但是没有加入到deps中(因为没有进行依赖收集)
而dep:id5和id6就是上面的数组和递归数组中元素的dep
dep:id3 就是
这下是不是很清楚了
进行依赖收集后的deps
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/92608.html
摘要:要想理解原理就得看源码,最近网上也找了好多初始化方法个恶魔。。。因为也是循序渐进的理解,对计算属性的初始化有几处看得不是很明白,网上也都是含糊其辞的要想深入必须深入。。。 要想理解原理就得看源码,最近网上也找了好多vue初始化方法(8个init恶魔。。。) 因为也是循序渐进的理解,对initComputed计算属性的初始化有几处看得不是很明白,网上也都是含糊其辞的(要想深入必须深入。。...
摘要:代码初始化部分一个的时候做了什么当我们一个时,实际上执行了的构造函数,这个构造函数内部挂载了很多方法,可以在我的上一篇文章中看到。合并构造函数上挂载的与当前传入的非生产环境,包装实例本身,在后期渲染时候,做一些校验提示输出。 概述 在使用vue的时候,data,computed,watch是一些经常用到的概念,那么他们是怎么实现的呢,让我们从一个小demo开始分析一下它的流程。 dem...
摘要:并在内执行了函数,在函数内部,访问了。至此知道了它依赖于。需要根据最新的计算。本例中收集到了依赖并且也被告知观察了他们。文章链接源码分析系列源码分析系列之环境搭建源码分析系列之入口文件分析源码分析系列之响应式数据一源码分析系列之响应式数据二 前言 上一节着重讲述了initData中的代码,以及数据是如何从data中到视图层的,以及data修改后如何作用于视图。这一节主要记录initCo...
摘要:被读取,包装的函数触发,第一次会进行计算被调用,进而被调用,被设置为,旧值页面被缓存起来。计算会读取,此时就收集到同时也会保存到的依赖收集器用于下一步。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下...
摘要:源码对于计算属性的理解这是我最近学习源码的一个个人总结和理解,所以可能并不适合每一位读者本文的整体脉络如下,首先尽可能去掉细节,对计算属性源码的大致实现有一个了解,然后举一例子,分别谈谈计算属性依赖收集和派发更新的流程。 vue源码-对于「计算属性」的理解 这是我最近学习vue源码的一个个人总结和理解,所以可能并不适合每一位读者 本文的整体脉络如下,首先尽可能去掉细节,对计算属性源码的...
阅读 2344·2021-11-11 16:54
阅读 2595·2021-09-26 09:47
阅读 3977·2021-09-08 09:36
阅读 2727·2021-07-25 21:37
阅读 926·2019-08-30 15:54
阅读 2540·2019-08-30 14:22
阅读 3245·2019-08-30 13:57
阅读 2558·2019-08-29 17:17