mobx中跟新了数据视图没变化_VUE 2.X - 变化侦测理解与实现

760bdb94a7cbb311ceda57a569059349.png

最近因为赶新项目,好久没跟新文章,请原谅,已经预备了很多主题,请期待~

今天的主题会围绕Vue的变化侦测展开探讨,会先简单的介绍MVVM的原理和实现一个基于“推”的变化侦测。

Agenda如下,

  1. MVVM,会简单的介绍下MVVM的原理,接着会针对以下两点进行着重探讨,

(1) 数据的双向绑定

(2) 变化侦测的两种类型,即“推PUSH”和“拉PULL”。

2. Reactive - Object / Array,模拟实现对一个Object的变化侦测,需要理解以下知识点,

(1) 如何追踪属性变化?

(2) 什么是依赖?

(3) 怎么通知依赖?

(4) 如何收集依赖?

除此之外,针对数组的变化侦测的实现,需要借用拦截器的原理。

3. Compile,模板解析器,即模拟Vue模板是如何被解析的,奔例会简单的解析 v-model 指令和 {{}}指令

4. Demo,Q & A

75d580e047b166f0dbe81d000e44555e.png

NK1 - MVVM

3a23df6d89c9dde553c587fb44f36563.png

MVVM,全称Model-View-ViewModel,即模型-视图-视图模型。

MVVM模式最早是由微软公司提出的一种简化用户界面的时间驱动编程方式,强调分离前端开发与后端业务逻辑的耦合。

View层,视图、模板,负责将数据模型转化为UI。

Model层,模型、数据,泛指从后台获取数据并且能够操作数据。

其核心思想主要是ViewModel层,该层向左与视图进行数据的双向绑定,实现模板自动渲染,向右与Model层通过接口来获取数据或跟新数据。

8284b55d3ec13123ff641572eb8b5f4e.png

区别于MVC:Model层与View层耦合,交互形成一个回路,流程:用户操作->View(负责接收用户的输入操作)->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View)。

37163bf68832c481fa294ffac42674ef.png

区别于MVP:将Controller改名为Presenter,改变了通信方向,View和Model之间不再通信,而是交给Presenter来双向通信,与MVVM的区别是,Presenter需要手动渲染模板。

0b0c12d173c3ea379e1819d0f40ea073.png

用Vue的视角来解释MVVM的话,View视图对应的是Vue的模板和样式表;而ViewModel则是该组件的实例,每个组件的作用域都是独立的,互不影响;而Vue的Model层,在没有引入第三方状态管理的情况下,就是指组件中data属性中的内容。如果系统中引入了全局的Model层,比如VUEX,那Model层也包含一个和Vue组件脱离的对象。

923c7cfafea2d1667645567d8e671b55.png

所以ViewModel的核心原理是来生成和维护视图数据层,而其优点主要体现在数据双向绑定,目前主要流行的响应式框架代表由Angular和Vue,两个又有不同的特点如下,

Angular采用脏数据检查的方式来遍历所有的Watcher,比较数据差异。

Vue采用数据劫持的方式,借用第三方API来劫持数据的每个属性对应的getter和setter方式,然后通知相关依赖,并且Vue2.x引入虚拟DOM来让节点更新优化。

9a49651f06c6325ef4cb77f7b955aa1b.png

NK2 - 变化侦测

什么是变化侦测?

在理解变化侦测原理时,先来了解一个小知识点:渲染,

UI的渲染是指从数据状态生成DOM,接着输出到用户界面显示。

f7286fb672cec9e3a65d7cc50d51eedb.png

我们知道Vue的渲染是声明式的,它在渲染过程中,用Vue模板来映射数据状态跟DOM的关系。

db7ee5a3884acddcc6f2d3fd1d567ebd.png

当状态在更改的时候,我们的用户界面也会跟着一起刷新,但其会出现一个贫瘠,即当你修改的越频繁,导致UI可能会出现跟不上响应的情况,即卡顿。

所以这时候就酝酿出很多出色的前端模板框架,其中一个原理是如何处理UI和数据状态的映射绑定,接着是如何优化UI渲染问题,我们把该原理称之为变化侦测。

变化侦测的作用是侦测数据的变化,即当数据发生改变的时候,会通知视图进行相应的跟新。

NK3 - 变化侦测类型

