网站首页 > 技术文章 正文
前言
前段时间在看vue关于proxy的源码,也在头条看了很多文章,之前也写了 一篇关于proxy讨论的文章 。为了更好地理解proxy,打算实际应用到项目中。Web Storage的操作是相对比较繁琐的,正好可以作为应用对象,来提升更好的开发体验。
项目的大体功能已经实现,代码已上传至 github ,也提交了npm包。欢迎大家下载使用,也可进行pr,更好的是点个star。你的认可是我不断前进的动力。
正文
项目名为 proxy-web-storage 。主要是使用proxy代理localStorage和sessionStorage,赋值、取值更加便捷,类型不变,并且支持 null 、 undefined 、 NaN 、 Infinity 等特殊值,还有 Date 、 RegExp 、 Function 等类型的存取。同时可以监听数据变化和设置过期时间。
基本功能——存取、删除
使用方法:
import { local, session } from 'proxy-web-storage';
local.test = 'Hello proxy-web-storage'; // works
delete local.test; // works
复制代码
当然,以上用 web storage 也可以实现。在MDN有指出可以像访问对象一样访问这些值。
localStorage.colorSetting = '#a4509b';
localStorage['colorSetting'] = '#a4509b';
delete localStorage.colorSetting;
复制代码
那么,我的实现有什么新的东西吗?是画蛇添足还是画龙点睛?各位看官接着往下看。
类型保持不变
import { local, session } from 'proxy-web-storage';
// number
local.test = 0;
local.test === 0; // true
// boolean
local.test = false;
local.test === false; // true
// undefined
local.test = undefined;
local.test === undefined; // true
// null
local.test = null;
local.test === null; // true
复制代码
正如上所示,你所赋的值是什么类型,访问的也是对应的类型。而 Web Storage 不管是什么类型,获取的必定是字符串。
localStorage.test = false;
localStorage.test // 'false'
复制代码
这会带来一定的麻烦,如果对 localStorage.test 进行判断的话,那么会返回 true ,这跟我们所期待的并不符合。
当然, proxy-web-storage 还支持 Date 、 RegExp 、 function 类型。
import { local } from 'proxy-web-storage';
// Date
local.test = new Date('2000-01-01T00:00:00.000Z');
local.test.getTime() === 946684800000; // true
// RegExp
local.test = /d(b+)d/g;
local.test.test("cdbbdbsbz"); // true
// function
local.test = function() {
return 'Hello proxy-web-storage!';
};
local.test() === 'Hello proxy-web-storage!'; // true
复制代码
如果是用 localStorage 来操作的话,可以正常保存,但是当再次获取来使用的时候,必须对值进行转换。 Date 还好, Date 构造函数支持 dateString 。 RegExp 会比较麻烦,获取到的值是 '/d(b+)d/g' ,需要拿到 pattern 和 flags 才能重新生成 RegExp 类型。而 function 则是借助 eval 重新生成。
不管怎么说, Date 、 RegExp 、 function 这几种类型的重新生成,多多少少需要写点逻辑代码,使用 proxy-web-storage 则可以省去不少烦恼,所以确定不点个star吗?
直接操作数据
从上面的 Date 和 RegExp 的例子,也可以看出 proxy-web-storage 可以对数据直接进行操作。对于 Object 和 Array ,那么更是方便了,像正常数据一样进行操作,无需进行重复的取值、parse、操作、赋值流程。
proxy-web-storage 让代码更加简洁,舍去重复繁琐逻辑,解放你的双手。
import { local } from 'proxy-web-storage';
// Object
local.test = { hello: 'world' };
local.test.hello = 'proxy-web-storage'; // works
// Array
local.test = ['hello'];
local.test.push('proxy-web-storage'); // works
local.test.length // 2
复制代码
监听数据变化
proxy-web-storage 通过 on 、 once 、 off 可以监听数据的变化。
import { local } from 'proxy-web-storage';
local.on('test', function(newVal, oldVal) {
console.log('test', newVal, oldVal);
});
local.on('test.a', function(newVal, oldVal) {
console.log('test.a', newVal, oldVal);
});
local.test = {};
// test {} undefined
local.test.a = 1;
// test.a 1 undefined
复制代码
对于 Object 和 Array 类型的数据,支持二级监听。 obj.a for Object and list[0] for Array 。还实现了 list.length 的监听,具体实现可滑动至下文查看。
对于 off 方法,如果没有传入key,则移除所有监听对象;如果没有传入callback,则移除指定key的所有回调函数;如果传了callback参数,则只移除callback方法。
设置过期时间
proxy-web-storage 通过 setExpires 、 getExpires 、 removeExpires 可以设置指定项的过期时间。
import { local } from 'proxy-web-storage';
local.test = 'hello proxy-web-storage';
local.setExpires('test', Date.now() + 10000);
// after 10's
local.test // undefined
复制代码
setExpires 如果传入的时间值小于当前时间,则直接删除指定项。
问题
- storage.ts 的 createInstrumentations 方法代码冗余:
(['clear', 'key'] as const).forEach(key => {
instrumentations[key] = target[key].bind(target);
});
instrumentations.getItem = function(keyName: string) {
return get(target, keyName, receiver);
};
instrumentations.removeItem = function(keyName: string) {
return deleteProperty(target, keyName);
};
instrumentations.setItem = function(keyName: string, keyValue: any) {
return set(target, keyName, keyValue, receiver);
};
...
复制代码
原意是模仿vue重写Array方法的写法,但是typescript没入门,怎么写都不对,所以请各位老哥指点一下。
- 关于Array.length的监听
vue是通过 ReactiveEffect 和闭包缓存 oldValue 实现Array.length的监听,可以说watch相关的逻辑都是如此,具体原理可在掘金搜索查看。而我并没有实现 ReactiveEffect 系统,想看通过proxy是否可以进行监听,所以实现上相对会比较繁琐。
length变化的几种情况有:
- push 、 pop 、 shift 、 unshift 、 splice
- let list = []; list.length = 5;
- let list = []; list[5] = 5;
其中第二种情况会触发handler的set,可以直接拿到length的变化,所以按照set的正常逻辑可以完成监听。而第三种并不会触发set,但先按下不表。
第一种情是通过重写以上方法,记录方法执行前的length,以及执行后的length。
(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
lengthAltering = true;
const oldLength: number = this.length;
const res = (proxyMap.get(this) as any)[key].apply(this, args)
if(this.length > oldLength) {
selfEmit(this, 'length', this.length, oldLength);
}
lengthAltering = false;
return res
}
});
复制代码
其中有个 lengthAltering 变量,是用来干什么的呢?
这就要结合第三种情况来讨论了。
let list = []; list.push(1, 2); 相当于 list[0] = 1; list[1] = 2; 。也就是说,第一种情况会影响第三种情况的判断,所以加多一个变量辅助。借助这个变量以及 key 是否大于等于 list.length ,来得到第三种情况的变化监听。
let arrayLength: number | undefined;
if(isArray(target) && !lengthAltering) {
arrayLength = target.length;
}
const result = Reflect.set(target, key, value, receiver);
if(isArray(target) && arrayLength !== undefined && Number(key) >= arrayLength) {
selfEmit(target, 'length', target.length, arrayLength);
}
复制代码
以上是关于Array.length的监听实现,脑袋嗡嗡的,极有可能走了弯路。 想请教有没有更简洁的实现?
猜你喜欢
- 2024-11-14 Vue3.0 响应式数据原理:ES6 Proxy
- 2024-11-14 腾讯面试四问,Are you OK? 腾讯hr面试问题
- 2024-11-14 ES6 四大新功能速成攻略 es6基础入门
- 2024-11-14 高频JavaScript手写面试题及答案 前端面试笔试题手写代码
- 2024-11-14 四大问题之腾讯面试 腾讯公司面试问题
- 2024-11-14 通俗易懂的Vue响应式原理以及依赖收集
- 2024-11-14 45道JS能力测评经典题总结 js基础题及答案解析
- 2024-11-14 JavaScript知识整理 js知识点总结
- 2024-11-14 JavaScript基础之对象与内置对象总结
- 2024-11-14 原生JS灵魂之问(中),看看你是否熟悉JavaScript?
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-