服务端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 05月29日 22:48

cURL 如何处理 Cookie 和会话管理?

发送 Cookie:-b "name=value" 发单个,-b cookies.txt 从文件读。保存 Cookie:-c cookies.txt 把响应 Set-Cookie 写入文件。同时发送和保存:-b cookies.txt -c cookies.txt(同一文件读写维持会话)。会话管理就是循环请求时每次 -b -c 同一个 cookie 文件,服务器通过 session_id 识别用户。追问-b 和 -H Cookie 有什么区别?-b "key=val" 自动生成 Cookie 头,更简洁。-H "Cookie: key=val" 手动写原始头,适合精确控制。多个 Cookie:-b "a=1" -b "b=2" 或 -H "Cookie: a=1; b=2"。Cookie 文件格式是什么?Netscape 格式:每行 tab 分隔 7 个字段——domain\tflag\tpath\tsecure\texpiration\tname\tvalue。由 -c 自动生成。如何模拟登录后访问受保护页面?curl -c cookies.txt -X POST login-url -d user=xxx -d pass=xxx,然后 curl -b cookies.txt protected-url。JWT 认证则不同——登录响应里的 token 需手动提取放到 Authorization 头。Cookie 和 Session 有什么区别?Cookie 存客户端,Session 存服务器端。Cookie 里的 session_id 是钥匙,服务器用这把钥匙找 Session 数据。cURL 模拟的是 Cookie 传输层。如何调试 Cookie 问题?加 -v 查看 Cookie 和 Set-Cookie:curl -v -b cookies.txt URL 2>&1 | grep -i cookie。常见问题:Domain/Path 不匹配、Secure 标记在 HTTP 下不发送、SameSite 跨站不携带。
服务端阅读 05月29日 22:48

DNS 为什么用 UDP 又用 TCP?各自什么场景?

标准查询用 UDP 53(快、无连接、适合小报文),区域传送(zone transfer)和超过 512 字节的响应用 TCP 53(可靠、支持大报文)。EDNS0 扩展后 UDP 响应可达 4096 字节,超过仍切 TCP。UDP 无需握手一个包搞定查询延迟最低;TCP 需要三次握手,适合需要可靠传输的场景。追问为什么不全部用 TCP?TCP 三次握手增加延迟——DNS 查询通常一个 UDP 包就够,用 TCP 慢 2-3 倍。DNS 查询量巨大,TCP 的连接开销对 DNS 服务器是灾难。UDP 丢包了怎么办?客户端设超时重试(2-5 秒),换个解析器再查。DNS 设计上幂等——重复查询结果一样,丢包重试即可。什么情况响应会超过 512 字节?大量 CNAME 链、DNSSEC 签名记录(很大)、大量 MX 记录。DNSSEC 几乎总是触发 TCP fallback。区域传送为什么必须 TCP?区域传送同步整个 zone 数据可能几百 KB,必须可靠完整。TCP 的有序可靠传输正好满足。DoH/DoT 对 UDP/TCP 的影响?DoH 和 DoT 都用 TCP/TLS 加密。HTTP/3 用 QUIC(基于 UDP),又回到 UDP 但加了加密和可靠传输。传统 DNS 用 UDP 求快,安全场景用 TCP 加密求稳。
服务端阅读 05月29日 22:48

cURL 如何处理大文件下载和断点续传?

断点续传:curl -C - -O URL(-C - 自动检测已下载部分继续)。原理:cURL 发 Range 头请求剩余部分,服务器返回 206 Partial Content。首次下载中断后重新执行同一命令即可续传。大文件优化:--limit-rate 2M 限速、--max-time 3600 超时保护、-# 显示进度条。前提:服务器必须支持 Range 头。追问怎么判断服务器是否支持断点续传?curl -I URL 看响应头:Accept-Ranges: bytes 表示支持,没有则不支持。下载中断后文件名不对怎么办?-O 用 URL 最后一段作为文件名,重定向后可能不对。用 -o 指定文件名,或 -L 跟随重定向后再用 -O。如何并行下载大文件?cURL 本身不支持分段并行下载。用 aria2c -x 16 -s 16 URL(16 连接并行)。或手动分段 curl -r 并合并。下载到一半磁盘满了怎么办?cURL 不会自动清理部分文件。脚本中 df -h 检查磁盘空间,超过 90% 停止下载并告警。大文件场景用 --max-filesize 限制大小。如何验证下载文件完整性?curl URL -o file && shasum -a 256 -c checksum.txt。或 curl URL | sha256sum 对比预期哈希。
服务端阅读 05月29日 22:48

