vue响应式原理

vue响应式原理

initState

new Vue() => _init() => initState:

function initState (vm: Component) {vm._watchers = []const opts = vm.$optionsif (opts.props) initProps(vm, opts.props)if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {initData(vm)} else {observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)if (opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)}
}

判断该vue实例是否存在propsmethodsdatacomputedwatch进行调用相应的初始化函数

initProps与initData

主要工作是调用defineProperty给属性分别挂载get(触发该钩子时,会将当前属性的dep实例推入当前的Dep.target也就是当前watcher的deps中即它订阅的依赖,Dep.target下文会讲到。且该dep实例也会将当前watcher即观察者推入其subs数组中)、set方法(通知该依赖subs中所有的观察者watcher去调用他们的update方法)。

initComputed

它的作用是将computed对象中所有的属性遍历,并给该属性new一个computed watcher(计算属性中定义了个dep依赖,给需要使用该计算属性的watcher订阅)。也会通过调用defineProperty给computed挂载get(get方法)、set方法(set方法会判断是否传入,如果没传入会设置成noop空函数)
computed属性的get方法是下面函数的返回值函数

function createComputedGetter (key) {return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]if (watcher) {watcher.depend()return watcher.evaluate()}}
}

注意其中的watcher.depend(),该方法让用到该属性的watcher观察者订阅该watcher中的依赖,且该计算属性watcher会将订阅它的watcher推入他的subs中(当计算属性值改变的时候,通知订阅他的watcher观察者)
watcher.evaluate(),该方法是通过调用watcher的get方法(其中需要注意的是watcher的get方法会调用pushTarget将之前的Dep.target实例入栈,并设置Dep.target为该computed watcher,被该计算属性依赖的响应式属性会将该computed watcher推入其subs中,所以当被依赖的响应式属性改变时,会通知订阅他的computed watcher,computed watcher 再通知订阅该计算属性的watcher调用update方法),get方法中调用计算属性key绑定的handler函数计算出值。

initWatch

该watcher 为user watcher(开发人员自己在组件中自定义的)。
initWatch的作用是遍历watch中的属性,并对每个watch监听的属性调用定义的$watch

