• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Vue3.0数据响应式原理详解

vue 搞代码 4年前 (2022-01-08) 11次浏览 已收录 0个评论

这篇文章主要介绍了Vue3.0数据响应式原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

基于Vue3.0发布在GitHub上的第一版源码(2019.10.05)整理

预备知识

  1. ES6 Proxy,整个响应式系统的基础。
  2. 新的composition-API的基本使用,目前还没有中文文档,可以先通过这个仓库(composition-api-rfc)了解,里面也有对应的在线文档。

先把Vue3.0跑起来

先把vue-next仓库的代码clone下来,安装依赖然后构建一下,vue的package下的dist目录下找到构建的脚本,引入脚本即可。
下面一个简单计数器的DEMO:

   <div id='app'></div>

template和之前一样,同样Vue3也支持手写render的写法,template和render同时存在的情况,优先render。

setup选项是新增的主要变动,顾名思义,setup函数会在组件挂载前(beforeCreate和created生命周期之间)运行一次,类似组件初始化的作用,setup需要返回一个对象或者函数。返回对象会被赋值给组件实例的renderContext,在组件的模板作用域可以被访问到,类似data的返回值。返回函数会被当做是组件的render。具体可以细看文档。

reactive的作用是将对象包装成响应式对象,通过Proxy代理后的对象。

上面的计数器的例子,在组件的setup函数中,创建了一个响应式对象state包含一个count属性。然后创建了一个increment递增的函数,最后将state和increment返回给作用域,这样template里的button按钮就能访问到increment函数绑定到点击的回调,count也能显示在按钮上。我们点击按钮,按钮上的数值就能跟着递增。

下面切入正题,我们就来探究下按钮上count值跟着响应式更新的原理

数据结构

首先列一下主要的一些数据结构,先列在这里,后面提到可以翻回来看看。

ReactiveEffect 一个Function对象,用于执行组件的挂载和更新。

 interface ReactiveEffect { (): any isEffect: true active: boolean raw: Function // 具体执行的函数 deps: Array computed?: boolean scheduler?: (run: Function) => void onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerE<strong style="color:transparent">本文来源gaodai#ma#com搞@@代~&码*网/</strong>vent) => void onStop?: () => void } 

targetMap 类似 {target -> key -> dep}的一个Map结构,用于缓存所有响应式对象和依赖收集。

 export type Dep = Set export type KeyToDepMap = Map export const targetMap: WeakMap = new WeakMap() 

Proxy代理拦截

reactive函数执行,会将传入的target对象通过Proxy包装,拦截它的get,set等,并将代理的target缓存到targetMap,targetMap.set(target, new Map())。

代理的get的时候会调用一个track函数,而set会调用一个triger函数。分别对应依赖收集和触发更新。

 // Proxy get 简化 function get(target: any, key: string | symbol, receiver: any) { // 通过key拿到原始值res const res = Reflect.get(target, key, receiver) // 过滤不需要代理的情况 // ... // 依赖收集 track(target, OperationTypes.GET, key) // 如果取到的值是个对象,将对象再代理包装一下 // Proxy只能代理对象第一层级 return isObject(res) ? reactive(res) : res } // Proxy set 简化 function set( target: any, key: string | symbol, value: any, receiver: any ): boolean { // 一些不需要代理设置的场景 // ... // 设置原始对象的值 const result = Reflect.set(target, key, value, receiver) // 避免重复trigger的逻辑 // ... // 触发通知更新 trigger(target, '更新的类型, 新增key或更新key', key) return result } 

依赖收集和触发更新

组件在render阶段,视图会读取数据对象上的值进行渲染,此时便触发了Proxy的get,由此触发对应的track函数,记录下了对应的ReactiveEffect,也就是常说的依赖收集。

ReactiveEffect其实就可以看作是组件的更新(mount是特殊的update),数据的变更触发trigger,trigger遍历调用track收集的对应的数据的ReactiveEffect,也就是对应有关联的组件的更新。

trigger触发的组件的更新,在render阶段又触发了新一轮的track依赖收集,更新依赖。

 // 简化的 track function track( target: any, type: OperationTypes, key?: string | symbol ) { // 只有在依赖收集阶段才进行依赖收集 // 除了render,其他场景也可能会触发Proxy的get,但不需要进行依赖收集 // activeReactiveEffectStack栈顶包装了当前render的组件的mount和update的逻辑 const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1] // 如果effect为空,说明当前不在render阶段 if (effect) { // ... // =====>初始化对应{target -> key -> dep}的结构 let depsMap = targetMap.get(target) if (depsMap === void 0) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key as string | symbol) if (!dep) { depsMap.set(key as string | symbol, (dep = new Set())) } //  key -> dep}的结构 // 依赖列表里如果没有,add if (!dep.has(effect)) { // 这里将effect作为依赖,缓存到依赖列表 dep.add(effect) effect.deps.push(dep) } } } // 简化的trigger function trigger( target: any, type: OperationTypes, key?: string | symbol, extraInfo?: any ) { // 获取对应target在track过程中缓存的依赖 const depsMap = targetMap.get(target) const effects: Set = new Set() // 省略分类逻辑 depsMap.forEach(dep => { // 将effect分类过滤添加到effects }) const run = (effect: ReactiveEffect) => { // 有个异步调度的过程,nextTick scheduleRun(effect, target, type, key, extraInfo) } effects.forEach(run) } 

大致流程:

总结

现在的代码只有新特性的实现,而且ES6+TS的组合可读性大大提高,编辑器支持也很好,所以相对会好读很多。这里只是简单的理了一下vue 3.0 reactive的整体流程,细节还有很多地方值得学习,继续加油。

以上就是Vue3.0数据响应式原理详解的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Vue3.0数据响应式原理详解

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址