DNS 递归查询和迭代查询有什么区别?

递归查询:客户端问解析器,解析器负责从头到尾查完返回最终结果——客户端只发一次请求。迭代查询:解析器问根服务器拿到 TLD 地址,再问 TLD 拿到权威地址,再问权威拿到最终 IP——解析器自己一步步问。实际流程:客户端→解析器是递归,解析器→根/TLD/权威是迭代。追问为什么不全用递归?根/TLD 服务器不提供递归服务——它们服务全球数十亿客户端,每个递归请求都打到根服务器扛不住。根只告诉你下一步问谁(迭代),解析器负责串联。解析器怎么知道根服务器地址?内置的根提示文件(root hints),包含 13 组根服务器的 IP。BIND/Unbound 等软件自带此文件。递归查询的性能瓶颈在哪?冷启动(缓存全空)需要 4 次 RTT:解析器→根→TLD→权威→拿到 IP。热缓存时 1 次 RTT 甚至 0(本地缓存命中)。解析器缓存命中率是核心指标——正常 >90%。什么是转发解析器?不直接迭代查询,而是把请求转发给上游解析器(如企业 DNS 转发给 8.8.8.8)。减少出口 IP 方便防火墙管理。缺点:多一跳延迟,上游故障全部影响。DNS 预取(Prefetching)是什么?解析器在 TTL 即将过期前主动刷新缓存,避免用户请求命中过期缓存触发完整迭代查询。用户无感知,缓存保持热度。
服务端阅读 05月29日 22:48

DNS 根服务器是什么?全球有多少个?

根服务器是 DNS 层级最顶端,返回所有 TLD(.com/.org/.cn 等)的服务器地址。全球 13 组根服务器(A-M),13 是早期 UDP 512 字节包大小限制的结果。每组通过 Anycast 技术在全球部署数百个实例,实际根服务器节点超过 1000 个。追问只有 13 组不会单点故障吗?Anycast 解决——同一 IP 全球数百个节点,请求自动路由到最近的。某节点故障流量自动切到其他节点。加上解析器缓存,根服务器实际负载不高。根服务器被攻击会怎样?2015 年 DDoS 攻击导致部分根服务器离线数小时,用户基本无感知——解析器有缓存 TTL 未过期前不需要问根。长期(>48h)离线才会影响新域名首次解析。谁在管理根服务器?12 个组织(A-M 各一个),受 ICANN 的 IANA 监督。2016 年根区文件管理权从美国移交 ICANN。中国有根服务器吗?有镜像节点(Anycast 实例),但不是运营方。2019 年引入 F/I/K/L 四组根的镜像,提升国内解析速度和抗断网能力。根区文件仍由 ICANN 管理。如果 13 组全挂了怎么办?解析器缓存能撑数小时到数天。顶级域 TLD 缓存同理。短期全挂影响有限,长期需本地部署替代根区。
服务端阅读 05月29日 22:48

DNS 负载均衡怎么实现?有哪些方案?

DNS 负载均衡就是同一域名返回多个 IP,客户端随机选一个访问。三种方案:轮询(Round Robin)——权威服务器按顺序返回不同 IP,简单但无法感知服务器负载;加权 DNS——不同 IP 返回不同比例,权重控制流量分配;GeoDNS——根据客户端 IP 所在地区返回最近的服务器 IP,CDN 核心技术。局限:无法做健康检查、受 TTL 缓存影响切换慢、客户端可能缓存某个 IP 不轮询。追问DNS 负载均衡和 Nginx 负载均衡有什么区别?DNS 在域名解析层分配 IP,客户端直连服务器——无中间层延迟但无法精细控制。Nginx 在应用层代理——可做健康检查、会话保持、权重动态调整,但增加一跳延迟。生产环境常组合:DNS 做全局负载(多机房),Nginx 做单机房内负载。TTL 设多少合适?负载均衡场景一般 60-300 秒。故障切换时临时改 TTL 为 30 秒。注意运营商 DNS 可能强制延长最低 TTL。如何实现 DNS 健康检查?DNS 本身不做健康检查。需要外部系统:监控服务检测后端可用性,故障时调用 DNS API 删除该 IP 记录。Route53/Cloudflare DNS 都有内置健康检查+自动故障切换。GeoDNS 的原理?权威服务器根据请求来源 IP 判断地理位置(用 GeoIP 数据库),返回该地区最近的服务器 IP。实际用 Anycast + EDNS Client Subnet(ECS)传递客户端子网信息。DNS 负载均衡能做灰度发布吗?能但粗糙——给新版本分配 5% 的 IP 权重逐步提升。粒度太粗且受 TTL 缓存影响。精细灰度用 Istio/Nginx 按请求头/cookie 路由。
服务端阅读 05月29日 22:48