变化侦测分为两种类型,一种是“推”PUSH,另一种是拉“PULL”,区别如下,

PULL的响应式框架代表有Angular和React,它们的特点主要是:不知道哪个状态变化了,当我收到信号后,就要进行差异“比较”。

Angular采取的是数据层级别的脏检查,而React是侦测View层的DOM节点比较。

cbfa2f1ad3e018fbc4681967ea952e23.png

而PUSH的响应式框架代表为Vue,知道哪个状态变化,可以进行细粒度的更新。

bb5c28369595c04262b7c3bcec144771.png

NK4 - 实现变化侦测步骤

网上有很多种方式去实现一个变化侦测,但是只要掌握其核心命脉就可以贯穿其原理,其实现步骤,即

  1. 实现一个Compile
  2. 实现一个Observer
  3. 实现一个Watcher
  4. 实现一个Dep

Compile,指的是指令解析器,即扫描和解析每一个元素DOM节点元素上绑定的指令,然后根据指令模板替换数据,以及绑定相应的跟新函数,本文中只进行指令 v-model和{{}}指令的解析。

Observer,网上称为“数据监听器”,能够对数据对象进行监听,即当读取或者修改数据属性的时候,能够追踪到变化。

Watcher,网上称为“订阅者”,简单来说,它就是一个依赖,能够监听到其绑定的数据属性发生变化(读取/修改),然后做出改变,所以它通常会是一个模板指令,computed属性等, 后续会介绍。

Dep,网上称为“消息订阅器”,简单来说,它就是一个收集器,它主要负责(1)用来收集依赖(2)当数据属性发生变化时,来通知相关依赖

12ae6ffae072d71f25b594c1b7109587.png

NK5 - Reactive - Object / Array

e81d807c9c8893a262857aaa29d85e9a.png

即模拟实现对一个Object的变化侦测,其效果Demo如下。

87d3f7218e38c043b4b124fff811669c.gif

根据上一页主题,我们根据实现步骤先来画个流程图,

(1)实现一个Observer,让数据对象变得可“观察”

假设我们要监听的Data数据格式如下,

const 

首先我们需要将Data属性("name"和"age")通过Oberver转换成Getter/Setter的形式来追踪变化,即

4a6b2dfcc25b7c48eedde8a7dad31837.png

(2)实现一个Watcher, 依赖即订阅者,能够订阅并收到每个属性变动的通知

假设我们的依赖是模板上的一个指令,如下

<

当外界(模板指令)通过Watcher来读取数据时,会触发依赖所绑定的属性("name")Getter,从而我们可以将Watcher添加到依赖收集器中。

f96052f49474e044cd1d17c6d044485b.png

(3)实现一个Dep,依赖收集器即消息订阅器,用来收集依赖和管理依赖(通知)

上面已经引入了Dep收集依赖的时机,即当Watcher被外界引入的时候。

接着当我们有一个可以改变数据属性的时机,如

<

当数据发生变化时,会触发数据属性的Setter,从而Dep会为绑定的依赖发送通知,Watcher接收到通知后,会向外界发送通知,变化通知外界触发视图跟新

58dbe5216c55d68a310eadefa605e50b.png

当掌握完以上基础原理之后,我们做一个简单的变化侦测实现,参考了一个前端工程师的文章,对其进行了小修改,加深认识。

NK6 - 变化侦测实现 - Observer

实现一个Obsever

980433baa370dac5dcd7f58ebe0cc004.png

如何追踪数据属性变化?针对该问题,我们需要借用第三方API:Object.defineProperty,其原理如下,能够控制对象属性的Getter和Setter权限,缺点是当一个对象后续新增或删除属性是,是无法被追踪。

4d9b09381b75e188d59dc257ecb76993.png
// 1.如何追踪依赖变化

其Console输出结果如下

3c24962ae32b94fe4b3d527befdda1b9.png

优化 - 递归所有的Key:

const 

其Console的输出结果,

49b451134b64888a0b633b50ab95f434.png

NK7 - 变化侦测实现 - Watcher

什么是Watcher?

b0fd684b4b7cbab7bf3a7375767e32d1.png

网上很多种别称,但本文中,它指的就是依赖,它主要的功能是当对象属性发生变化(读取或者修改)时,所要通知的目标,而依赖有很多种形式,例如可能是模板上绑定的指令,

