最近写了一个浏览器的起始页,是我无数忙碌任务中拼命抽时间赶出来的,很简陋但也是这段时间的一点心血,在无数忙碌生活中留下一个小小的自己的栖息罢了。
JustPure 起始页——摒弃繁杂,唯有纯粹 https://www.justpure.dev/ Link
目前网站位于 cf 全球网络 (国内减速网络),之后我想办法优化回国带宽体验,也会写一个浏览器插件使之直接运行于本地,减少网络开销和卡顿。
界面和设计
额,这次就是我大面积用了 shadcn/ui,跟着组件库走还是可以的,有大佬反馈太花哨…我是计划提供切换一个模糊的关闭功能 (其实之前写了但是实在写不完了,那个开关被我注释掉了)
Redux 的数据持久化
主要是想讲一下这个,首先是在 Next.js 中可以直接参考官方文档:
Redux Toolkit Setup with Next.js https://redux.js.org/usage/nextjs Link
我的计划是维护一个数据仓库,并且会实时同步到 localstorage 做数据持久化
思路是这样的:
首先 Redux 提供了一个 preloadedState 部分,这允许我们先预先加载一部分数据,我们可以利用这个将 localstorage 的内容加载进来,而对于保存,Redux,提供了一个事件订阅 subscribe,我们可以在这里异步进行存储和更新。
思路很明确了,那我们就可以直接开始写了:
加载状态
const loadState = () => {
if (typeof window === 'undefined') {
return undefined;
}
try {
const serializedState = localStorage.getItem('appState');
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
console.error(err);
return undefined;
}
};
保存/覆盖状态
const saveState = (state: RootState) => {
if (typeof window === 'undefined') {
return;
}
try {
const serializedState = JSON.stringify(state);
localStorage.setItem('appState', serializedState);
} catch (err) {
console.error(err);
}
};
于是我们可以在构建 store 中使用这两个函数
export const makeStore = () => {
const store = configureStore({
reducer: {
//...(你的 reducers)
},
preloadedState: loadState(),
});
if (typeof window !== 'undefined') {
store.subscribe(() => {
saveState(store.getState());
});
}
return store;
}
如果你使用的是 js,那么恭喜你已经没有问题了,但是如果是 ts,…这种写法类型推断就无法正常运行了
原因在哪里呢?
其实很简单我们看函数签名就一目了然了,由于 JSON.parse 返回了 any,整个函数返回了 undefined|any,导致类型与 store 不匹配,那么我们就要着手解决这个问题。
我们先集中定义类型:
// 1. 先集中定义所有 reducers
const reducers = {
//...(你的 reducers)
};
然后我们要手动推导 RootStore 的类型:
// 2. 基于 reducers 推导 RootState 类型
export type RootState = {
[K in keyof typeof reducers]: ReturnType<typeof reducers[K]>
};
拿到了类型就可以在加载数据的时候进行类型断言啦:
// 3. 这里解析后断言为 RootState 类型(需确保存储结构与当前 reducers 匹配)
return JSON.parse(serializedState) as RootState;
此时我们组合之前的函数:
export const makeStore = () => {
const store = configureStore({
reducer: reducers, // 使用集中定义的 reducers
preloadedState: loadState(), // 现在类型明确为 RootState | undefined
});
if (typeof window !== 'undefined') {
store.subscribe(() => {
saveState(store.getState());
});
}
return store;
}
此时的类型推断就正常啦:
完整代码直接参考我的仓库:https://github.com/grtsinry43/pure-start/blob/main/lib/store.ts
谈谈想法
这个是我最近真的是拼命挤出时间写的,连这篇文章也很仓促,无论评价是精美还是花哨我都慢慢去改进,提供自定义和多个用户体验,对我来说就当是熟悉这个组件库了,还是练手的成分更多
此项目正处于早期开发阶段,欢迎提出建议和贡献代码
https://github.com/grtsinry43/pure-start https://github.com/grtsinry43/pure-start Link