如何用 cURL 编写 API 自动化测试脚本?

cURL 测试脚本 = cURL 命令 + shell 判断 + 状态码/响应体验证。核心模式:curl -s -o /dev/null -w "%{http_code}" URL 拿状态码;curl -s URL | jq .field 提取响应体字段;for/while 循环批量测试。进阶:--fail 返回非零表示 HTTP 错误、--max-time 10 超时控制、--retry 3 重试。追问cURL 和 Postman 做测试哪个好?cURL 适合 CI/CD 管道和脚本化——轻量、无 GUI 依赖、易版本控制。Postman 适合手动探索和团队协作。生产环境两者结合:开发用 Postman 调试,CI 用 cURL 验证。怎么验证响应体内容?curl -s URL | jq -e .status==200,-e 让 jq 在结果为 false 时返回 exit code 1。多字段验证:jq -e ".status==200 and .data.id != null"。如何做性能基准测试?curl -s -w "timetotal: %{timetotal}s\n" -o /dev/null URL。批量测用循环+awk 算平均值。注意 cURL 不含 DNS 缓存预热,首次请求偏慢。怎么处理需要登录的接口?先 curl 登录拿 token:TOKEN=$(curl -s -X POST login-url -d user/pass | jq -r .token),后续带 -H "Authorization: Bearer $TOKEN"。cURL 脚本的缺点?无测试报告、无并行执行、错误定位不友好。超过 20 个接口建议上专用测试框架(pytest+requests/Jest+supertest),cURL 适合轻量级冒烟测试和 CI 快速验证。
服务端阅读 05月29日 22:48

DNS 缓存怎么工作?TTL 起什么作用?

DNS 查询每级都缓存:浏览器缓存(Chrome 约 1 分钟)→ 操作系统缓存(TTL 决定)→ 本地解析器缓存(运营商/企业 DNS)→ 权威服务器。TTL(Time To Live)是权威服务器设定的缓存有效期,单位秒。TTL=300 表示缓存 5 分钟内可直接使用,过期后必须重新查询。越短数据越新但查询越多,越长越快但变更延迟越大。追问浏览器 DNS 缓存怎么看?Chrome 地址栏输入 chrome://net-internals/#dns 查看缓存条目和过期时间。运营商会篡改 TTL 吗?会。部分运营商 DNS 强制最低 TTL(5 分钟甚至 1 小时),无视权威设定的短 TTL。对策:用权威 DNS 服务商的低 TTL + 多地探测验证。负 DNS 缓存是什么?缓存 NXDOMAIN(域名不存在)结果,TTL 由 SOA 的 minimum 字段决定。防止频繁查询不存在的域名。域名刚注册后负缓存未过期,部分地区仍解析失败——等 TTL 过期即可。如何强制刷新 DNS 缓存?macOS:sudo dscacheutil -flushcache。Linux:sudo systemd-resolve --flush-caches。Windows:ipconfig /flushdns。运营商缓存无法控制,只能等 TTL 过期。TTL 设为 0 会怎样?每次查询都穿透到权威服务器,不缓存。生产环境最低建议 60 秒。CDN 场景通常 300-3600 秒。
服务端阅读 05月29日 22:40

区块链有哪些安全威胁?51%攻击和双花攻击怎么防?

三大威胁:51%攻击——控制超半数算力/权益后可回滚交易;双花攻击——同一笔钱花两次;智能合约漏洞——重入、溢出、权限缺失。51%防御:PoS 中 slash 没收质押金让攻击成本远超收益。双花防御:等 6 个确认(BTC)或 finalize(ETH PoS 约 12 分钟)。合约防御:Checks-Effects-Interactions 模式、OpenZeppelin 库、审计。追问51%攻击在 PoS 链上更容易还是更难?更难。PoW 攻击后算力还在手上无损失。PoS 攻击被发现后质押金被 slash 没收,经济损失巨大且不可逆。交易所如何防双花?不信任 0 确认交易。大额等 6+ 确认,用区块链监控检测重组。女巫攻击和 51%攻击什么关系?女巫攻击是创建大量虚假身份影响共识,是 51%攻击的前提手段之一。PoW/PoS 都通过经济成本抑制。MEV 算安全威胁吗?算。前端运行抢跑、三明治攻击让用户成交价变差。Flashbots 等私有内存池将 MEV 收益转移给用户,但不能完全消除。形式化验证能替代审计吗?不能。只能验证你写出 spec 的部分,业务逻辑漏洞不违反 spec 就检测不到。验证+审计+测试网+Bug Bounty 四层防护。
服务端阅读 05月29日 22:40

