Vue3.0 beta源码学习笔记(一)
今天开始总结学习Vue3.0的基本原理。(reactive笔记)
在Vue3.0中将响应式处理放到reactivity文件夹中,然后将其中的reactive,effect,computed, ref各自抽离分模块编写。首先记录一下创建相应数据的reactive方法。
首先在使用reactive方法创建响应式对象时,需要传入一个目标对象:
1const state = reactive({name : "DreamYI",age : 22})
所以在实现reactive(target)方法时应该基于目标对象进行响应式对象的创建,但是目标对象可能不仅仅是一个普通对象、数组还有可能是set map,所以在处理普通的对象和数组时可以在reactive中自定义并返回一个创建响应式数据的方法createReactiveObject(target,mutableHandler)。
1export function reactive(target){
2 // 创建一个响应式的对象 目标对象可能不一定是数组或者对象 可能还有 set map
3 return createReactiveObject(target,mutableHandler);
4}
下面来实现这个创建响应式数据的方法:
1function createReactiveObject(target,baseHandler){
2 if(!isObject(target)){ // 不是对象直接返回即可 isObject为自定义的工具函数判断参数是否为对象
3 return target;
4 }
5 const observed = new Proxy(target,baseHandler);
6 return observed;
7}
这个函数中返回了代理的对象,下面来实现代理的具体处理过程,实现最基本的访问、修改拦截。也就是填补mutableHandler参数的get set操作。在此之前可以将mutableHandler的实现抽离出来。
1//拦截普通对象的处理
2export const mutableHandler = {
3 get,
4 set,
5 // 除了代理这些方法之外 可能还有很多逻辑 deleteProperty has...
6}
在这个参数对象里我们可以对目标对象做很多的拦截操作,这里只简单实现get set。为了代码简洁,将get set抽离单独编写。
1const get = createGetter();
2const set = createSetter();
3// proxy + reflect => es6 的api
4function createGetter() {
5 return function get(target, key, receiver) { // proxy + reflect
6 const res = Reflect.get(target, key, receiver); // target[key];
7 // todo..
8 console.log('用户对这个对象取值了',target,key);
9 return res
10 }
11}
12
13function createSetter() {
14 return function set(target, key, value, receiver) {
15 const result = Reflect.set(target, key, value, receiver); // target[key] = value
16 // todo...
17 console.log('用户对这个对象取值了',target,key);
18 return result;
19 }
20}
现在已经对普通对象进行了基本的数据拦截
1用户对这个对象取值了 {name: "DreamYi", age: 22} name
2用户对这个对象取值了 {name: "duan", age: 22} name
但是此时在传入target是如果有数组元素时:
1const state = reactive({ name: "DreamYi", age: 22 ,arr:[1,2,3]})
2state.arr.push(4)
并且调用数组的push方法:
1用户对这个对象取值了 {name: "duan", age: 22, arr: Array(3)} arr
结果表示:的确是取到了数组arr 但是并没有增加元素4
这是因为此时取到的arr还是一个普通的数组并不是响应式数据,所以此时需要再次对取到的对象做代理执行reactive():
1function createGetter() {
2 return function get(target, key, receiver) { // proxy + reflect
3 const res = Reflect.get(target, key, receiver); // target[key];
4 // todo..
5 console.log('用户对这个对象取值了',target,key);
6 if (isObject(res)) {
7 return reactive(res)
8 }
9 return res
10 }
11}
此时的结果:
1用户对这个对象取值了 {name: "duan", age: 22, arr: Array(3)} arr
2baseHandlers.js:13 用户对这个对象取值了 (3) [1, 2, 3]
push
3baseHandlers.js:13 用户对这个对象取值了 (3) [1, 2, 3]
length
4baseHandlers.js:29 修改操作 (4) [1, 2, 3, 4] 3
5baseHandlers.js:29 修改操作 (4) [1, 2, 3, 4]
length
但是现在会触发两次修改的操作,对于length属性的操作是没有意义的,所以希望把他屏蔽掉:
1function createSetter() {
2 return function set(target, key, value, receiver) {
3 // 需要判断是修改属性 还是增加属性 ,如果原来的值 和新设置的值一样什么都不做
4 const hadKey = hasOwn(target, key);
5 const oldValue = target[key];
6 const result = Reflect.set(target, key, value, receiver); // target[key] = value
7 // todo...
8 if (!hadKey) {
9 console.log('属性的新增操作',target,key);
10 } else if (hasChanged(value, oldValue)) {
11 console.log('修改操作',target,key);
12 }
13 // 值没有变化什么都不用做
14 return result;
15 }
16}
hadKey()、hasChanged()分别是判断对象中是否存在某个属性,与对象是否发生变化的工具函数;
此时要判断是增加属性还是修改属性,所以如果新址值与旧值一样 那就什么都不用做;此时结果:
1用户对这个对象取值了 {name: "DreamYi", age: 22, arr: Array(3)} arr
2baseHandlers.js:13 用户对这个对象取值了 (3) [1, 2, 3]
push
3baseHandlers.js:13 用户对这个对象取值了 (3) [1, 2, 3]
length
4baseHandlers.js:30 属性的新增操作 (4) [1, 2, 3, 4] 3
此时的新增操作只有索引3;
在vue3.0版本中并不是直接对传入的目标对象进行深度的递归,而是在取值的时候进行代理,也解决了vue2.x版本中不能及时对数组索引变化做响应式处理的问题,在性能上得到了很大的提升。
至此,就实现了vue3.0中简单的代理功能。
- 防止连接Mysql超时,JDBC探活配置
- 剑指offer——面试题10输入一个十进制整数,统计其中二进制1的个数
- 剑指offer——面试题9计算斐波纳切第n个数
- 剑指 offer——面试题8求旋转数组的最小值
- MYSQL INNODB表压缩
- 剑指offer——年龄排序问题
- Mysql Group Replication介绍
- 剑指offer——快速排序
- 架构高性能网站秘笈(四)——反向代理缓存
- 架构高性能网站秘笈(一)——了解衡量网站性能的指标
- MYSQL5.6&5.7编译安装
- 架构高性能网站秘笈(三)——浏览器缓存
- 剑指 offer代码解析——面试题39判断平衡二叉树(高效方法)
- 跟着柴毛毛学Spring(4)——面向切面编程![这里写图片描述](http://img.blog.csdn.net/20171031111402095)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python pty & magic | 加固shell
- 通达OA伪造SESSION | Nmap脚本
- Postman使用完全指南
- meterpreter shell | 加固shell
- R语言绘图:复杂散点图绘制
- windows下部署sentinel模式的Redis主从集群
- tmux | 加固shell
- Windows下离线部署Redis主从集群
- SSH 后门 | Nmap 脚本
- 【Tomcat源码解析】第二章:不用死记硬背记住Tomcat整体架构
- 计划任务后门 | Linux 后门系列
- alias后门 | Linux 后门系列
- vim 后门 | Linux 后门系列
- 个站建设基础教程
- 【Tomcat源码解析】第一章:如何搭建源码阅读环境