1. 工作原理分析
SSR 中 useStore 状态持久化原理
- 在 SSR 场景下,服务器首先渲染页面。useStore 状态需要在服务器端初始化,以便页面首次渲染时就包含正确的状态。这通常涉及从数据库、缓存或其他数据源中获取初始状态数据。
- 服务器将包含初始状态的 HTML 发送到客户端。客户端在接收到 HTML 后,会“hydrate”(注水)页面,即重新创建 React 应用并将服务器端传来的状态合并到客户端的 useStore 实例中。这样可以确保客户端的状态与服务器端渲染时的状态无缝衔接,避免页面闪烁或重新渲染不一致的问题。
SSG 中 useStore 状态持久化原理
- SSG 是在构建时生成静态 HTML 文件。对于 useStore 状态,在构建阶段会根据配置或数据源获取初始状态,并将其嵌入到生成的 HTML 文件中。
- 当用户访问页面时,浏览器加载包含预渲染状态的 HTML 文件。客户端同样进行“hydrate”操作,将静态 HTML 中的状态数据整合到客户端的 useStore 实例中,实现状态的持久化。
2. 面临的挑战及技术思路
服务器端初始化 useStore 状态与客户端状态无缝衔接
- 技术思路:
- 在服务器端,创建一个函数来获取初始状态,例如从数据库查询数据。在 Qwik 应用中,可以使用
useServerStore
等机制在服务器端管理状态。
- 将服务器端获取的状态序列化为 JSON 格式,并通过 HTML meta 标签、data - attributes 或其他方式嵌入到 HTML 中。
- 在客户端,在应用启动时,从 HTML 中提取嵌入的状态数据,并将其作为初始状态传递给 useStore。
- 代码示例:
// 服务器端代码
import { getServerSideProps } from '@builder.io/qwik-city';
import { useMyStore } from '~/stores/myStore';
export const getServerSideProps = async () => {
const initialState = await fetchInitialStateFromDB(); // 从数据库获取初始状态
return {
props: {
initialStoreState: JSON.stringify(initialState)
}
};
};
// 客户端代码
import { component$, useStore } from '@builder.io/qwik';
import { useMyStore } from '~/stores/myStore';
export const MyComponent = component$(() => {
const { initialStoreState } = useContext(PageContext);
const myStore = useStore(useMyStore, JSON.parse(initialStoreState));
return <div>{myStore.value}</div>;
});
优化状态传输以减少 SSR/SSG 过程中的数据冗余
- 技术思路:
- 采用差分序列化,只传输状态的变化部分,而不是整个状态对象。可以使用 immer 等库来跟踪状态变化。
- 对于大型状态对象,进行分块传输或懒加载,只在需要时加载相关部分的状态。
- 利用缓存机制,避免重复获取相同的状态数据。在服务器端,可以使用内存缓存或分布式缓存(如 Redis)。
- 代码示例:
import produce from 'immer';
import { useStore } from '@builder.io/qwik';
const myStore = () => {
const state = {
count: 0,
data: []
};
const increment = () => {
state.count++;
};
const addData = (newData) => {
state.data.push(newData);
};
return {
state,
increment,
addData
};
};
const useMyStore = useStore(myStore);
// 在服务器端获取差分状态
const originalState = { count: 0, data: [] };
const newState = produce(originalState, draft => {
draft.count++;
draft.data.push('new item');
});
const diff = produce(originalState, newState, (draft, newState) => {
return draft;
});
// 传输 diff 而不是 newState 以减少冗余
处理不同环境(浏览器、服务器)下 useStore 状态持久化的兼容性问题
- 技术思路:
- 使用环境判断语句,在不同环境下执行不同的初始化逻辑。例如,在服务器端使用 Node.js 的内置模块进行文件读取或数据库查询,而在客户端使用浏览器特定的 API 如
localStorage
或 sessionStorage
进行状态持久化。
- 确保状态管理库(如 useStore)在不同环境下的行为一致。避免使用仅在浏览器或服务器端可用的特定功能,除非进行了适当的环境检查。
- 代码示例:
import { useStore } from '@builder.io/qwik';
import { useState } from 'react';
const myStore = () => {
const isServer = typeof window === 'undefined';
let initialState;
if (isServer) {
initialState = require('fs').readFileSync('server - initial - state.json');
} else {
const storedState = localStorage.getItem('my - store - state');
initialState = storedState? JSON.parse(storedState) : { count: 0 };
}
const state = useState(initialState);
const increment = () => {
state[1](prevState => ({...prevState, count: prevState.count + 1 }));
};
return {
state: state[0],
increment
};
};
const useMyStore = useStore(myStore);