乐闻世界logo
搜索文章和话题

面试题手册

如何在 Zustand 中使用 TypeScript 确保类型安全?

Zustand 中使用 TypeScript 的方法:定义状态类型 interface UserState { user: { id: string; name: string; email: string; } | null; isLoading: boolean; error: string | null; setUser: (user: UserState['user']) => void; setLoading: (loading: boolean) => void; setError: (error: string | null) => void; logout: () => void; }创建类型化的 Store import { create } from 'zustand'; const useUserStore = create<UserState>((set) => ({ user: null, isLoading: false, error: null, setUser: (user) => set({ user }), setLoading: (loading) => set({ loading }), setError: (error) => set({ error }), logout: () => set({ user: null, error: null }), }));在组件中使用 import { useUserStore } from './store'; function UserProfile() { const user = useUserStore((state) => state.user); const isLoading = useUserStore((state) => state.isLoading); const error = useUserStore((state) => state.error); const logout = useUserStore((state) => state.logout); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; if (!user) return <div>Please login</div>; return ( <div> <h1>Welcome, {user.name}!</h1> <p>Email: {user.email}</p> <button onClick={logout}>Logout</button> </div> ); }类型推断和自动补全TypeScript 会自动推断状态和操作的类型编辑器会提供智能提示和类型检查复杂状态类型对于嵌套状态,可以使用嵌套接口对于动态状态,可以使用泛型中间件的类型支持persist 中间件的类型配置自定义中间件的类型定义最佳实践:为每个 store 创建单独的类型定义文件使用接口明确状态结构和操作签名利用 TypeScript 的类型检查捕获潜在错误结合泛型处理复杂的状态类型
阅读 0·3月7日 11:44

如何在 Zustand 中处理异步操作?

