vue data 值如何渲染_vue源码阅读复盘-watcher模块
回顾目标
数据驱动视图:
- 理解watcher、dep、observer这三个对象之间的关系
- 这和VUE对象又有什么关系?
- 这和视图又有什么关系?
叙述过程
先彻底理解了一下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重新渲染数据
评估结果:
结果不等于目标
- 开始否认了认为每一个vue对象管理一个watcher
- 我们推导出了computed和watcher的关系,并认为这是watch唯一的作用
结果等于目标
- 理解watcher、dep、observer这三个对象之间的关系 每一个用户定义回调函数管理一个watcher,然后每个VUE内部data都管理一个observer,每个observer管理一个sub,由observer通知watcher。
- 这和VUE对象又有什么关系 VUE对象保存了data数据,每一个data都管理一个observer,computed是一种特殊的watcher,VUE对象渲染视图的时候会要求watcher返回一个值,这个时候watcher执行用户定义的函数句柄,也就是computed当中定义的函数时,每个data被读取的时候会和当前的函数建立依赖关系,当这个data更新的时候会重新渲染视图,重新执行用户定义函数。且当new一个vuew对象的时候watcher会监听所有的内部数据对象。
- 这和视图又有什么关系 每个watcher负责自动更新视图。
分析原因
开始否认为每一个vue对象管理一个watcher
- 先看了别人的文章然后带着结论去看的代码,最后只是找怎么支持这个结论的代码段。但是发现了文章之外的内容误以为文章错误了。
- watcher是被vue对象直接调用的,很容易让人联想一对一关系,但不确定
- dep.target作为关键的变量,作搞清楚这个变量的作用,就可以回答谁依赖谁这个问题。
我们推导出了computed和watcher的关系,并认为这是唯一的作用
- 当看到watcher.value时。意识到一个vue对象可能需要维护多个watcher,因此原来的假设不成立,那么需要找到watcher具体是做什么的。沿着watcher.get一直往下看,发现了watcher的作用和computed有关联,因此认为computed是watcher的一种表现,而一个vue对象又有n个computed。这才认为作者的观点是错误的。
- 很多文章中没有找到关于computed和watcher的作用,误以为是别人理解错了,其实这和我的结论并不冲突。
推演规律
自然语言更加容易被人理解,因为首因效应,人对事物的理解不太容易改变,那么我们看别人的文章,我们理解的不是原作者的设计思想,而是经过了非原作者的几层解释的最终结果。
那么这就导致了我对其他人的结论有一种怀疑,想找出反证的点。而这个时候就需要从官方文档中找到依据,作者和团队的文档最具有权威和可行度。
从非自然语言——代码。总结出来的,我们可以在阅读源代码参考别人的文章,但是其中最有价值的部分,不是别人的结论,而是别人阅读的顺序,看别人是如何抓住主干去理解的。综上有两个东西对阅读源代码有益,分别是文章的行文顺序和文章的大标题,文章的大标题一般是对源代码模块的高度概括。
vue data 值如何渲染_vue源码阅读复盘-watcher模块相关推荐
- vue data 值如何渲染_Vue执行流程解析
引言 相信绝大多数的前端小伙伴已记不清做了多少项目,写了多少代码了,每个人如同教科书般地写着Vue代码: // 单文件组件中常见代码 export default {data () {return { ...
- FreeSWITCH 1.10 源码阅读(3)-sofia 模块原理及其呼入处理流程
文章目录 1. 前言 2. 源码分析 2.1 sofia 模块的加载 2.2 呼入的处理流程 1. 前言 SIP(Session Initiation Protocol) 是应用层的信令控制协议,有许 ...
- vue add element报错_Vue 源码解析 -- new Vue -gt; mountComponent 001
这一系列文章的学习出处于Vue.js 技术揭秘 | Vue.js 技术揭秘,有兴趣的伙伴自行阅览 本文涉及到vue源码文件中的 src/core/instance/index.js ==> ne ...
- freeswitch源码阅读 之 sofia模块
sofia模块在freeswitch中的位置非常重要, 所有的sip通话都和它有关, 那么我们就看一下该模块的执行流程. 一. 实现的功能: 1. sip注册; 2. 呼叫; 3. Presence; ...
- Vue 源码阅读学习(三)
第三节:函数柯里化与渲染模型 嘿,朋友们,本节是 Vue 源码阅读的第三讲.Vue 源码阅读系列得到了赞赏,我很高兴,同时希望大家可以给予反馈!我虚心接纳您的意见! 如果没有看之前的第一讲和第二讲的内 ...
- springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解)
博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解) 视 ...
- 【SeaJS】【3】seajs.data相关的源码阅读
在SeaJS官网上推荐了源码阅读顺序,本文并没有采用这个顺序,而是按个人习惯以调试官方示例的方式进行源码阅读.早期版本作者玉伯使用了几个闭包形式,本文源码版本为2.1.1,它的编码方式个人认为更加脚本 ...
- 视频教程-经典Vue从入门到案例到源码分析教程(含资料)-Vue
经典Vue从入门到案例到源码分析教程(含资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国移动等知名企业 ...
- python+vue校园足球联赛管理系统django源码
本次设计任务是要设计一个足球联赛管理系统,通过这个系统能够满足用户及时浏览各种足球联赛信息.系统的主要功能包括主页.个人中心.用户管理.球队赛程管理.球队信息管理.球员信息管理.打赏球员管理.系统管理 ...
最新文章
- 给Linux系统/网络管理员的nmap的29个实用例子
- 总结了200道经典的机器学习面试题 (附参考答案)
- android使用perfetto工具步骤
- nuxt generate 报错 window is not defined
- SpringAOP之@EnableAspectJAutoProxy如何实现自动代理?
- 惠普HP LaserJet 1320n 打印机驱动
- 偷盗者问题。甲乙丙丁四个嫌疑犯,只有一个是偷盗者。在审讯中,四人都有可能说真话或假话
- 人人网冷落主业,押注智能硬件
- b区计算机复试国家线,今年调剂太恐怖 B区考研分数线竟比A区高?
- 打印机服务器文件,打印机服务器ftp配置文件
- 6 Transport
- hadoop ls命令
- bfs hrbust 2188
- linux系统6.8下载,Linux CentOS 6.8 官方原版镜像
- 从回合霸王到网游巨头 网易游戏十年史
- 成功改造企业文化的八项原则
- 2017美国数学建模ICM E题(环境) 翻译 需要可持续城市!(Sustainable Cities Needed!)
- Java GUI编程学习
- 文心一言发布,你怎么看?chatGPT
- skimage的基本使用
热门文章
- Task02:SQL基础查询与排序-天池龙珠计划SQL训练营
- HDU - 1054 Strategic Game (二分图匹配模板题)
- k8s中yaml文件pod的语法(转)
- Hyper-v 虚拟机使用NAT方式连接网络
- mark ubuntu 16.04 64bit + cpu only install mtcnn
- 调试阶段 获取微信小程序openid
- 简易nodejs服务器
- 自制jQuery 复选框全选与反选插件
- simple_html_dom.php 使用 乱码处理作者:gaoming13
- Win7下使用toad连接oracle出现can't initialize OCI -1