目录

  • 准备
  • vue3 reactive原理例子重点讲解
  • vue3 reactive原理例子完整代码

准备:

  1. 下载vue-next

  2. 安装依赖 npm install

核心部分package,里面的vue整合package内各个文件,__tests__里面的单元测试文件(以spect.ts结尾)用来测试功能是否符合预期的

  1. 启动开发环境 npm run dev,默认将代码打包到dist,会把ts编译成js

  2. 引用编译出来的文件即可进行测试vue3.0

packages里面的文件说明

  • compiler- 用于编译

  • reactivity- 用于响应式

  • runtime- 代码运行时的方法

  • server-randerer 服务器端渲染

  • shared 所有包中的共享方法

-core是核心,-dom是基于core封装针对浏览器的

没有对比没有伤害,复习:vue2响应式原理

vue2响应式原理:核心使用Object.defineProperty给属性定义get和set

vue3 reactive的基本使用

let proxy = Vue.reactive({name:'fur'})
//effect:副作用,默认会调用一次,数据变化时触发Vue.effect(()=>{console.log(proxy.name)
})
proxy.name = 'furfur-jiang'
//fur
//furfur-jiang

vue3 reactive原理例子

先分开逐个重点讲解,再整合起来

划重点部分:弱映射表,多层递归,Reflect,数组,依赖收集

弱映射表

需要用到弱引用映射表记录一下, 防止对象已经代理过了或者多次代理同一个对象,即防止多次出现 reactive(object) 与 reactive(proxy);弱引用映射表(es6),使用get和set方法进行存取

弱映射表定义

let toProxy = new WeakMap()//原对象=>被代理对象
let toRaw = new WeakMap()//被代理对象=>原对象

下面代码位于createReactive方法中,因为在createReactive会new observed,目的就是为了避免多次new observed

if(toProxy.get(target)){return proxy
}
//如果对象再次被代理,返回原对象
//即判断该对象已经是代理过的,不再次代理
//不需要使用get即不需要获取,直接判断target有无代理过既可
if(toRaw.has(target)){return target
}
多层递归

关键在于isObject(res)?reactive(res):res;若res为对象,需要递归,与vue2一上来就递归不同,会判断需要递归再递归,

get(target,key,receiver){console.log("获取")let res = Reflect.get(target,key,receiver)return isObject(res)?reactive(res):res;// return target[key] 效果等同Reflect.get方法
}
Reflect

observed的参数baseHandler对象,Reflect.get,Reflect.set,Reflect.deleteProperty

reflect 优点:不会报错,而且有返回值,会替代掉Object上的方法

let baseHandler = {//receiver表示代理后对象
get(target,key,receiver){console.log("获取")let res = Reflect.get(target,key,receiver)return isObject(res)?reactive(res):res;//实现多层代理,若res为对象,需要递归// return target[key] 效果等同Reflect.get方法
},
set(target,key,value,receiver){console.log("设置")let res = Reflect.set(target,key,value,receiver)return res//target[key] = value //效果等同Reflect.set方法,但是上面会有布尔类型返回值,明确设置成功或者失败
},
deleteProperty(target,key){console.log("删除")return Reflect.deleteProperty(target,key)}
}
let observed = new Proxy(target,baseHandler)
数组

存在问题,因为会涉及length修改,所以如果不屏蔽,会触发两次,即两次视图更新,不需要

即第一次将元素 push 进去,第二次将 length 改成 对应的值,但是第二次是无意义的更新,需要屏蔽

关键:判断是否为新增属性

function hasOwn(target,key){return target.hasOwnProperty(key)
}

在set中进行处理

set(target,key,value,receiver){let hadKey = hasOwn(target,key)let oldValue = target[key]let res = Reflect.set(target,key,value,receiver)//判断是否新增属性if(!hadKey){console.log('新增属性')}else if(oldValue !== value){//如果修改的不等于原来的,才会执行,即需要手动改length才会执行console.log('修改属性')}return res//target[key] = value //效果等同Reflect.set方法,但是上面会有布尔类型返回值,明确设置成功或者失败
}

调用:

let arr = [1,2,3]
let proxyArr = reactive(arr)
proxyArr.push(4)  //新增属性
proxyArr.length = 5 //修改属性
依赖收集

activeEffectStacks 栈:先进后出,目的让属性和方法关联,形成响应

effect方法:响应式副作用,默认先执行一次,依赖数据变了再执行

createReactiveEffect方法:创建响应式的副作用

run方法:1.让fn执行, 2.将effect存入栈

//响应式副作用
function effect(fn){//createReactiveEffect:创建响应式的副作用let effect = createReactiveEffect(fn)effect()//默认先执行一次
}
function createReactiveEffect(fn){let effect = function(){return run(effect,fn) }return effect;
}
function run(effect,fn){//防止由于报错而不继续执行try{activeEffectStacks.push(effect);fn();}finally{activeEffectStacks.pop()}
}let obj = reactive({name:'fur'})
effect(()=>{//运行get时进行依赖收集console.log(obj.name)
})