Zustand 中处理异步操作的方法:基本异步操作在 store 中定义异步 action使用 async/await 语法示例: import { create } from 'zustand'; const useUserStore = create((set) => ({ user: null, isLoading: false, error: null, fetchUser: async (userId) => { set({ isLoading: true, error: null }); try { const response = await fetch(`https://api.example.com/users/${userId}`); const user = await response.json(); set({ user, isLoading: false }); } catch (error) { set({ error: error.message, isLoading: false }); } }, }));使用 Promise返回 Promise 以便组件可以等待操作完成示例: javascript fetchUser: async (userId) => { set({ isLoading: true, error: null }); try { const response = await fetch(`https://api.example.com/users/${userId}`); const user = await response.json(); set({ user, isLoading: false }); return user; // 返回结果 } catch (error) { set({ error: error.message, isLoading: false }); throw error; // 抛出错误 } },处理多个异步操作并行执行多个异步操作示例: javascript fetchMultipleData: async () => { set({ isLoading: true }); try { const [user, posts] = await Promise.all([ fetch('https://api.example.com/user').then(res => res.json()), fetch('https://api.example.com/posts').then(res => res.json()) ]); set({ user, posts, isLoading: false }); } catch (error) { set({ error: error.message, isLoading: false }); } },中间件处理使用自定义中间件处理异步操作示例: const asyncMiddleware = (store) => (next) => (action) => { if (typeof action === 'function') { return action(store.getState, store.setState); } return next(action); }; const useStore = create( asyncMiddleware((set, get) => ({ // 状态和操作 })) );最佳实践始终处理加载状态和错误状态为异步操作提供取消机制合理使用 try/catch 捕获错误考虑使用 SWR 或 React Query 处理复杂的异步数据常见异步场景API 调用数据加载和缓存文件上传下载认证和授权操作
阅读 0·3月7日 11:44

Zustand 的中间件有哪些,如何使用它们?

Zustand 常用中间件:persist 中间件功能:将状态持久化到 localStorage、sessionStorage 或自定义存储使用场景:需要保持用户登录状态、用户偏好设置等示例: import { create } from 'zustand'; import { persist } from 'zustand/middleware'; const useStore = create( persist( (set) => ({ user: null, setUser: (user) => set({ user }), }), { name: 'user-storage', // 存储名称 } ) );devtools 中间件功能:集成 Redux DevTools,方便调试状态变化使用场景:开发环境中调试状态管理示例: import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; const useStore = create( devtools( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }) ) );immer 中间件功能:使用 Immer 库简化不可变状态更新使用场景:处理复杂的嵌套状态更新示例: import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; const useStore = create( immer((set) => ({ user: { name: 'John', age: 30 }, updateName: (name) => set((state) => { state.user.name = name; }), })) );combine 中间件功能:组合多个状态切片使用场景:模块化管理复杂状态中间件组合使用:import { create } from 'zustand';import { persist, devtools } from 'zustand/middleware';const useStore = create( devtools( persist( (set) => ({ // 状态和操作 }), { name: 'app-storage' } ) ));自定义中间件:可以根据需要创建自定义中间件,例如日志记录、性能监控等。
阅读 0·3月7日 11:43

Cookie 的 Expires 和 Max-Age 属性有什么区别?如何设置 Cookie 的过期时间?

Expires 和 Max-Age 都是用于控制 Cookie 过期时间的属性,但它们的实现方式和行为有所不同。Expires 属性特点使用绝对时间(GMT 格式)指定 Cookie 的具体过期日期和时间兼容性更好,支持旧版浏览器语法// 设置 Expiresconst expires = new Date();expires.setDate(expires.getDate() + 7); // 7天后过期document.cookie = "token=abc; Expires=" + expires.toUTCString();// 完整示例document.cookie = "token=abc; Expires=Wed, 09 Jun 2026 10:18:14 GMT; Path=/";注意事项时间格式必须是 UTC (GMT)如果设置的时间早于当前时间,Cookie 立即删除客户端和服务器端时间不同步可能导致问题Max-Age 属性特点使用相对时间(秒数)指定 Cookie 从创建开始的有效期更现代的属性,优先级高于 Expires语法// 设置 Max-Age(单位:秒)document.cookie = "token=abc; Max-Age=3600"; // 1小时后过期// 完整示例document.cookie = "token=abc; Max-Age=86400; Path=/"; // 1天后过期特殊值Max-Age=0:立即删除 CookieMax-Age 为负数:立即删除 Cookie不设置 Max-Age:会话 Cookie,浏览器关闭时删除两者对比| 特性 | Expires | Max-Age ||------|---------|---------|| 时间类型 | 绝对时间 | 相对时间 || 单位 | 日期时间字符串 | 秒数 || 优先级 | 低 | 高 || 兼容性 | 所有浏览器 | 现代浏览器 || 时区问题 | 有 | 无 |优先级规则// 同时设置时,Max-Age 优先document.cookie = "token=abc; Expires=Wed, 09 Jun 2026 10:18:14 GMT; Max-Age=3600";// Cookie 会在 1 小时后过期,而不是指定日期使用场景会话 Cookie// 不设置过期时间document.cookie = "sessionId=abc";// 浏览器关闭时删除短期 Cookie// 使用 Max-Age 更清晰document.cookie = "tempToken=abc; Max-Age=1800"; // 30分钟长期 Cookie// 使用 Expires 更直观const expires = new Date();expires.setFullYear(expires.getFullYear() + 1);document.cookie = "rememberMe=true; Expires=" + expires.toUTCString();删除 Cookie// 方法 1:设置过去的 Expiresdocument.cookie = "token=abc; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/";// 方法 2:设置 Max-Age=0document.cookie = "token=abc; Max-Age=0; Path=/";最佳实践优先使用 Max-Age// 推荐document.cookie = "token=abc; Max-Age=3600";// 不推荐(除非需要兼容旧浏览器)document.cookie = "token=abc; Expires=" + new Date(Date.now() + 3600000).toUTCString();兼容性处理function setCookieWithExpiry(name, value, seconds) { let cookieString = `${name}=${value}`; // 优先使用 Max-Age if (typeof seconds === 'number') { cookieString += `; Max-Age=${seconds}`; } else { // 降级使用 Expires const expires = new Date(); expires.setSeconds(expires.getSeconds() + seconds); cookieString += `; Expires=${expires.toUTCString()}`; } document.cookie = cookieString;}安全考虑敏感信息使用短期过期时间"记住我"功能使用长期过期时间定期轮换 Token 并更新过期时间实际应用示例// 登录成功后设置 Cookiefunction setLoginCookie(token, rememberMe) { const options = { httpOnly: true, secure: true, sameSite: 'strict', path: '/' }; if (rememberMe) { // 记住我:30天 options.maxAge = 30 * 24 * 60 * 60; } else { // 不记住:1小时 options.maxAge = 60 * 60; } // 服务器端设置(Node.js Express 示例) res.cookie('authToken', token, options);}
阅读 0·3月6日 23:40

pnpm workspace 如何配置和使用?

pnpm workspace 是 pnpm 内置的 monorepo 解决方案,用于管理多个包在一个仓库中。基本配置:# pnpm-workspace.yamlpackages: - 'packages/*' # 所有 packages 目录下的包 - 'apps/*' # 所有 apps 目录下的应用 - 'shared/*' # 共享模块项目结构示例:my-monorepo/├── pnpm-workspace.yaml├── package.json├── pnpm-lock.yaml├── packages/│ ├── ui/│ │ ├── package.json│ │ └── src/│ └── utils/│ ├── package.json│ └── src/└── apps/ ├── web/ │ ├── package.json │ └── src/ └── server/ ├── package.json └── src/包间依赖引用:// apps/web/package.json{ "name": "@my-org/web", "dependencies": { "@my-org/ui": "workspace:*", // 使用 workspace 协议 "@my-org/utils": "workspace:^1.0.0" }}workspace 协议类型:{ "dependencies": { "package": "workspace:*", // 使用最新版本 "package": "workspace:^", // 兼容版本 "package": "workspace:~", // 近似版本 "package": "workspace:^1.0.0" // 指定版本范围 }}常用命令:# 安装所有依赖pnpm install# 在特定包中运行命令pnpm --filter @my-org/ui buildpnpm -F @my-org/ui build # 简写# 在所有包中运行命令pnpm -r build # 递归执行# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 添加包间依赖pnpm --filter @my-org/web add @my-org/ui优势对比:| 特性 | pnpm workspace | Lerna | Yarn Workspaces ||------|---------------|-------|-----------------|| 内置支持 | ✅ | ❌ 需安装 | ✅ || 依赖共享 | ✅ 硬链接 | ❌ | ✅ 符号链接 || 磁盘效率 | 最高 | 一般 | 中等 || 配置复杂度 | 简单 | 复杂 | 简单 |最佳实践:# 使用 changesets 管理版本pnpm add -Dw @changesets/clipnpm changeset init# 发布流程pnpm changeset # 记录变更pnpm changeset version # 更新版本pnpm -r publish # 发布所有包
阅读 0·3月6日 23:39

pnpm 如何处理 peer dependencies?与 npm 有什么不同?

pnpm 对 peer dependencies 的处理更加严格和正确,这是其重要特性之一。什么是 Peer Dependencies:// react-dom/package.json{ "peerDependencies": { "react": "^18.0.0" // 需要宿主项目提供 react }}npm/Yarn 的问题:# 项目依赖{ "dependencies": { "react": "^17.0.0", "react-dom": "^18.0.0" # 需要 react 18 }}# npm/Yarn 可能静默安装,导致版本不匹配# 运行时才发现问题pnpm 的严格处理:# 安装时报错pnpm install# ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies# react-dom@18.0.0 requires react@^18.0.0 but you have react@17.0.0解决方案:正确安装匹配版本pnpm add react@18 react-dom@18使用 pnpm.overrides// package.json{ "pnpm": { "overrides": { "react": "^18.0.0" } }}忽略 peer dependency(不推荐){ "pnpm": { "peerDependenciesMeta": { "react": { "optional": true } } }}peerDependencies 的正确使用:// 插件包的 package.json{ "name": "my-react-plugin", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "peerDependenciesMeta": { "react-dom": { "optional": true // 可选的 peer dependency } }}monorepo 中的处理:# pnpm-workspace.yamlpackages: - 'packages/*'# packages/plugin/package.json{ "peerDependencies": { "react": "^18.0.0" }}# packages/app/package.json{ "dependencies": { "react": "^18.0.0", "@my-org/plugin": "workspace:*" }}# pnpm 会自动将 app 中的 react 提供给 plugin自动安装 peer dependencies:# .npmrcauto-install-peers=true # 自动安装 peer dependenciesstrict-peer-dependencies=false # 不严格检查对比总结:| 特性 | npm/Yarn | pnpm ||------|----------|------|| 检查时机 | 可能延迟到运行时 | 安装时立即检查 || 错误提示 | 可能不明显 | 明确的错误信息 || 版本冲突 | 可能静默忽略 | 严格报错 || monorepo 支持 | 一般 | 优秀 |
阅读 0·3月6日 23:39

pnpm 常用命令有哪些?与 npm 命令有什么区别?

pnpm 提供了丰富的 CLI 命令来管理依赖和项目。安装命令:# 安装所有依赖pnpm installpnpm i# 安装单个包pnpm add lodashpnpm add lodash@4.17.21# 安装到不同依赖类型pnpm add lodash --save-prod # dependencies (默认)pnpm add lodash --save-dev # devDependenciespnpm add lodash --save-optional # optionalDependenciespnpm add lodash -D # devDependencies 简写pnpm add lodash -O # optionalDependencies 简写# 全局安装pnpm add -g lodashpnpm add --global lodash更新命令:# 更新单个包pnpm update lodashpnpm up lodash# 更新所有依赖pnpm updatepnpm up# 更新到最新版本pnpm update --latestpnpm up -L# 交互式更新pnpm update --interactivepnpm up -i删除命令:# 删除包pnpm remove lodashpnpm rm lodash# 删除多个包pnpm remove lodash express# 删除全局包pnpm remove -g lodash运行脚本:# 运行 package.json 中的脚本pnpm run buildpnpm run test# 简写pnpm buildpnpm test# 传递参数pnpm build -- --watchMonorepo 相关命令:# 在特定包中运行命令pnpm --filter <package-name> buildpnpm -F <package-name> build# 在所有包中运行pnpm -r build# 并行运行pnpm -r --parallel build# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 递归执行命令pnpm -r exec rm -rf node_modules查询命令:# 查看包信息pnpm info lodashpnpm view lodash# 查看已安装的包pnpm listpnpm ls# 查看全局安装的包pnpm list -g# 查看依赖树pnpm list --depth=2# 查看过时的包pnpm outdatedStore 管理命令:# 查看 store 路径pnpm store path# 清理 storepnpm store prune# 验证 storepnpm store verify其他常用命令:# 创建项目pnpm create react-app my-apppnpm create vite# 执行包命令pnpm dlx create-react-app my-app# 导入其他锁文件pnpm import# 链接本地包pnpm link ../local-package# 检查依赖问题pnpm audit# 为什么安装了这个包pnpm why lodash配置命令:# 查看配置pnpm config list# 设置配置pnpm config set store-dir /path/to/store# 删除配置pnpm config delete store-dir命令对比:| npm | yarn | pnpm ||-----|------|------|| npm install | yarn | pnpm install || npm add lodash | yarn add lodash | pnpm add lodash || npm run build | yarn build | pnpm build || npm update | yarn upgrade | pnpm update || npm remove lodash | yarn remove lodash | pnpm remove lodash |
阅读 0·3月6日 23:39

pnpm 的 overrides 和 resolutions 有什么区别?如何使用?

pnpm 提供了强大的依赖覆盖机制,主要通过 overrides 和 resolutions 来实现。pnpm.overrides:用于强制覆盖依赖版本,无论原始依赖声明什么版本。// package.json{ "pnpm": { "overrides": { "lodash": "^4.17.21", "react": "^18.0.0" } }}使用场景:修复安全漏洞{ "pnpm": { "overrides": { "minimist@<1.2.6": "^1.2.6" } }}强制统一版本{ "pnpm": { "overrides": { "typescript": "^5.0.0" } }}替换包{ "pnpm": { "overrides": { "node-sass": "sass" } }}路径覆盖:{ "pnpm": { "overrides": { "react": "$react", // 使用项目中的版本 "webpack>lodash": "^4.17.21" // 只覆盖 webpack 的 lodash } }}resolutions(Yarn 兼容):// package.json{ "resolutions": { "lodash": "^4.17.21" }}区别对比:| 特性 | pnpm.overrides | resolutions ||------|----------------|-------------|| 适用范围 | 所有依赖 | 所有依赖 || 优先级 | 高 | 中 || Yarn 兼容 | ❌ | ✅ || 路径指定 | ✅ | ❌ || 版本引用 | ✅ | ❌ |实际应用示例:// 复杂覆盖场景{ "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0", "antd": "^5.0.0" }, "pnpm": { "overrides": { // 确保所有包使用相同 React 版本 "react": "$react", "react-dom": "$react-dom", // 修复 antd 的某个依赖漏洞 "antd>rc-util": "^5.30.0", // 替换废弃的包 "request": "axios" } }}验证覆盖效果:# 查看实际安装的版本pnpm list react# 查看依赖树pnpm list --depth=10# 查看为什么安装了这个版本pnpm why lodash注意事项:谨慎使用全局覆盖// ❌ 不推荐:全局覆盖可能导致不兼容{ "pnpm": { "overrides": { "react": "^18.0.0" } }}// ✅ 推荐:路径指定更精确{ "pnpm": { "overrides": { "some-package>react": "^18.0.0" } }}更新锁文件# 修改 overrides 后需要重新安装pnpm install# 或强制更新pnpm install --force
阅读 0·3月6日 23:39