区块链用了哪些密码学技术?哈希、签名和 Merkle Tree 各起什么作用?

三大核心:哈希函数(SHA-256/Keccak256)保证数据完整性,区块头哈希链接形成链;数字签名(ECDSA/EdDSA)私钥签名公钥验证,证明交易身份;Merkle Tree 只需 log(n) 个哈希即可证明某笔交易在区块中(SPV 验证)。三者协作:签名保证身份,哈希保证不可篡改,Merkle 保证高效验证。追问SHA-256 和 Keccak256 有什么区别?SHA-256 是 NIST 标准(比特币),Keccak256 是 SHA-3 竞赛获胜者(以太坊)。算法完全不同:SHA-2 是 Merkle-Damgard 构造,Keccak 是海绵构造。为什么不直接用公钥当地址?公钥 64 字节太长。以太坊地址=keccak256(pubKey) 后 20 字节,比特币地址=Base58Check(SHA-256+RIPEMD-160(pubKey))。缩短地址减少链上存储。Merkle 证明怎么验证?提供目标哈希+每层兄弟哈希,逐层向上组合计算根哈希,对比是否等于已知根。只需 log2(n) 个哈希值。零知识证明怎么用?zk-SNARK/zk-STARK:证明我知道 X 而不透露 X。应用:Zcash 隐私交易、zkRollup 批量证明、身份验证(证明年满 18 岁不暴露生日)。量子计算会破解这些密码吗?ECDSA 会被 Shor 算法破解。SHA-256 只被 Grover 算法削弱(等价密钥长度减半)。抗量子方案:签名用 lattice-based,哈希加倍长度。以太坊已有抗量子路线图。
服务端阅读 05月29日 22:40

Service Worker 调试有哪些常用方法和工具?

Chrome DevTools 是主战场:Application > Service Workers 面板可查看注册状态、更新、卸载;Sources 面板可打断点;Network 面板勾选 Bypass for network 绕过 SW。Console 里 self.addEventListener(fetch, e => console.log(e.request.url)) 打日志最直接。开发时勾选 Update on reload 避免手动等待激活。追问SW 更新后页面还是旧逻辑?SW 更新走 install→wait→activate。wait 阶段等旧 SW 退出才激活。开发时勾选 Update on reload;生产用 skipWaiting()+clients.claim() 立即接管,但可能导致不兼容。推荐:显示提示让用户主动刷新。如何模拟离线场景?DevTools Network 选 Offline,或 Application > Service Workers 勾选 Offline。也可用 navigator.onLine 检测。注意:离线需要 SW 有缓存策略否则白屏。SW 里 console.log 看不到?SW 运行在独立线程,日志在 Sources > Service Worker 专用控制台。或在 Application > Service Workers 点 inspect 打开专用 DevTools。缓存没命中怎么排查?打印 caches.match(request) 结果:命中返回 Response,未命中 undefined。常见原因:URL 查询参数不一致、request method 不匹配(默认只匹配 GET)、vary 头导致缓存分裂。生产环境如何监控 SW 异常?self.addEventListener(error/unhandledrejection, …) 捕获异常上报。关注注册失败率和缓存命中率。Workbox 的 workbox-google-analytics 可追踪离线 PV。
服务端阅读 05月29日 22:40

DeFi 是什么?核心协议和流动性挖矿怎么运作?