get方法调用(跟踪)track()和set方法调用(触发)trigger()

function track(target,key){//跟踪let effect = activeEffectStacks[activeEffectStacks.length-1]if(effect){//有对应关系才关联,动态创建依赖关系let depsMap = targetsMap.get(target)if(!depsMap){targetsMap.set(target,depsMap = new Map)}let deps = depsMap.get(key)if(!deps){depsMap.set(key,deps = new Set())}if(!deps.has(effect)){deps.add(effect)}}
}function trigger(target,type,key){//触发let depsMap = targetsMap.get(target)if(depsMap){let deps = depsMap.get(key)if(deps){//将当前key对应的effect一次执行deps.forEach(effect=>{effect()})}}
}

完整代码

下面贴出完整代码myreactive.js:包含详细注释

//弱映射表
let toProxy = new WeakMap()//原对象=>被代理对象
let toRaw = new WeakMap()//被代理对象=>原对象function isObject(val){return typeof val === 'object' && val !== null;
}//判断当前对象上有没有这个属性
function hasOwn(target,key){return target.hasOwnProperty(key)
}function reactive(target){return createReactive(target)
}function createReactive(target){//如果已经被代理过了,返回代理结果if(toProxy.get(target)){return proxy}//如果对象再次被代理,返回原对象//即判断该对象已经是代理过的,不再次代理//不需要使用get即不需要获取,直接判断target有无代理过既可if(toRaw.has(target)){return target}if(!isObject(target)){return target}let baseHandler = {//receiver表示代理后对象// reflect 优点:不回报错,而且有返回值,会替代掉Object上的方法get(target,key,receiver){// console.log("获取")let res = Reflect.get(target,key,receiver)//收集依赖,把当前key和effect对应起来//如果目标上的key变了,重新让数组里的fn执行即可track(target,key)return isObject(res)?reactive(res):res;//实现多层代理,若res为对象,需要递归// return target[key] 效果等同Reflect.get方法}, set(target,key,value,receiver){let hadKey = hasOwn(target,key)let oldValue = target[key]let res = Reflect.set(target,key,value,receiver)//判断是否新增属性if(!hadKey){trigger(target,'add',key)console.log('新增属性')}else if(oldValue !== value){trigger(target,'set',key)//如果修改的不等于原来的,才会执行,即需要手动改length才会执行console.log('修改属性')}console.log("设置")return res//target[key] = value //效果等同Reflect.set方法,但是上面会有布尔类型返回值,明确设置成功或者失败},deleteProperty(target,key){console.log("删除")return Reflect.deleteProperty(target,key)}}let observed = new Proxy(target,baseHandler)toProxy.set(target,observed)toRaw.set(observed,target)return observed
}let object = {name:'fur',hobby:{play:'code'}}
let proxy = reactive(object)
//需要记录一下, 防止对象已经代理过了或者多次代理同一个对象
//即防止多次出现 reactive(object) 与 reactive(proxy)
//利用弱引用映射表(es6),使用get和set方法进行存取proxy.name//获取
proxy.name = 'furfur-jiang' //设置 获取
console.log(proxy.name)//furfur-jiang
delete proxy.name  //删除//验证实现了多层代理
proxy.hobby.play = 'study'
console.log(proxy.hobby.play)//study//对数组操作
//存在问题,因为会涉及length修改,所以如果不屏蔽,会触发两次,第一次将4push进去,第二次将length改成4
let arr = [1,2,3]
let proxyArr = reactive(arr)
proxyArr.push(4)    //新增属性
proxyArr.length = 5 //修改属性
console.log(proxyArr)//依赖收集,也称发布订阅
//栈:先进后出,目的让属性和方法关联,形成响应
let activeEffectStacks = []let targetsMap = new WeakMap(); // 集合和hash表
function track(target,key){let effect = activeEffectStacks[activeEffectStacks.length-1]if(effect){//有对应关系才关联,动态创建依赖关系let depsMap = targetsMap.get(target)if(!depsMap){targetsMap.set(target,depsMap = new Map)}let deps = depsMap.get(key)if(!deps){depsMap.set(key,deps = new Set())}if(!deps.has(effect)){deps.add(effect)}}
}function trigger(target,type,key){let depsMap = targetsMap.get(target)if(depsMap){let deps = depsMap.get(key)if(deps){//将当前key对应的effect一次执行deps.forEach(effect=>{effect()})}}}//响应式副作用
function effect(fn){//createReactiveEffect:创建响应式的副作用let effect = createReactiveEffect(fn)effect()//默认先执行一次
}
function createReactiveEffect(fn){let effect = function(){return run(effect,fn) //1.让fn执行, 2.将effect存入栈}return effect;
}
function run(effect,fn){//防止由于报错而不继续执行try{activeEffectStacks.push(effect);fn();}finally{activeEffectStacks.pop()}
}let obj = reactive({name:'fur'})
//effect:响应式副作用,默认先执行一次,依赖数据变了再执行
effect(()=>{//运行get,console.log(obj.name)
})
obj.name = 'furfur-jiang'
obj.name = 'furfur-jiang'//无意义修改,只运行一次
obj.name = 'furfurJiang'//有意义修改,会运行两次

vue3.0初体验(例子解读reactive响应式)相关推荐

  1. vue3.0响应式源码实践,vue3.0初体验

    vue3.0响应式源码实践,vue3.0初体验 镇楼图--杀生丸.jpg vue-next(vue3.0预体验) 1. 使用: 2.vue-next的目录结构 3. reactive内部实现 作者上篇 ...

  2. 来一起学习一下vue3.0 初体验---comeon

    vue3.0 初体验 第一步创建项目 第二步对vue项目进行升级 接下来你就可以尽情的开发啊 第一步创建项目 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页.如果你想学习如何使用Ma ...

  3. php的swoole教程,PHP + Swoole2.0 初体验(swoole入门教程)

    PHP + Swoole2.0 初体验(swoole入门教程) 环境:centos7 + PHP7.1 + swoole2.0 准备工作: 一. swoole 扩展安装 1 .下载swoole cd/ ...

  4. 【Vue3】学习笔记-reactive响应式

    [Vue3]学习笔记-reactive响应式 用ref 设置响应式对象 用reactive 设置响应式对象 总结 用ref 设置响应式对象 JS中设置对象 import { ref } from &q ...

  5. 魅族 android6.0,大屏又好用的 MEIZU 魅蓝MAX及Flyme6.0初体验

    大屏又好用的 MEIZU 魅蓝MAX及Flyme6.0初体验 2017-01-03 15:49:10 9点赞 11收藏 28评论 其实这个手机已经买了快2个月了,奈何拖延症犯了.....一直不想写,拖 ...

  6. Vue2.0 —— 由设计模式切入,实现响应式原理

    Vue2.0 -- 由设计模式切入,实现响应式原理 <工欲善其事,必先利其器> 既然点进来了,麻烦你看下去,希望你有不一样的收获. 大家好,我是vk,好久不见,今天我们一起来盘一盘关于 V ...

  7. 玩转Spring—Spring5新特性之Reactive响应式编程实战

    1 什么是响应式编程 一句话总结:响应式编程是一种编程范式,通用和专注于数据流和变化的,并且是异步的. 维基百科原文: In computing, reactive programming is an ...

  8. 华为晟思Mindspore初体验:全场景统一式框架与函数式微分编程

    华为晟思Mindspore初体验:全场景统一式框架与函数式微分编程 1. Mindspore的全场景统一式框架 2. Mindspore的自动微分技术(Automatic Differentiatio ...

  9. (已更新)【S-CMS企业建站系统 v5.0 】CMS+含小程序+响应式布局+支持手机版网站+支持Q旺旺客服

    闪灵CMS企业建站系统是一款专门为企业建站提供解决方案的产品,前端模板样式主打HTML5模板,以动画效果好.页面流畅.响应式布局为特色,程序主体采用PHP+MYSQL构架,拥有独立自主开发的一整套函数 ...

最新文章

  1. OpenCV + python 实现人脸检测(基于照片和视频进行检测)
  2. Unity_UIWidgets学习笔记03_组件_Container
  3. java dozer 深度_java – Dozer深度映射设置为Set
  4. Oracle sqlplus 各种登录方式
  5. 【转】RMAN 高级恢复
  6. 初学ctypes:打开进程并返回相关信息
  7. css实现风车转动,纯CSS实现的风车转动效果特效演示
  8. win10计算机等应用不能能用,Win10电脑无法安装应用软件的解决办法
  9. 创客系列首发 | 是的,做一名创客,热爱生活!
  10. 微信增加粉丝的108种方法
  11. 《互联网的那些事之时代》第三回:滟滟随波千万里,何处春江无月明
  12. VSCode配置编译MSVC程序
  13. .dms文档打开方式
  14. 怎么免费获取帆软9.0激活码,帆软8.0激活码,如何激活
  15. 菜鸟学Django(持续更新)
  16. Unity优化大全(七)之 GPU-Ligh和其他
  17. 多卡并行训练遇到的问题
  18. 程序员做好这三件事,在哪都能吃得开
  19. Ubuntu18.04 realsenseD435i深度摄像头外参标定的问题
  20. 创新的时机 – 黄金点游戏

热门文章

  1. 【时间转换】将秒转换成“时分秒”格式
  2. Vue3+TS 快速上手 (尚硅谷)
  3. 非常感人非常激发人的潜能的信,你一定要看!
  4. Markdown (CSDN) MD编辑器(三)- 图片缩放、指定尺寸、居中、左对齐、右对齐
  5. centos 命令行模式切换桌面化
  6. XRebel 第一次使用时激活
  7. 汉字一、二级字库的汉字与unicode编码(十六进制)对照表,按照unicode的顺序排列 1
  8. 用CSS画小猪佩奇,你就是下一个社会人! js将“I am a coder”反转成 “coder a am I”,不许用split,join,subString,reverse;求解方法三...
  9. 1024程序员节来了,
  10. OpenGl法向量计算