要想理解原理就得看源码,最近网上也找了好多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. 吴裕雄--天生自然C语言开发:排序算法

    #include <stdio.h> void bubble_sort(int arr[], int len) { int i, j, temp; ; i < len - ; i++ ...

  2. 不同SQL数据库之间表数据的实时同步-发布与订阅

    https://blog.csdn.net/rand_muse/article/details/81326879 上述文章中,如果是实时同步,选择  事务发布即可 在快照代理 那里,不需要勾选  运行 ...

  3. PAT甲级——1041 Be Unique

    1041 Be Unique Being unique is so important to people on Mars that even their lottery is designed in ...

  4. spring boot web 开发及数据库操作

    推荐网站http://springboot.fun/ 1.json 接口开发 2.自定义 filter 3.自定义 property 4.log 配置 5.数据库操作 6.测试

  5. Jmeter阶梯式压测

    https://www.cnblogs.com/Zfc-Cjk/p/11639219.html 什么是阶梯式压测? 阶梯式压测,就是对系统的压力呈现阶梯性增加的过程,每个阶段压力值都要增加一个数量值, ...

  6. linux查看并发连接数

    1.查看TCP的并发请求数及其TCP连接状态: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' netstat ...

  7. F5 BIG-IP之二LTM术语

    Node : Pool:中有设置负载均衡方式 Virtual Server :监听客户端请求的 根据 IP 和 端口号 Profile: VS 调用 Profile

  8. Redis Client 官方下载地址

    下载地址:https://github.com/caoxinyu/RedisClient 界面 归途(LM)

  9. JFreeChart插件使用

    以java project为例,首先需要导入需要的jar包:jcommon-1.0.23.jar, jfreechart-1.0.19.jar. 画饼状图示例: package com.it.jfch ...

  10. AVFoundation Programming Guide(官方文档翻译4)Editing - 编辑

    新博客:完整版 - AVFoundation Programming Guide 分章节版:- 第1章:About AVFoundation - AVFoundation概述- 第2章:Using A ...