// 存储数据 localforage.setItem('username', 'John Doe').then(function() { console.log('数据存储成功!'); }). catch(function(err) { console.error('数据存储失败:', err); }); // 堆代码 duidaima.com // 获取数据 localforage.getItem('username').then(function(value) { console.log('获取的数据:', value); }). catch(function(err) { console.error('获取数据失败:', err); }); // 移除数据 localforage.removeItem('username').then(function() { console.log('数据已成功移除!'); }). catch(function(err) { console.error('数据移除失败:', err); });localforage主要提供了setItem、getItem和removeItem等方法,分别用于存储、获取和移除数据。此外,它还支持许多其他方法,如clear用于清空所有数据、key用于根据索引获取键名等等。
这种自动选择存储后端的方式保证了在各种浏览器环境下都能正常工作,并且利用了现代浏览器提供的更强大的存储机制,从而在性能和存储容量方面获得了最佳的表现。
localforage.setItem('username', 'John Doe').then(function () { console.log('数据存储成功!'); }).catch(function (err) { console.error('数据存储失败:', err); });
在上面的例子中,setItem方法返回一个Promise对象,我们可以使用then和catch方法来处理存储成功和失败的情况。这种方式使得代码逻辑更加清晰和简洁。
localforage允许我们存储JavaScript原生的数据类型,如字符串、数字、数组、对象等等。但是,在底层存储时,数据需要先进行序列化,以便于存储在后端数据库中。而在获取数据时,localforage会自动将存储的序列化数据反序列化为JavaScript原生数据类型。
需要注意的是,虽然localforage可以提供比Cookie更大的存储容量,但不同的浏览器和存储后端对于本地存储的容量限制是有差异的。对于大规模数据存储,仍然需要慎重考虑存储容量的问题,避免超出浏览器的限制。
import React from 'react'; import { useLocalStorage } from './useLocalStorage'; function MyComponent() { const [data, setData] = useLocalStorage('myData', 'default-value',false,'my-domain'); const handleChange = (event) => { setData(event.target.value); }; return ( <div> <input type="text" value={data} onChange={handleChange} /> <p>Current value: {data}</p> </div> ); } export default MyComponent;首先,我们来看一下这个自定义HookuseLocalStorage的参数和返回值:
export function useLocalStorage<T>( key: string, defaultValue: T, isDefaultOnFirst: boolean = true, pathname?: string ) { // ... return [state, updateState] as const; }key: 存储数据时使用的键名,它会被用来在LocalStorage中唯一标识数据。
import { useCallback, useEffect, useRef, useState } from 'react'; import localforage from 'localforage'; export function useLocalStorage<T>( key: string, defaultValue: T, isDefaultOnFirst: boolean = true pathname?: string, ) { const refKey = useRef(key); const refInit = useRef(false); const EventMapRef = useRef(new Map<string, Function[]>()); const EventEmitterRef = useRef( class EventEmitter { static on<T>(key: string, callback: (value: T) => void) { if (EventMapRef.current.has(key)) { EventMapRef.current.get(key)?.push(callback); } else { EventMapRef.current.set(key, [callback]); } return () => { const funcList = EventMapRef.current.get(key); EventMapRef.current.set( key, funcList!.filter((func) => func !== callback), ); }; } static emit<T>(key: string, value: T) { if (EventMapRef.current.has(key)) { EventMapRef.current.get(key)?.forEach((func) => { func(value); }); } } }, ); function getStoredValue() { return new Promise<T>((resolve) => { localforage .getItem(refKey.current) .then((raw) => { if (typeof raw !== 'undefined' && raw !== null) { resolve(raw as T); } else { resolve(defaultValue); } }) .catch((e) => { console.error(e); resolve(defaultValue); }); }); } const [state, setState] = useState( isDefaultOnFirst ? defaultValue : undefined, ); const [initSetList, setInitSetList] = useState<Function[]>([]); useEffect(() => { const path = pathname || location.pathname.replace(/\//g, '_'); refKey.current = `${path}-${key}`; getStoredValue().then((value) => { setState(value); if (initSetList.length) { initSetList.forEach((func) => func()); } }); refInit.current = true; }, [key, pathname]); useEffect(() => { const handleEventEmitter = (eventValue: T) => { if (JSON.stringify(eventValue) !== JSON.stringify(state)) { updateState(eventValue, true); } }; const removeHandler = EventEmitterRef.current.on<T>( key, handleEventEmitter, ); return () => { removeHandler(); }; }, [state]); const updateState = useCallback( (value: T, isEmit?: boolean) => { function updateForageState(currentState: T) { setState(currentState); if (typeof currentState === 'undefined') { localforage.removeItem(refKey.current); } else { localforage .setItem(refKey.current, currentState) .then(() => { if (!isEmit) { console.log('emit'); EventEmitterRef.current.emit(key, currentState); } }) .catch((e) => { console.error(e); }); } } if (!refInit.current) { setInitSetList((list) => [ ...list, updateForageState.bind(useLocalStorage, value), ]); } else { updateForageState(value); } }, [refKey, refInit, key], ); return [state, updateState] as const; }我们分析这个Hook的实现细节: