要想理解原理就得看源码,最近网上也找了好多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

综上,计算属性基本的原理就是这样了,主要是自己的理解,有不对的地方还请指出,哎,还是写代码舒服点,打字描述太累。。。

vue之initComputed模块源码说明的更多相关文章

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

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

  2. XposedNoRebootModuleSample 不需要频繁重启调试的Xposed 模块源码例子

    XposedNoRebootModuleSample(不需要频繁重启调试的Xposed 模块源码例子) Xposed Module Sample No Need To Reboot When Debu ...

  3. nginx健康检查模块源码分析

    nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...

  4. 【 js 模块加载 】深入学习模块化加载(node.js 模块源码)

    一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须按照一定的格式编写.AMD,CMD,CommonJS 是目前最常用的三种模块化书写规范.  1.AMD(Asy ...

  5. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  6. Spark Scheduler模块源码分析之DAGScheduler

    本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...

  7. gorm的日志模块源码解析

    gorm的日志模块源码解析 如何让gorm的日志按照我的格式进行输出 这个问题是<如何为gorm日志加traceId>之后,一个群里的朋友问我的.如何让gorm的sql日志不打印到控制台, ...

  8. koa2--delegates模块源码解读

    delegates模块是由TJ大神写的,该模块的作用是将内部对象上的变量或函数委托到外部对象上.然后我们就可以使用外部对象就能获取内部对象上的变量或函数.delegates委托方式有如下: gette ...

  9. 基于Python的datetime模块和time模块源码阅读分析

    目录 1 前言  2 datetime.pyi源码分步解析 2.1 头部定义源码分析 2.2 tzinfo类源码分析 2.3 date类源码分析 2.4 time类源码分析 2.5 timedelta ...

随机推荐

  1. Linux下查找Nginx配置文件位置

    1.查看Nginx进程 命令: ps -aux | grep nginx 圈出的就是Nginx的二进制文件 2.测试Nginx配置文件 /usr/sbin/nginx -t 可以看到nginx配置文件 ...

  2. 主效应|处理误差 |组间误差|处理效应|随机误差|组内误差|误差|效应分析|方差齐性检验|SSE|SSA|SST|MSE|MSA|F检验|关系系数|完全随机化设计|区组设计|析因分析

    8 什么是只考虑主效应的方差分析? 就是不考虑交互效应的方差分析,即认为因素之间是不相互影响的,就是无重复的方差分析.   什么是处理误差 (treatment error).组间误差(between ...

  3. python学习笔记(12)常用模块

    一.模块.包 什么是模块? 模块实质上就是一个python文件,它是用来组织代码的,意思就是说把python代码写到里面,文件名就是模块的名称,test.py test就是模块名称. 什么是包? 包, ...

  4. Django学习之视图层

    视图层 小白必会三板斧 HttpResponse render redirect django视图函数必须要给返回一个HttpResponse对象(render和redirect内部返回的也是一个Ht ...

  5. Python基本了解

    1. 计算机基础知识 CPU : 人类的大脑,运算处理问题 内存 : 临时储存数据,断点数据就会消失,存储数据快 硬盘 : 永久存储各种数据,相对于内存存储速度慢 操作系统 : 本质上是一个软件,用于 ...

  6. Freeswitch录音Dialplan

    <extension name="record"> <condition field="destination_number" express ...

  7. 跨越真实和虚拟世界的边界——走近SIGGRAPH 2014大会

    2014大会" title="跨越真实和虚拟世界的边界--走近SIGGRAPH 2014大会"> 作者:孙鑫 微软亚洲研究院研究员 一场大会振奋一座城 温哥华位于加 ...

  8. idea 内存溢出解决方法

    在Run/Debug configuration 的 vm options里面输入 -server -XX:PermSize=128M -XX:MaxPermSize=256m 具体如下图:

  9. Spring+Struts2+Mybatis整合

    1. 创建项目 2. 添加Spring能力 使用MyEclipse自动加载Struts2和Spring的jar包和配置文件,并在web.xml文件中添加上下文和监听器 web.xml文件如下: < ...

  10. 从广义线性模型(GLM)理解逻辑回归

    1 问题来源 记得一开始学逻辑回归时候也不知道当时怎么想得,很自然就接受了逻辑回归的决策函数--sigmod函数: 与此同时,有些书上直接给出了该函数与将 $y$ 视为类后验概率估计 $p(y=1|x ...