Vue.prototype.$watch = function (expOrFn: string | Function,cb: any,options?: Object): Function {const vm: Component = thisif (isPlainObject(cb)) {return createWatcher(vm, expOrFn, cb, options)}options = options || {}options.user = true // 代表该watcher是用户自定义watcherconst watcher = new Watcher(vm, expOrFn, cb, options)if (options.immediate) {cb.call(vm, watcher.value)}return function unwatchFn () {watcher.teardown()}}

代码中调用new Watcher的时候,也会同render watcher一样,执行下watcher的get方法,调用pushTarget将当前user watcher赋值给Dep.target,get()中value = this.getter.call(vm, vm)这个语句会触发该自定义watcher监听的响应式属性的get方法,并将当前的user watcher推入该属性依赖的subs中,所以当user watcher监听的属性set触发后,通知订阅该依赖的watcher去触发update,也就是触发该watch绑定的key对应的handler。然后就是调用popTarget出栈并赋值给Dep.target。

$mount

initState初始化工作大致到这里过,接下去会执行$mount开始渲染工作
$mount主要工作:new了一个渲染Watcher,并将updateCompent作为callback传递进去并执行

updateComponent = () => {vm._update(vm._render(), hydrating)}
new Watcher(vm, updateComponent, noop, {before () {if (vm._isMounted) {callHook(vm, 'beforeUpdate')}}}, true /* isRenderWatcher */)

三种watcher中new Watcher的时候,只有computed watcher不会一开始就执行它的get()方法。$mount里面new的这个render watcher会调用get()方法,调用pushTarget将当前render watcher赋值给Dep.target。接下去重头戏来了,调用updateComponent,该方法会执行vm._update(vm._render(), hydrating),其中render函数会触发html中使用到的响应式属性的get钩子。get钩子会让该响应式属性的依赖实例dep将当前的render watcher推入其subs数组中,所以当依赖的响应式属性改变之后,会遍历subs通知订阅它的watcher去调用update()。

例子

可能大家对watcher和dep调来调去一头雾水,我讲个实例

<div id="app"><div>{{a}}</div><div>{{b}}</div></div>
new Vue({el: "#app",data() {return {a:1,}},computed:{b() {return a+1}},
})

我直接从渲染开始讲,只讲跟dep跟watcher有关的
$mount:new一个渲染watcher(watcher的get方法中会将渲染watcher赋值给Dep.target)的时候会触发 vm._update(vm._render(), hydrating),render的时候会获取html中用到的响应式属性,上面例子中先用到了a,这时会触发a的get钩子,其中dep.depend()会将当前的渲染watcher推入到a属性的dep的subs数组中。
接下去继续执行,访问到b(b是计算属性的值),会触发计算属性的get方法。计算属性的get方法是调用createComputedGetter函数后的返回函数computedGettercomputedGetter函数中会执行watcher.depend()。Watcher的depend方法是专门留给computed watcher使用的。刚才上面说过了除了computed watcher,其他两种watcher在new 完之后都会执行他们的get方法,那么computed watcher在new完之后干嘛呢,它会new一个dep。回到刚才说的专门为computed watcher开设的方法watcher.depend(),他的作用是执行this.dep.depend()(computed watcher定义的dep就是在这里使用到的)。this.dep.depend()会让当前的渲染watcher订阅该计算属性依赖,该计算属性也会将渲染watcher推入到它自己的subs([render watcher])中,当计算属性的值修改之后会通知subs中的watcher调用update(),所以计算属性值变了页面能刷新。回到前面说的触发b计算属性的get钩子那里,get钩子最后会执行watcher.evaluate(),watcher.evaluate()会执行computed watcher的get()方法。这时候重点来了,会将Dep.target(render watcher)推入targetStack栈中(存入之后以便待会儿取出继续用),然后将这个计算属性的computed watcher赋值给Dep.target。get方法中value = this.getter.call(vm, vm),会执行computed属性绑定的handler。如上面例子中return a + 1。使用了a那么就一定会触发a的get钩子,get钩子又会调用dep.depend(),dep.depend()会让computed watcher将dep存入它的deps数组中,a的dep会将当前的Dep.target(computed watcher)存入其subs数组中,当前例子中a的subs中就会是[render watcher,computed watcher],所以a值变化会遍历a的subs中的watcher调用update()方法,html中用到的a会刷新,计算属性watcher调用update()方法会通知他自己的subs([render watcher])中render watcher去调用update方法,html中用到的计算属性b才会刷新dom(这里提个醒,我只是粗略的讲,计算属性依赖的属性变化后他不一定会触发更新,他会比较计算完之后的值是否变化)。computed watcher的get()方法最后会调用popTarget(),将之前存入render watcher出栈并赋值给Dep.target,这时候我例子中targetStack就变成空数组了。render watcher的get方法执行到最后也会出栈,这时候会将Dep.target赋值会空。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253558.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动对焦方法学习

实现自动对焦的方法有很多种,可以根据不同的工作原理,将自动对焦技术分成不同种类。 按照系统是否自带信号发射系统,可以分为主动式与被动式两种类型。 主动式对焦方法是由成像系统中的发射装置发出信号,然后再由接收装置接收从被摄景物所反射回来的反馈信号并利用通过计算…

微粒化运营:升级内容产业消费体验(附视频版)

那些最受欢迎的内容平台做对了什么&#xff1f; Facebook和Google是全球互联网广告产业中最早开始微粒化运营的代表&#xff0c;Google的互联网精准广告的思路与微粒化运营是完全相同的&#xff0c;这两家公司也因此获得了全球超过20%的互联网广告的收入。 以Facebook为例&…

chisel快速入门(一)

一、概述 Chisel&#xff08;Constructing Hardware In a Scala Embedded Language&#xff09;是一种嵌入在高级编程语言Scala的硬件构建语言。Chisel实际上只是一些特殊的类定义&#xff0c;预定义对象的集合&#xff0c;使用Scala的用法&#xff0c;所以在写Chisel程序时实际…

U盘基本处理,U盘与移动固态硬盘

一、辨别 USB2.0 和 USB3.0 1、从USB外观上来看&#xff0c;USB2.0通常是白色或黑色&#xff0c;而USB3.0则改观为“高大上”的蓝色接口。 目前&#xff0c;部分笔记本电脑USB接口&#xff0c;已同时提供对USB2.0及USB3.0的支持&#xff0c;我们可以通过接口颜色来区别。 2、从…

UWP_小说在线阅读器:功能要求与技术要求

注&#xff1a;2017年2月23日正式提上日程 学了WP开发也有一年了&#xff0c;也没做过什么软件的。17年进发UWP&#xff0c;锻炼自己一下。做一个开源的小说阅读器吧。 既然开发一个软件。所以要设计一下吧。 功能要求&#xff1a; 可能要用到的技术&#xff0c;这个吗&#xf…

chisel快速入门(二)

上一篇见此&#xff1a; chisel快速入门&#xff08;一&#xff09;_沧海一升的博客-CSDN博客简单介绍了chisel&#xff0c;使硬件开发者能快速上手chisel。https://blog.csdn.net/qq_21842097/article/details/121415341 十、运行和测试 现在我们已经定义了模块&#xff0c;…

【WPF】设置TextBox内容为空时的提示文字

原文:【WPF】设置TextBox内容为空时的提示文字<TextBox Width"150" Margin"5"><TextBox.Resources><VisualBrush x:Key"HintText" TileMode"None" Opacity"0.5" Stretch"None" AlignmentX"Le…

读书笔记之《The Art of Readable Code》Part 2

如何写好流程控制语句(if-else/switch/while/for)使得代码更可读些&#xff1f;(chap 7)* 提高条件语句的可读性(if语句, 或者bool型summary变量) if (length > 10) // Good if (10 < length) // Badwhile (bytes_received < bytes_expected) // Good while (b…

chisel快速入门(三)

前一篇见此&#xff1a; chisel快速入门&#xff08;二&#xff09;_沧海一升的博客-CSDN博客简单介绍了chisel&#xff0c;使硬件开发者能快速上手chisel。https://blog.csdn.net/qq_21842097/article/details/121418806 十四、模块的功能创建 制造用于模块构造的功能接口也…

Redis作者摊上事了:多人要求修改Redis主从复制术语master/slave

作者 | ANTIREZ、小智近日&#xff0c;Redis 作者在 GitHub 上发起了一个“用其他词汇代替 Redis 的主从复制术语”的 issue。有人认为 Redis 中的术语 master/slave &#xff08;主人 / 奴隶&#xff09;冒犯到了别人&#xff0c;要求 Redis 作者 ANTIREZ 修改这个术语&#x…

CMOS图像传感器——2021产品选谈

据Yole统计,2020年全球CMOS图像传感器(CIS)市场规模为207亿美元,出货量为70.08亿颗。跟其它半导体器件一样,CIS也因为疫情和生产周期长,以及各种市场因素,而导致采购和供应链紧张。Yole预计2021年将趋于平稳,销售额相比2020年略有增长(3.2%),将达到214亿美元,出货量…

C++匿名对象

匿名对象&#xff1a; string("hello")就是匿名对象&#xff1b; 匿名对象当做参数引用时必须加const; 转载于:https://www.cnblogs.com/achao123456/p/9634810.html

MVC源码分析 - Action查找和过滤器的执行时机

接着上一篇, 在创建好Controller之后, 有一个 this.ExecuteCore()方法, 这部分是执行的. 那么里面具体做了些什么呢? //ControllerBaseprotected virtual void Execute(RequestContext requestContext) {if (requestContext null){throw new ArgumentNullException("req…

CCIE-MPLS基础篇-实验手册

又一部前期JUSTECH&#xff08;南京捷式泰&#xff09;工程师职业发展系列丛书完整拷贝。 MPLS&#xff08;Multi-Protocol Label Switching&#xff09; 目录 1&#xff1a;MPLS 基础实验.... 3 1.1实验拓扑... 3 1.2实验需求&#xff1a;... 3 1.3实验步骤... 3 1.4校验…

RCA/BNC接口

RCA接口&#xff08;消费类市场&#xff09; RCA 是Radio Corporation of American的缩写词&#xff0c;因为RCA接头由这家公司发明的。RCA俗称莲花插座&#xff0c;又叫AV端子&#xff0c;也称AV 接口&#xff0c;几乎所有的电视机、影碟机类产品都有这个接口。它并不是专门为…

2021手机CIS技术趋势总结

手机摄像头CIS&#xff08;CMOS图像传感器&#xff09;自从突破1亿像素以后&#xff0c;再谈像素数量增大&#xff0c;似乎已经很难让市场产生激烈反应了。这两年电子工程专辑对于手机摄像头CIS&#xff0c;以及更多领域不同类型的图像/视觉传感器&#xff08;如ToF、基于事件的…

关于Unity中NGUI的背包实现之Scrollview(基于Camera)

基于UIPanel的scrollview实现方式在移动设备上的性能不如基于camera的方式。因为UIPanel的scrollview实现方式要渲染很多的道具图&#xff0c;性能自然就降低了。如果是用第二个摄像机camera的方式&#xff0c;物体并没有动&#xff0c;只是拖动第二个摄像机摄像机&#xff0c;…

YUV422/420 format

(在本文中&#xff0c;U 一词相当于 Cb&#xff0c;V 一词相当于 Cr。) YUV422 format as shown below 4:2:2 表示 2:1 的水平取样&#xff0c;没有垂直下采样 YUV420 format as shown below4:2:0 表示 2:1 的水平取样&#xff0c;2:1 的垂直下采样. YUV4:2:0并不是说只有U&…

数字后端——ECO

目录 一、概述 二、ECO分类 1、按时间节点 1&#xff09;流片前的ECO 2&#xff09;流片过程的ECO 3&#xff09;流片后的ECO 2、按网表是否改变 1&#xff09;功能ECO 2&#xff09;时序ECO 三、ECO处理内容 1、设计规则违例 1&#xff09;提升标准单元驱动力 2…

电视百科常识 九大视频接口全接触

1 射频 天线和模拟闭路连接电视机就是采用射频&#xff08;RF&#xff09;接口。作为最常见的视频连接方式&#xff0c;它可同时传输模拟视频以及音频信号。RF接口传输的是视频和音频混合编码后的信号&#xff0c;显示设备的电路将混合编码信号进行一系列分离、解码在输出成像。…