DeFi 是在区块链上用智能合约替代传统金融中介的开放金融体系。三大核心协议:DEX(Uniswap 用 AMM 自动做市取代订单簿)、借贷协议(Aave/Compound,超额抵押借款,利率由供需算法决定)、衍生品(dydx 链上永续合约)。流动性挖矿:用户向资金池提供流动性获得 LP 代币+治理代币奖励。追问AMM 和订单簿哪个好?AMM 无需撮合随时可交易,适合长尾资产。但大额滑点大且有无常损失。订单簿体验接近 CEX,但需要链下撮合引擎。什么是无常损失?LP 提供两种代币后价格比例变化,LP 持有的资产价值低于简单持有两种代币。差值即无常损失。2 倍价格变化约 5.7% 损失。超额抵押为什么不高效?借 100 美元需存 150 美元抵押品。闪电贷解决了这个问题——同一交易内借还无需抵押,但只能用于原子操作。DeFi 的系统性风险?合约漏洞、预言机操纵、治理攻击、可组合性传染。2022 年 Celsius/Luna 崩盘是典型传染案例。流动性挖矿还有利可图吗?蓝筹 APR 从 2020 年 100%+ 降到个位数。现在要看:挖矿收益+交易手续费+代币增值预期综合评估。纯通胀模式不可持续。
服务端阅读 05月29日 22:40

Zustand 有哪些常用中间件?怎么用?

Zustand 中间件就是高阶函数,用函数组合串联:create(devtools(persist(immer((set) => …)))) 从内到外依次包裹。常用 5 个:persist 持久化到 storage;immer 支持 mutable 写法更新嵌套对象;devtools 接入 Redux DevTools;subscribeWithSelector 精确订阅子属性变化;combine 合并多个 slice。追问中间件的执行顺序有讲究吗?有。从内到外:最内层的先执行。devtools(persist(immer(…))) 意味着 immer 先处理,再 persist,最后 devtools 记录。顺序反了会出错。immer 和普通 set 的性能差异?immer 用 Proxy 追踪变更,有额外开销但通常可忽略。大多数项目 immer 的开发体验收益远大于性能损失。自定义中间件怎么做日志?拦截 set 参数,前后打印状态即可。生产环境用 devtools 替代手动日志。persist 的 partialize 怎么用?partialize: (state) => ({ token: state.token, theme: state.theme }) 只持久化指定字段,避免把临时 UI 状态也存进 storage。subscribeWithSelector 解决什么问题?默认 useStore(s => s.items) 用 Object.is 比较,对象每次都是新引用所以总是重渲染。subscribeWithSelector 让你可以用 shallow 比较或自定义 equalityFn。
服务端阅读 05月29日 22:40

PoW、PoS 和 DPoS 共识机制有什么区别?优缺点各是什么?

PoW:算力竞争出块,安全但耗能(BTC 每笔约 1000 kWh)。PoS:持币量决定出块权,节能 99.9% 但有质押中心化风险。DPoS:投票选验证者(如 EOS 21 节点),出块快但去中心化程度低。选型:极致安全选 PoW,效率可扩展选 PoS,高 TPS 选 DPoS。Ethereum 已从 PoW 转为 PoS。追问PoS 不是更容易富者愈富吗?是的,这是核心争议。缓解:设质押上限、slashing 惩罚、流动性质押降低门槛。完全消除不现实,但比 PoW 的矿机垄断门槛低。DPoS 为什么被批评不够去中心化?21 个节点就能控制整条链,容易串谋。选民冷漠也加剧集中。支持者认为出块快(0.5s)、吞吐高,适合特定商业场景。Ethereum 为何选 PoS 不选 DPoS?Casper FFG 允许任意 32 ETH 质押者参与(超 50 万验证者),远比 21 节点分散。DPoS 的高 TPS 代价是牺牲去中心化,Ethereum 选 L2 Rollup 扩容。PoW 的 51%攻击成本怎么算?比特币约需 200 万台矿机,成本超 50 亿美元+每日电费数百万。攻击后 BTC 价格暴跌,矿机变废铁。小算力链就没有这个保障。还有其他共识机制吗?PoA(权威证明,联盟链)、PoH(历史证明,Solana)、BFT 类(Tendermint,2/3 投票确认,确定性快但不适合大规模验证者)。
服务端阅读 05月29日 22:40

Service Worker 如何实现跨域资源的缓存?

