回顾目标

数据驱动视图:

  1. 理解watcher、dep、observer这三个对象之间的关系
  2. 这和VUE对象又有什么关系?
  3. 这和视图又有什么关系?

叙述过程

先彻底理解了一下VUE的简介,并写出了一份建议书。关键词有渐进式、自底向上逐层应用、声明式开发、组件化。

之后了解了观察者模式,观察者模式的初衷是建立低耦合的通信机制,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

记得以前看过一本掘金小册,其中作者的结论是:每个vue对象对应一个watcher,每个observer和watcher通过dep建立一对多关系。

然后开始走读代码。大概读了一遍watcher、observer、deb发现没什么新的收获,开始认为每一个vue对象管理一个watcher,然后每个变量都管理一个observer,每个observer管理一个sub,由observer通知watcher,然后渲染。

但是**提出了一个关键性的问题,有一个公共的target是用来存放watcher的,发现这个target和依赖关系非常有关,然后启动浏览器用检查器监察这个值,发现这个值一般是null,然后打开vscode,全局查找,找出是什么时候写的这个值,最后定位这个target在*watch读数据的时候被缓存。

这段话非常的重要,只有当执行用户自定义函数的时候,才会建立依赖关系。

翻看了文档。找到了一段清晰的代码,

var vm = new Vue({data: { a: 1 },computed: {// 仅读取aDouble: function () {return this.a * 2},// 读取和设置aPlus: {get: function () {return this.a + 1},set: function (v) {this.a = v - 1}}}})

然后带着上面的问题,去阅读了vue的构造过程,在initState中发现了computed的构造,其中有一个关键的用法:

function initComputed (vm: Component) {const computed = vm.$options.computed...const userDef = computed[key]makeComputedGetter(userDef, vm)...
}function makeComputedGetter (getter: Function, owner: Component): Function {const watcher = new Watcher(owner, getter, noop, {lazy: true})...}

对于观察者来说用户定义的函数是getter,也就是对于框架底层来说,程序员写的computed函数,对组件来说是getter。computed实际上是用包装了一下用户定义的函数。可以把他理解成一种特殊的watcher,根据官方的文档,提到了computed有缓存功能,不会更新相同的结果。简单起见我们就把computed当作watcher理解。

然后开始关注这个函数内部发生了什么:

...
computed: {// 仅读取aDouble: function () {return this.a * 2},
...

这里读取了this.a。我想起来vue内部的变量都是处理过的。用了observe这个工厂方法加工过,set和get都和watcher和deb耦合。执行aDouble这个函数,computed就作为一个watcher被Dep.target记住了,而a就是一个obsserver,调用了get就会将watcher保存在deps数组中,就好像a调用get就被aDouble盯上(watch)了,下次a变化(调用a.set)就重新执行一遍aDouble。

这个时候我们再回去看wacher对象,发现这个对象有一个cb存放回调函数,而且代码中还出来了属于VNode模块的patch。cb应该是负责重新渲染视图。

所以这就可以解释VUE是如何驱动视图改变的,源自于一种自动更新的一对多机制。这可以解决父子组件的通信问题,父子组件状态同步可以通过向子组件注入父组件的数据引用,可以实现单向数据流。

查看了官方文档,结果发现:

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

证实了掘金那个作者说的没错,每一个vue的确有一个watcher

查看watcher.get的call stack:

get (vue.js:647)
Watcher (vue.js:638)
Vue.$watch (vue.js:1254)
createWatcher (vue.js:1232)
initWatch (vue.js:1217)
initState (vue.js:1105)
Vue._init (vue.js:2110)
Vue (vue.js:2150)
(anonymous) (app.js:7)

我们看到Vue.$watch说明vue下确实有一个watcher

这个watcher目的是什么呢?我们回去看了一下watch的构造函数

constructor (vm: Component,expOrFn: string | Function,cb: Function,options?: Object = {}) {... this.getter = parsePath(expOrFn)this.value = this.lazy? undefined: this.get()...}

在parsePath之中会触发所有vue的内部对象的get的函数句柄,然后被执行,所有的内部数据改动都会导致wacher重新渲染数据

评估结果:

结果不等于目标

  1. 开始否认了认为每一个vue对象管理一个watcher
  2. 我们推导出了computed和watcher的关系,并认为这是watch唯一的作用

结果等于目标

  1. 理解watcher、dep、observer这三个对象之间的关系 每一个用户定义回调函数管理一个watcher,然后每个VUE内部data都管理一个observer,每个observer管理一个sub,由observer通知watcher。
  2. 这和VUE对象又有什么关系 VUE对象保存了data数据,每一个data都管理一个observer,computed是一种特殊的watcher,VUE对象渲染视图的时候会要求watcher返回一个值,这个时候watcher执行用户定义的函数句柄,也就是computed当中定义的函数时,每个data被读取的时候会和当前的函数建立依赖关系,当这个data更新的时候会重新渲染视图,重新执行用户定义函数。且当new一个vuew对象的时候watcher会监听所有的内部数据对象。
  3. 这和视图又有什么关系 每个watcher负责自动更新视图。

分析原因

开始否认为每一个vue对象管理一个watcher

  1. 先看了别人的文章然后带着结论去看的代码,最后只是找怎么支持这个结论的代码段。但是发现了文章之外的内容误以为文章错误了。
  2. watcher是被vue对象直接调用的,很容易让人联想一对一关系,但不确定
  3. dep.target作为关键的变量,作搞清楚这个变量的作用,就可以回答谁依赖谁这个问题。

我们推导出了computed和watcher的关系,并认为这是唯一的作用

  1. 当看到watcher.value时。意识到一个vue对象可能需要维护多个watcher,因此原来的假设不成立,那么需要找到watcher具体是做什么的。沿着watcher.get一直往下看,发现了watcher的作用和computed有关联,因此认为computed是watcher的一种表现,而一个vue对象又有n个computed。这才认为作者的观点是错误的。
  2. 很多文章中没有找到关于computed和watcher的作用,误以为是别人理解错了,其实这和我的结论并不冲突。

推演规律

自然语言更加容易被人理解,因为首因效应,人对事物的理解不太容易改变,那么我们看别人的文章,我们理解的不是原作者的设计思想,而是经过了非原作者的几层解释的最终结果。

那么这就导致了我对其他人的结论有一种怀疑,想找出反证的点。而这个时候就需要从官方文档中找到依据,作者和团队的文档最具有权威和可行度。

从非自然语言——代码。总结出来的,我们可以在阅读源代码参考别人的文章,但是其中最有价值的部分,不是别人的结论,而是别人阅读的顺序,看别人是如何抓住主干去理解的。综上有两个东西对阅读源代码有益,分别是文章的行文顺序和文章的大标题,文章的大标题一般是对源代码模块的高度概括。

vue data 值如何渲染_vue源码阅读复盘-watcher模块相关推荐

  1. vue data 值如何渲染_Vue执行流程解析

    引言 相信绝大多数的前端小伙伴已记不清做了多少项目,写了多少代码了,每个人如同教科书般地写着Vue代码: // 单文件组件中常见代码 export default {data () {return { ...

  2. FreeSWITCH 1.10 源码阅读(3)-sofia 模块原理及其呼入处理流程

    文章目录 1. 前言 2. 源码分析 2.1 sofia 模块的加载 2.2 呼入的处理流程 1. 前言 SIP(Session Initiation Protocol) 是应用层的信令控制协议,有许 ...

  3. vue add element报错_Vue 源码解析 -- new Vue -gt; mountComponent 001

    这一系列文章的学习出处于Vue.js 技术揭秘 | Vue.js 技术揭秘,有兴趣的伙伴自行阅览 本文涉及到vue源码文件中的 src/core/instance/index.js ==> ne ...

  4. freeswitch源码阅读 之 sofia模块

    sofia模块在freeswitch中的位置非常重要, 所有的sip通话都和它有关, 那么我们就看一下该模块的执行流程. 一. 实现的功能: 1. sip注册; 2. 呼叫; 3. Presence; ...

  5. Vue 源码阅读学习(三)

    第三节:函数柯里化与渲染模型 嘿,朋友们,本节是 Vue 源码阅读的第三讲.Vue 源码阅读系列得到了赞赏,我很高兴,同时希望大家可以给予反馈!我虚心接纳您的意见! 如果没有看之前的第一讲和第二讲的内 ...

  6. springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解)

     博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解) 视 ...

  7. 【SeaJS】【3】seajs.data相关的源码阅读

    在SeaJS官网上推荐了源码阅读顺序,本文并没有采用这个顺序,而是按个人习惯以调试官方示例的方式进行源码阅读.早期版本作者玉伯使用了几个闭包形式,本文源码版本为2.1.1,它的编码方式个人认为更加脚本 ...

  8. 视频教程-经典Vue从入门到案例到源码分析教程(含资料)-Vue

    经典Vue从入门到案例到源码分析教程(含资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国移动等知名企业 ...

  9. python+vue校园足球联赛管理系统django源码

    本次设计任务是要设计一个足球联赛管理系统,通过这个系统能够满足用户及时浏览各种足球联赛信息.系统的主要功能包括主页.个人中心.用户管理.球队赛程管理.球队信息管理.球员信息管理.打赏球员管理.系统管理 ...

最新文章

  1. 给Linux系统/网络管理员的nmap的29个实用例子
  2. 总结了200道经典的机器学习面试题 (附参考答案)
  3. android使用perfetto工具步骤
  4. nuxt generate 报错 window is not defined
  5. SpringAOP之@EnableAspectJAutoProxy如何实现自动代理?
  6. 惠普HP LaserJet 1320n 打印机驱动
  7. 偷盗者问题。甲乙丙丁四个嫌疑犯,只有一个是偷盗者。在审讯中,四人都有可能说真话或假话
  8. 人人网冷落主业,押注智能硬件
  9. b区计算机复试国家线,今年调剂太恐怖 B区考研分数线竟比A区高?
  10. 打印机服务器文件,打印机服务器ftp配置文件
  11. 6 Transport
  12. hadoop ls命令
  13. bfs hrbust 2188
  14. linux系统6.8下载,Linux CentOS 6.8 官方原版镜像
  15. 从回合霸王到网游巨头 网易游戏十年史
  16. 成功改造企业文化的八项原则
  17. 2017美国数学建模ICM E题(环境) 翻译 需要可持续城市!(Sustainable Cities Needed!)
  18. Java GUI编程学习
  19. 文心一言发布,你怎么看?chatGPT
  20. skimage的基本使用

热门文章

  1. Task02:SQL基础查询与排序-天池龙珠计划SQL训练营
  2. HDU - 1054 Strategic Game (二分图匹配模板题)
  3. k8s中yaml文件pod的语法(转)
  4. Hyper-v 虚拟机使用NAT方式连接网络
  5. mark ubuntu 16.04 64bit + cpu only install mtcnn
  6. 调试阶段 获取微信小程序openid
  7. 简易nodejs服务器
  8. 自制jQuery 复选框全选与反选插件
  9. simple_html_dom.php 使用 乱码处理作者:gaoming13
  10. Win7下使用toad连接oracle出现can't initialize OCI -1