赞
踩
一、碎碎念
总觉得自己做项目ts写得很别扭,很多用法都不会也不知从何学起,对于项目结构也是似懂非懂,于是开始看Vben源码,确实看得头皮发麻,但是没办法,还是得一步一步来,希望能坚持看完,刚理解了本地数据存储的封装,确实有学到一些新东西,记录一下。
二、Vben本地数据存储封装思路
我自己在做项目时,存储token之类的数据都是直接操作localStorage,每次要获取就要从localStorage里面取一次,并且每个数据都单独存一个字段,这种做法好像是有那么点问题。在Vben中首先封装了一个Memory类,该类用于记录需要存储在本地的对象,并且在给memory对象赋值时还会设置一个定时器,到期自动移除memory对象中的属性。在存入localStorage或者sessionStorage时使用JSON.stringfy进行序列化成为一个字符串然后存入。每次需要修改storage中内容时先修改对应memory的值,然后整体存入,获取也是获取memory的值。也就是每次都是直接操作memory对象,然后再将对象序列化存入storage中,这样value过期的问题也不用再判断啦。下面上我简化过一些的代码
- // Memory类 memory.ts
- /**
- * time 到期时间戳
- * alive 存活时间 seconds
- */
- export interface Cache<V = any> {
- value?: V
- timeoutId?: ReturnType<typeof setTimeout>
- time?: number
- alive?: number
- }
- const NOT_ALIVE = 0
-
- export class Memory<T = any, V = any> {
- private cache: { [key in keyof T]?: Cache<V> } = {}
- private alive: number
- constructor(alive = NOT_ALIVE) {
- this.alive = alive * 1000
- }
- get getCache() {
- return this.cache
- }
- setCache(cache: { [key in keyof T]?: Cache<V> }) {
- this.cache = cache
- }
-
- get(key: keyof T) {
- return this.cache[key]
- }
- // expires传失效日期时间戳
- set(key: keyof T, value: V, expires?: number) {
- let item = this.get(key)
- if (!expires || expires <= 0) {
- expires = this.alive
- }
- if (item) {
- if (item.timeoutId) {
- clearTimeout(item.timeoutId)
- item.timeoutId = undefined
- }
- item.value = value
- item.alive = expires
- } else {
- item = { value, alive: expires }
- this.cache[key] = item
- }
- const now = new Date().getTime()
- item.time = now + item.alive!
- item.timeoutId = setTimeout(
- () => {
- this.remove(key)
- },
- expires > now ? expires - now : expires
- )
- }
-
- remove(key: keyof T) {
- const item = this.get(key)
- Reflect.deleteProperty(this.cache, key)
- if (item) {
- clearTimeout(item.timeoutId!)
- }
- }
-
- resetCache(cache: { [key in keyof T]: Cache }) {
- Object.keys(cache).forEach((key) => {
- const k = key as keyof T
- const item = cache[k]
- if (item && item.time) {
- const now = new Date().getTime()
- const expire = item.time
- if (expire > now) {
- this.set(k, item.value, expire)
- }
- }
- })
- }
- clear() {
- Object.keys(this.cache).forEach((key) => {
- const k = key as keyof T
- const item = this.cache[k]
- item && item.timeoutId && clearTimeout(item.timeoutId)
- })
- this.cache = {}
- }
- }
- // 封装创建Storage的函数 storageCache.ts
- export interface CreateStorageParams {
- prefixKey: string
- timeout?: Nullable<number>
- }
- /**
- *
- * @param timeout Expiration time in seconds
- * @param prefixKey 前缀
- * @param storage 创建的本地存储类型localStorage或sessionStorage
- */
- export const createStorage = (
- storage = localStorage,
- { prefixKey = '', timeout = null }: CreateStorageParams
- ) => {
- const WebStorage = class WebStorage {
- private storage: Storage
- private prefixKey: string
- constructor() {
- this.storage = storage
- this.prefixKey = prefixKey
- }
- private getKey(key: string) {
- return `${this.prefixKey}${key}`.toUpperCase()
- }
- set(key: string, value: any, expire = timeout) {
- const stringData = JSON.stringify({
- value,
- time: Date.now(),
- expire: expire ? new Date().getTime() + expire * 1000 : null
- })
- this.storage.setItem(this.getKey(key), stringData)
- }
- get(key: string, def: any = null) {
- const val = this.storage.getItem(this.getKey(key))
- if (!val) return def
- try {
- const data = JSON.parse(val)
- const { value, expire } = data
- if (!expire || expire >= new Date().getTime()) {
- return value
- }
- } catch (e) {
- return def
- }
- }
- remove(key: string) {
- this.storage.removeItem(this.getKey(key))
- }
- clear() {
- this.storage.clear()
- }
- }
- return new WebStorage()
- }
- // 导出分别创建localStorage和sessionStorage的函数
- export const createLocalStorage = (
- options: CreateStorageParams = { prefixKey: '', timeout: null }
- ) => {
- return createStorage(localStorage, options)
- }
- export const createSessionStorage = (
- options: CreateStorageParams = { prefixKey: '', timeout: null }
- ) => {
- return createStorage(sessionStorage, options)
- }
- // 存储数据实操类 persistent.ts
- import { Memory } from './memory'
- import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting'
- import {
- ROLES_KEY,
- TOKEN_KEY,
- USER_INFO_KEY,
- APP_LOCAL_CACHE_KEY,
- APP_SESSION_CACHE_KEY
- } from '@/enums/cacheEnum'
- import { UserInfo } from '@/types/store'
- import { toRaw } from 'vue'
- import { createLocalStorage, createSessionStorage } from './storageCache'
-
- interface BasicStore {
- [TOKEN_KEY]: string | number | null | undefined
- [USER_INFO_KEY]: UserInfo
- [ROLES_KEY]: string[]
- }
- type LocalStore = BasicStore
- type SessionStore = BasicStore
- type LocalKeys = keyof LocalStore
- type SessionKeys = keyof SessionStore
-
- const localMemory = new Memory(DEFAULT_CACHE_TIME)
- const sessionMemory = new Memory(DEFAULT_CACHE_TIME)
- const ls = createLocalStorage()
- const ss = createSessionStorage()
-
- function initPersistentMemory() {
- const localCache = ls.get(APP_LOCAL_CACHE_KEY)
- const sessionStorage = ss.get(APP_SESSION_CACHE_KEY)
- localCache && localMemory.resetCache(localCache)
- sessionStorage && sessionMemory.resetCache(sessionStorage)
- }
-
- export class Persistent {
- static getLocal(key: LocalKeys) {
- return localMemory.get(key)?.value
- }
- static setLocal(
- key: LocalKeys,
- value: LocalStore[LocalKeys],
- immadiate = false
- ) {
- localMemory.set(key, toRaw(value))
- // TODO
- immadiate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache)
- }
- static removeLocal(key: LocalKeys, immadiate = false) {
- localMemory.remove(key)
- immadiate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache)
- }
- static clearLocal(immadiate = false) {
- localMemory.clear()
- immadiate && ls.clear()
- }
- static getSession(key: SessionKeys) {
- return sessionMemory.get(key)?.value
- }
- static setSession(
- key: SessionKeys,
- value: SessionStore[SessionKeys],
- immediate = false
- ) {
- sessionMemory.set(key, toRaw(value))
- immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache)
- }
- static removeSession(key: SessionKeys, immediate = false): void {
- sessionMemory.remove(key)
- immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache)
- }
- static clearSession(immediate = false): void {
- sessionMemory.clear()
- immediate && ss.clear()
- }
- static clearAll(immediate = false) {
- localMemory.clear()
- sessionMemory.clear()
- if (immediate) {
- ls.clear()
- ss.clear()
- }
- }
- }
-
- function storageChange(e: any) {
- const { key, newValue, oldValue } = e
- if (!key) {
- Persistent.clearAll()
- return
- }
- if (!!newValue && !!oldValue) {
- if (APP_LOCAL_CACHE_KEY === key) {
- Persistent.clearLocal()
- }
- if (APP_SESSION_CACHE_KEY === key) {
- Persistent.clearSession()
- }
- }
- }
- // 当前页面使用的storage被其他页面修改时触发,符合同源策略,在同一浏览器下的不同窗口,
- // 当焦点页以外的其他页面导致数据变化时,如果焦点页监听storage事件,那么该事件会触发,
- // 换一种说法就是除了改变数据的当前页不会响应外,其他窗口都会响应该事件
- window.addEventListener('storage', storageChange)
-
- initPersistentMemory()
三、一些以前没用过的知识点
(1) 对于timeoutId在ts中如果直接定义为number会报类型错误,我之前都是用window.setTimeout来解决,这次学到了新的写法,但是对于ReturnType还是一知半解
timeoutId: ReturnType<typeof setTimeout>
(2) Reflect
Reflect是ES6为了操作对象而提供的新的API,Reflect对象的设计目的为:将Object对象的一些明显属于语言内部的方法放到Reflect对象上;修改某些Object方法的返回结果,让其变得更合理;让Object操作都变成函数行为。比较常用的有
- // Object.defineProperty无法定义时抛出错误,而Reflect.defineProperty返回false
- Reflect.defineProperty(target, propertyKey, attributes)
- // delete obj[name]
- // Reflect.deleteProperty返回boolean,操作成功或者属性不存在返回true
- Reflect.deleteProperty(obj, name)
- // name in obj
- Reflect.has(obj,name)
(3) 监听storage变化
- // 当前页面使用的storage被其他页面修改时触发,符合同源策略,在同一浏览器下的不同窗口,
- // 当焦点页以外的其他页面导致数据变化时,如果焦点页监听storage事件,那么该事件会触发,
- // 换一种说法就是除了改变数据的当前页不会响应外,其他窗口都会响应该事件
- window.addEventListener('storage', storageChange)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。