Service Worker 可以缓存跨域资源,但有 CORS 限制:fetch 跨域请求默认不发 credentials,响应必须包含 Access-Control-Allow-Origin 头,否则浏览器拦截无法缓存。opaque response(no-cors 模式下)可缓存但无法读取内容,且计入 7 倍缓存配额,最多存活 7 天(Chrome)。CDN 支持 CORS 时务必用 cors 模式。追问opaque response 有什么坑?status 始终为 0,无法读取 body 也不能判断缓存是否有效。占用 7 倍缓存配额,7 天后自动清理。CDN 支持 CORS 务必用 cors 模式。如何让第三方 CDN 走 CORS?CDN 侧配置 Access-Control-Allow-Origin。SW 侧 fetch 时 mode: cors(默认值)。CDN 不支持 CORS 只能退回 no-cors。缓存跨域字体和图片有什么区别?字体必须 CORS 才能被 @font-face 使用(浏览器安全策略)。图片/CSS/JS 不受此限制,但 SW 缓存时 opaque response 仍有 7 天限制。Stale-While-Revalidate 怎么用?caches.match 返回缓存,后台 fetch 更新缓存。先快后新,下次请求就是新数据。跨域 API 的缓存怎么处理?API 通常带 CORS 头可正常缓存。但带 Authorization 的请求不能缓存到共享 Cache,会泄露用户数据。私有数据只缓存内存或 SessionStorage。
服务端阅读 05月29日 22:40

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

定义 interface StoreState 声明所有状态和 action 类型,create()((set) => …) 传入泛型。关键:set((state) => ({ count: state.count + 1 })) 这种函数式更新需要泛型才能正确推断 state 类型。Action 也写在 interface 里,类型签名一目了然。追问set 的类型怎么写才不报错?set 接受 Partial | ((state: StoreState) => Partial)。用 immer middleware 时写法是 set((state) => { state.count += 1 }),不需要返回值。selector 类型怎么保证?useStore(s => s.count) 自动推断为 number。复杂 selector 用 shallow 比较避免重复渲染。不要写 useStore 丢失类型。persist middleware 的泛型怎么传?persist 单独传泛型,和 create 的泛型一致。漏传泛型会导致 set 内部类型丢失。多个 slice 怎么组织类型?按功能拆分文件,每个 slice 导出自己的 interface 和 create,主 store 用组合模式合并。和 Jotai 的类型体验比呢?Jotai 的 atom 天然类型安全,不需要额外泛型。Zustand 需要手动传泛型但更灵活。两者都能做到完全类型安全。
服务端阅读 05月29日 22:40

Zustand 和 Redux 有什么区别?选哪个?

核心区别:Zustand 不需要 Provider 包裹、不需要 reducer/action 模板代码、store 直接用 set 修改。Redux 需要 Provider + createSlice + useDispatch + useSelector 四件套。Zustand 1KB vs Redux+RTK 30KB。选型:新项目/中小团队选 Zustand,已有 Redux 代码库或需要强约束选 Redux。追问Zustand 没有 DevTools 吗?有。import { devtools } from zustand/middleware,用法和 Redux DevTools 一样,时间旅行、action 日志都支持。Redux 的 middleware 体系 Zustand 怎么替代?Zustand 用函数组合替代:create(devtools(persist(immer((set) => …)))) 一行串联。异步不需要 thunk/saga,直接在 action 里 async/await。大型项目 Redux 更稳吗?Redux 的约束在大型团队中减少出错概率。但 Zustand 配合 TypeScript + selector 约定也能达到类似效果。关键是团队规范。能混用吗?可以但不推荐——两套模式增加认知负担。迁移建议:新模块用 Zustand,旧模块保持 Redux,逐步替换。Immer middleware 和 RTK 的区别?都是用 Immer 实现不可变更新,语法几乎一致,底层都是 produce。区别只在包装层。
服务端阅读 05月29日 22:40

React Native 中如何使用 Zustand 管理状态?

和 Web 端完全一样——npm install zustand,create((set) => ({ … })) 创建 store,组件里 useStore(selector) 读取。Zustand 不依赖 DOM API,纯 JS 实现,React Native 直接能用。唯一需要注意的是持久化:Web 用 localStorage,RN 用 mmkv 或 AsyncStorage,配合 zustand/middleware 的 persist 中间件传入不同的 storage adapter。追问Zustand 和 Redux 在 RN 中哪个更合适?Zustand。RN 对包体积敏感,Zustand 1KB vs Redux+RTK 约 30KB。API 更简洁,不需要 Provider 包裹根组件。新项目优先 Zustand。如何做持久化存储?用 MMKV 而非 AsyncStorage——MMKV 同步读写,快 30 倍。persist 中间件传入 createJSONStorage(() => mmkvStorage) 即可。关键字段(token、用户偏好)必须持久化。多个页面共享状态会重复渲染吗?用 selector 精确订阅就不会。useStore(s => s.token) 只在 token 变化时重渲染。切忌 useStore() 全量订阅。后台被系统杀掉 store 会丢吗?没做持久化的会丢。RN 端内存回收后 JS 上下文重建,store 恢复初始值。关键字段必须 persist。导航中需要传递 store 吗?不需要。Zustand 是全局单例,任何组件直接 useStore 即可,不用像 Context 那样逐层传递。
服务端阅读 05月29日 22:35