e6db8ed29ed1649d2b67254f8d0d8d27.png

在Vue中依赖还可能是computed属性,warch或者props等

假设,我们定义一个computed属性type,它的逻辑需要根据obj.age,来判断他是成年人还是未成年人。

// 实现计算属性的监听

接着我们再来实现一个计算属性的变化侦测,

// 当计算属性的值被跟新时调用

onComputedUpdate主要的作用是,当计算属性变化的时候,我们需要去通知依赖改变。

当我们调用的时候以下命令时,

console

其Console的输出结果,

ee4fa88a3f618791888bd23051283c5d.png

能够正常刷新type的值,但其特点需要手动才能查看type的值,但是这不是我们想要的,我们想要的是改变属性的时候,能主动通知依赖,所以问题来了,

如何主要通知?

我们知道,当一个可观测对象的属性被读写时,会触发它的getter/setter方法。换个思路,如果我们可以在可观测对象的getter/setter里面,去执行监听器里面的onComputedUpdate()方法,是不是就能够实现让对象主动发出通知的功能呢?

NK8 - 变化侦测实现 - Dep

实现一个Dep,它的主要功能有三点

04eeef82272f973a686480701eac9d4e.png

(1)创建一个“依赖收集器”,专门用来存储依赖用的

2142b426ee7063046e5203180ab7b831.png

它只有一个属性,即"target"属性指向的是依赖,即Watcher实例

(2)修改上面Watcher中绑定要触发的回调函数,但此时的Dep.target就是该Warcher("type")

1be8d61726295ac93c8c2e54553f68ef.png

(3)修改Observer中注入的Getter和Setter,

getter时,收集依赖

setter时,触发依赖

3a4502c94478d4a9dc7ef9188070c6d8.png

其Console的结果显示如下,

1b18b5f52343e690abe62e3ee62c29ce.png

以上就完成了一个变化侦测的具体实现方式,而针对数组的变化侦测会稍微麻烦一点,因为数组的变化侦测是针对它原型的方法,如"push()","pop()","shift()","unshift()","splice()", "sort()","reverse()"。

所以要考虑的点是,如果直接把变化侦测注入在原型方法上,则会影响到所有的数组,所以针对数组的变化侦测,我们需要加多一个类似拦截器的家伙来拦截访问数组的方法。

其特点还要考虑兼容性,如果浏览器提供属性 “_proto__”可以访问到原型,我们就可以在此挂载拦截器,否则,我们只能直接挂载到被侦测的数组上,当作对象方法来用。

046d173c085e4875112181589c50ab79.png

其原理逻辑如下图,

61668d8a826ba4bc22cbc2514b1a17fb.png

NK9 - Compile指令解析器

d9c67e7441e42ae85f076e9e7709625e.png

指令解析器,对每个元素节点的指令进行扫描和解析,例如解析本文中 v-model 指令和 {{}} 指令

这里不深究,网上有很多案例和优化,但其工作原理主要是,解析指令,初始化模板数据,初始化Watcher,即进行依赖收集。

ff286200f63c117de28f10ac25ccad15.png

而代码显示如下

c26b30dd7d4b116af9c25cd526f4b828.png

NK10 - Demo(Q & A)

5803abcf35e45d3b78b9ed64d078f35f.png

参考文章

(1)深入浅出基于“依赖收集”的响应式原理

(2)vue的双向绑定原理及实现

(3)Vue变化侦测 - Berwin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mobx-react是一个用于将Mobx和React框架结合使用的间件工具。它提供了一些装饰器和高阶组件,使得在React组件使用Mobx更加方便。通过使用@inject装饰器,我们可以将Mobx的store注入到组件的props,以便在组件访问和使用store数据。同时,使用@observer装饰器可以将组件转化为观察者,使得组件能够响应store数据变化并进行相应的更新。这样,当store数据发生变化时,被观察的组件也会相应地进行更新。\[3\] #### 引用[.reference_title] - *1* [mobx在react如何使用](https://blog.csdn.net/weixin_43834567/article/details/118414211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [React-Mobx(入门)](https://blog.csdn.net/weixin_57844432/article/details/127923571)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [React之mobxmobx-react 入门](https://blog.csdn.net/sinat_17775997/article/details/82765771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值