Zustand 中如何处理异步操作?

Zustand 的 store 就是个普通对象,create 回调里直接写 async 函数即可,不需要 thunk 之类的中间件。写法:在 create((set, get) => ({ ... })) 里定义 async action,内部 await 拿到数据后调 set({ data, loading: false })。手动管理 loading/error 状态是最常见的方式。如果嫌重复,用 zustand/middleware 的 immer 简化嵌套更新,或封装一个 createAsyncAction 工具函数统一处理 loading/success/error 三态。追问Zustand 和 Redux Toolkit 处理异步有什么区别?RTK 需要 createAsyncThunk + extraReducers 处理三态,模板代码多。Zustand 直接在 action 里 await + set,没有中间件概念,代码量少一半以上。RTK 的优势是 DevTools 自动追踪异步状态,Zustand 需要手动 devtools middleware。并发请求怎么处理?两个独立请求各自 async action 并行调用即可。如果需要等全部完成:await Promise.all([fetchA(), fetchB()])。注意竞态问题——快速切换页面时旧请求后到会覆盖新数据,用请求 ID 或 AbortController 取消旧请求。Suspense 能配合 Zustand 用吗?可以,但需要包装成 throw promise 的模式:store 里存 promise 而非数据,组件读时如果 promise 未 resolve 就 throw 出去,Suspense 捕获。推荐用 use 包(React 19 内置)或 suspend-react 简化。不过大多数项目手动 loading 状态更直观,Suspense 方案适合设计系统级别统一处理。如何做请求缓存和去重?简单方案:store 里维护 Map<cacheKey, { data, timestamp }>,action 里先查缓存未过期就直接返回。复杂场景用 SWR 或 TanStack Query 管缓存,Zustand 只管 UI 状态,职责分离更清晰。服务端渲染(SSR)时异步怎么处理?create 时传入 hydrate 数据,客户端 useEffect 里发起请求覆盖。注意避免服务端和客户端数据不一致的 hydration mismatch——初始渲染用服务端数据,客户端请求完成后 set 更新即可。
服务端阅读 05月29日 22:35

Solidity 中 view、pure 和 payable 函数修饰符有什么区别?

view 可读不可写状态变量,pure 不可读也不可写,payable 允许接收 ETH。无修饰符的函数可读可写。view 和 pure 不消耗 gas(外部调用时),因为节点可以本地模拟执行而不上链。但 view/pure 在合约内部被交易调用时,调用者仍需付 gas。payable 的唯一作用是让函数能通过 msg.value 收到 ETH,非 payable 函数收到 ETH 会自动 revert。追问view 函数真的不花 gas 吗?外部调用(call / eth_call)不花 gas,因为是只读模拟。但如果一笔交易内部调用了 view 函数,那笔交易本身要付 gas——view 只是承诺不修改状态,不代表调用它的上下文免费。payable 和 non-payable 的 gas 差异?non-payable 函数开头会自动插入 require(msg.value == 0) 检查(约 200 gas)。payable 跳过这个检查,所以 gas 略低。如果函数明确需要收 ETH,加 payable 既是功能需求也省 gas。为什么编译器会警告"view 函数修改了状态"?因为你在 view 函数里调用了写操作(写 storage、发 ETH、触发事件等)。编译器按修饰符检查,不符合就报错。解决:要么去掉 view(确实需要写),要么确保只读。emit 事件在 view 函数中也不允许,因为事件本身是状态变更的日志。pure 函数里能用 block.timestamp 吗?不能。block.timestamp、block.number、msg.sender 都属于读取区块链状态,pure 里不允许。只允许用函数参数和内存变量做纯计算。如果你需要读链上状态但不写,用 view。接口中的 view/pure 声明有什么用?接口中声明 view/pure 是给编译器的契约——实现合约的对应函数也必须是 view/pure。如果实现合约把 view 改成 non-view,编译会报错。这保证了外部调用者可以安全地用 eth_call 调用而不用发交易。