[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject":1017,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject":1039},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":13,"pinned":14,"draft":14,"body":15,"seo":1013,"stem":1014,"id":1015,"extension":1016},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。","技能小册",[12],"react","2026-05-08",false,{"type":16,"value":17,"toc":1000},"minimark",[18,21,40,44,55,59,121,135,144,406,410,444,448,455,466,469,501,516,520,528,535,541,544,547,553,575,612,618,659,677,680,695,698,739,751,754,785,788,892,895,916,919,922,996],[19,20,8],"h1",{"id":8},[22,23,24,25,29,30,33,34,39],"p",{},"新建 React 项目时，官方与社区主流工具为 ",[26,27,28],"strong",{},"Vite","；全栈场景常用 ",[26,31,32],{},"Next.js","。见 ",[35,36,38],"a",{"href":37},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fecology","生态","。",[41,42,43],"h2",{"id":43},"核心概念",[22,45,46,47,50,51,54],{},"React 官方不提供「创建项目」的工具，选择构建工具本质是在回答两个问题：",[26,48,49],{},"要不要 SSR\u002F全栈能力","、",[26,52,53],{},"要不要框架级的路由与数据获取约定","。纯客户端 SPA（后台系统、工具类应用）通常选 Vite；需要 SEO、首屏 HTML、同仓库前后端的应用选 Next.js（或 React Router v7 的 Framework Mode，Remix 已并入其中，不再是独立框架）。",[41,56,58],{"id":57},"vite-react","Vite + React",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm create vite my-app --template react-ts\ncd my-app && pnpm install && pnpm dev\n","bash","",[67,68,69,95],"code",{"__ignoreMap":65},[70,71,74,78,82,85,88,92],"span",{"class":72,"line":73},"line",1,[70,75,77],{"class":76},"sbgvK","pnpm",[70,79,81],{"class":80},"s_sjI"," create",[70,83,84],{"class":80}," vite",[70,86,87],{"class":80}," my-app",[70,89,91],{"class":90},"stzsN"," --template",[70,93,94],{"class":80}," react-ts\n",[70,96,98,102,104,108,111,114,116,118],{"class":72,"line":97},2,[70,99,101],{"class":100},"sptTA","cd",[70,103,87],{"class":80},[70,105,107],{"class":106},"sP7_E"," &&",[70,109,110],{"class":76}," pnpm",[70,112,113],{"class":80}," install",[70,115,107],{"class":106},[70,117,110],{"class":76},[70,119,120],{"class":80}," dev\n",[122,123,124,128],"ul",{},[125,126,127],"li",{},"开发：原生 ESM + HMR。",[125,129,130,131,134],{},"生产：",[67,132,133],{},"vite build"," 输出静态资源，可部署到任意静态托管。",[22,136,137,138,39],{},"配置见 ",[35,139,143],{"href":140,"rel":141},"https:\u002F\u002Fcn.vite.dev\u002F",[142],"nofollow","Vite 文档",[60,145,149],{"className":146,"code":147,"language":148,"meta":65,"style":65},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F vite.config.ts 常见配置\nimport { defineConfig } from \"vite\";\nimport react from \"@vitejs\u002Fplugin-react\";\nimport path from \"node:path\";\n\nexport default defineConfig({\n  plugins: [react()],\n  resolve: {\n    alias: { \"@\": path.resolve(__dirname, \"src\") },\n  },\n  server: {\n    proxy: {\n      \"\u002Fapi\": \"http:\u002F\u002Flocalhost:3000\", \u002F\u002F 开发期代理后端接口，规避 CORS\n    },\n  },\n});\n","ts",[67,150,151,157,189,209,228,235,253,274,285,333,339,349,359,384,390,395],{"__ignoreMap":65},[70,152,153],{"class":72,"line":73},[70,154,156],{"class":155},"sutJx","\u002F\u002F vite.config.ts 常见配置\n",[70,158,159,163,166,170,173,176,180,183,186],{"class":72,"line":97},[70,160,162],{"class":161},"sVHd0","import",[70,164,165],{"class":106}," {",[70,167,169],{"class":168},"su5hD"," defineConfig",[70,171,172],{"class":106}," }",[70,174,175],{"class":161}," from",[70,177,179],{"class":178},"sjJ54"," \"",[70,181,182],{"class":80},"vite",[70,184,185],{"class":178},"\"",[70,187,188],{"class":106},";\n",[70,190,192,194,197,200,202,205,207],{"class":72,"line":191},3,[70,193,162],{"class":161},[70,195,196],{"class":168}," react ",[70,198,199],{"class":161},"from",[70,201,179],{"class":178},[70,203,204],{"class":80},"@vitejs\u002Fplugin-react",[70,206,185],{"class":178},[70,208,188],{"class":106},[70,210,212,214,217,219,221,224,226],{"class":72,"line":211},4,[70,213,162],{"class":161},[70,215,216],{"class":168}," path ",[70,218,199],{"class":161},[70,220,179],{"class":178},[70,222,223],{"class":80},"node:path",[70,225,185],{"class":178},[70,227,188],{"class":106},[70,229,231],{"class":72,"line":230},5,[70,232,234],{"emptyLinePlaceholder":233},true,"\n",[70,236,238,241,244,247,250],{"class":72,"line":237},6,[70,239,240],{"class":161},"export",[70,242,243],{"class":161}," default",[70,245,169],{"class":246},"sGLFI",[70,248,249],{"class":168},"(",[70,251,252],{"class":106},"{\n",[70,254,256,260,263,266,268,271],{"class":72,"line":255},7,[70,257,259],{"class":258},"skxfh","  plugins",[70,261,262],{"class":106},":",[70,264,265],{"class":168}," [",[70,267,12],{"class":246},[70,269,270],{"class":168},"()]",[70,272,273],{"class":106},",\n",[70,275,277,280,282],{"class":72,"line":276},8,[70,278,279],{"class":258},"  resolve",[70,281,262],{"class":106},[70,283,284],{"class":106}," {\n",[70,286,288,291,293,295,297,301,303,305,308,311,314,317,320,322,325,327,330],{"class":72,"line":287},9,[70,289,290],{"class":258},"    alias",[70,292,262],{"class":106},[70,294,165],{"class":106},[70,296,179],{"class":178},[70,298,300],{"class":299},"sZUrc","@",[70,302,185],{"class":178},[70,304,262],{"class":106},[70,306,307],{"class":168}," path",[70,309,310],{"class":106},".",[70,312,313],{"class":246},"resolve",[70,315,316],{"class":168},"(__dirname",[70,318,319],{"class":106},",",[70,321,179],{"class":178},[70,323,324],{"class":80},"src",[70,326,185],{"class":178},[70,328,329],{"class":168},") ",[70,331,332],{"class":106},"},\n",[70,334,336],{"class":72,"line":335},10,[70,337,338],{"class":106},"  },\n",[70,340,342,345,347],{"class":72,"line":341},11,[70,343,344],{"class":258},"  server",[70,346,262],{"class":106},[70,348,284],{"class":106},[70,350,352,355,357],{"class":72,"line":351},12,[70,353,354],{"class":258},"    proxy",[70,356,262],{"class":106},[70,358,284],{"class":106},[70,360,362,365,368,370,372,374,377,379,381],{"class":72,"line":361},13,[70,363,364],{"class":178},"      \"",[70,366,367],{"class":299},"\u002Fapi",[70,369,185],{"class":178},[70,371,262],{"class":106},[70,373,179],{"class":178},[70,375,376],{"class":80},"http:\u002F\u002Flocalhost:3000",[70,378,185],{"class":178},[70,380,319],{"class":106},[70,382,383],{"class":155}," \u002F\u002F 开发期代理后端接口，规避 CORS\n",[70,385,387],{"class":72,"line":386},14,[70,388,389],{"class":106},"    },\n",[70,391,393],{"class":72,"line":392},15,[70,394,338],{"class":106},[70,396,398,401,404],{"class":72,"line":397},16,[70,399,400],{"class":106},"}",[70,402,403],{"class":168},")",[70,405,188],{"class":106},[41,407,409],{"id":408},"create-react-appcra","Create React App（CRA）",[22,411,412,417,418,421,422,424,425,427,428,431,432,435,436,439,440,443],{},[35,413,416],{"href":414,"rel":415},"https:\u002F\u002Fcreate-react-app.dev\u002F",[142],"create-react-app"," 已进入",[26,419,420],{},"维护模式","，官方文档建议新项目选用框架或 Vite。已有 CRA 项目可继续维护，但不宜作为新起点。若需要迁移旧 CRA 项目到 Vite，通常步骤为：安装 ",[67,423,182],{}," + ",[67,426,204],{},"，把 ",[67,429,430],{},"public\u002Findex.html"," 移到根目录并调整脚本引用方式，环境变量前缀由 ",[67,433,434],{},"REACT_APP_"," 改为 ",[67,437,438],{},"VITE_","，逐步替换 Webpack 专属配置（如 ",[67,441,442],{},"craco"," 自定义项）。",[41,445,447],{"id":446},"nextjs全栈-ssr","Next.js（全栈 \u002F SSR）",[22,449,450,454],{},[35,451,32],{"href":452,"rel":453},"https:\u002F\u002Fnextjs.org\u002F",[142]," 基于 React，提供：",[122,456,457,460,463],{},[125,458,459],{},"文件系统路由（App Router）。",[125,461,462],{},"服务端组件（RSC）、SSR、SSG。",[125,464,465],{},"API Routes \u002F Server Actions 等。",[22,467,468],{},"适合需要 SEO、首屏 HTML、同仓库前后端的应用。纯客户端 SPA 仍可用 Vite。",[60,470,474],{"className":471,"code":472,"language":473,"meta":65,"style":65},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F app\u002Fusers\u002F[id]\u002Fpage.tsx（App Router，服务端组件）\nexport default async function UserPage({ params }: { params: { id: string } }) {\n  const user = await fetch(`https:\u002F\u002Fapi.example.com\u002Fusers\u002F${params.id}`).then((r) => r.json());\n  return \u003Ch1>{user.name}\u003C\u002Fh1>;\n}\n","tsx",[67,475,476,481,486,491,496],{"__ignoreMap":65},[70,477,478],{"class":72,"line":73},[70,479,480],{},"\u002F\u002F app\u002Fusers\u002F[id]\u002Fpage.tsx（App Router，服务端组件）\n",[70,482,483],{"class":72,"line":97},[70,484,485],{},"export default async function UserPage({ params }: { params: { id: string } }) {\n",[70,487,488],{"class":72,"line":191},[70,489,490],{},"  const user = await fetch(`https:\u002F\u002Fapi.example.com\u002Fusers\u002F${params.id}`).then((r) => r.json());\n",[70,492,493],{"class":72,"line":211},[70,494,495],{},"  return \u003Ch1>{user.name}\u003C\u002Fh1>;\n",[70,497,498],{"class":72,"line":230},[70,499,500],{},"}\n",[22,502,503,504,507,508,511,512,515],{},"服务端组件在",[26,505,506],{},"构建\u002F请求时","于服务器执行，不会把数据获取逻辑打进客户端 JS，有利于减少包体积并简化数据流；需要交互（",[67,509,510],{},"useState","、事件）的部分再拆出 ",[67,513,514],{},"\"use client\""," 组件。",[41,517,519],{"id":518},"目录习惯vite-spa","目录习惯（Vite SPA）",[60,521,526],{"className":522,"code":524,"language":525},[523],"language-text","src\u002F\n  components\u002F\n  pages\u002F\n  hooks\u002F\n  App.tsx\n  main.tsx\n","text",[67,527,524],{"__ignoreMap":65},[22,529,530,531,534],{},"按团队规模可引入 ",[67,532,533],{},"features\u002F"," 等分层，无唯一标准：",[60,536,539],{"className":537,"code":538,"language":525},[523],"src\u002F\n  features\u002F\n    user\u002F\n      components\u002F\n      hooks\u002F\n      api.ts\n    order\u002F\n  shared\u002F\n    ui\u002F\n    hooks\u002F\n  app\u002F\n    router.tsx\n    main.tsx\n",[67,540,538],{"__ignoreMap":65},[22,542,543],{},"「按功能（feature）分层」比「按类型（components\u002Fhooks\u002Fpages）分层」在中大型项目中更容易维护，因为一个功能的相关文件都聚在一起，删除功能时也更容易连带删除对应目录。",[41,545,546],{"id":546},"环境变量",[22,548,549,550,552],{},"Vite 仅暴露以 ",[67,551,438],{}," 前缀的变量到客户端：",[60,554,556],{"className":62,"code":555,"language":64,"meta":65,"style":65},"# .env\nVITE_API_BASE=https:\u002F\u002Fapi.example.com\n",[67,557,558,563],{"__ignoreMap":65},[70,559,560],{"class":72,"line":73},[70,561,562],{"class":155},"# .env\n",[70,564,565,568,572],{"class":72,"line":97},[70,566,567],{"class":168},"VITE_API_BASE",[70,569,571],{"class":570},"smGrS","=",[70,573,574],{"class":80},"https:\u002F\u002Fapi.example.com\n",[60,576,578],{"className":146,"code":577,"language":148,"meta":65,"style":65},"const base = import.meta.env.VITE_API_BASE;\n",[67,579,580],{"__ignoreMap":65},[70,581,582,586,590,593,596,598,601,603,606,608,610],{"class":72,"line":73},[70,583,585],{"class":584},"sbsja","const",[70,587,589],{"class":588},"s_hVV"," base",[70,591,592],{"class":570}," =",[70,594,595],{"class":161}," import",[70,597,310],{"class":106},[70,599,600],{"class":588},"meta",[70,602,310],{"class":106},[70,604,605],{"class":168},"env",[70,607,310],{"class":106},[70,609,567],{"class":588},[70,611,188],{"class":106},[22,613,614,615,617],{},"勿将密钥写入 ",[67,616,438],{}," 变量（会打进前端包）。",[60,619,621],{"className":62,"code":620,"language":64,"meta":65,"style":65},"# .env.development \u002F .env.production 按环境区分\n# .env.development\nVITE_API_BASE=http:\u002F\u002Flocalhost:3000\n\n# .env.production\nVITE_API_BASE=https:\u002F\u002Fapi.example.com\n",[67,622,623,628,633,642,646,651],{"__ignoreMap":65},[70,624,625],{"class":72,"line":73},[70,626,627],{"class":155},"# .env.development \u002F .env.production 按环境区分\n",[70,629,630],{"class":72,"line":97},[70,631,632],{"class":155},"# .env.development\n",[70,634,635,637,639],{"class":72,"line":191},[70,636,567],{"class":168},[70,638,571],{"class":570},[70,640,641],{"class":80},"http:\u002F\u002Flocalhost:3000\n",[70,643,644],{"class":72,"line":211},[70,645,234],{"emptyLinePlaceholder":233},[70,647,648],{"class":72,"line":230},[70,649,650],{"class":155},"# .env.production\n",[70,652,653,655,657],{"class":72,"line":237},[70,654,567],{"class":168},[70,656,571],{"class":570},[70,658,574],{"class":80},[22,660,661,662,665,666,669,670,672,673,676],{},"Vite 会根据 ",[67,663,664],{},"--mode"," 或 ",[67,667,668],{},"NODE_ENV"," 自动加载对应文件，",[67,671,133],{}," 默认使用 ",[67,674,675],{},"production"," 模式。",[41,678,679],{"id":679},"部署",[60,681,683],{"className":62,"code":682,"language":64,"meta":65,"style":65},"pnpm build          # 生成 dist\u002F\n",[67,684,685],{"__ignoreMap":65},[70,686,687,689,692],{"class":72,"line":73},[70,688,77],{"class":76},[70,690,691],{"class":80}," build",[70,693,694],{"class":155},"          # 生成 dist\u002F\n",[22,696,697],{},"静态产物可部署到 Nginx、Vercel、Netlify、对象存储 + CDN 等：",[60,699,703],{"className":700,"code":701,"language":702,"meta":65,"style":65},"language-nginx shiki shiki-themes material-theme-lighter github-light github-dark","server {\n  listen 80;\n  root \u002Fvar\u002Fwww\u002Fmy-app\u002Fdist;\n  location \u002F {\n    try_files $uri $uri\u002F \u002Findex.html;  # SPA 路由回退\n  }\n}\n","nginx",[67,704,705,710,715,720,725,730,735],{"__ignoreMap":65},[70,706,707],{"class":72,"line":73},[70,708,709],{},"server {\n",[70,711,712],{"class":72,"line":97},[70,713,714],{},"  listen 80;\n",[70,716,717],{"class":72,"line":191},[70,718,719],{},"  root \u002Fvar\u002Fwww\u002Fmy-app\u002Fdist;\n",[70,721,722],{"class":72,"line":211},[70,723,724],{},"  location \u002F {\n",[70,726,727],{"class":72,"line":230},[70,728,729],{},"    try_files $uri $uri\u002F \u002Findex.html;  # SPA 路由回退\n",[70,731,732],{"class":72,"line":237},[70,733,734],{},"  }\n",[70,736,737],{"class":72,"line":255},[70,738,500],{},[22,740,741,742,745,746,39],{},"Next.js 项目部署到 Vercel 最省心（官方产品，天然支持 App Router 特性）；自建服务器部署需要 Node 运行时执行 ",[67,743,744],{},"next start"," 或使用 ",[35,747,750],{"href":748,"rel":749},"https:\u002F\u002Fnextjs.org\u002Fdocs\u002Fpages\u002Fapi-reference\u002Fnext-config-js\u002Foutput",[142],"standalone 输出",[41,752,753],{"id":753},"最佳实践",[122,755,756,759,762,765,782],{},[125,757,758],{},"中后台\u002F工具类纯前端项目默认选 Vite，不要为了「用了 Next.js 显得更高级」而引入不必要的 SSR 复杂度。",[125,760,761],{},"需要 SEO 的营销页\u002F内容站优先选 Next.js（或 Astro 等专门的内容站方案）。",[125,763,764],{},"大项目按 feature 组织目录，而不是按文件类型平铺，便于代码定位与团队协作。",[125,766,767,768,771,772,775,776,771,778,781],{},"环境变量区分 ",[67,769,770],{},".env.development","\u002F",[67,773,774],{},".env.production","，敏感信息始终放后端或 CI Secrets，绝不进入 ",[67,777,438],{},[67,779,780],{},"NEXT_PUBLIC_"," 前缀变量。",[125,783,784],{},"从 CRA 迁移时先建一个并行的 Vite 项目跑通基础路由再逐步搬迁业务代码，避免一次性大改动。",[41,786,787],{"id":787},"常见坑",[789,790,791,807],"table",{},[792,793,794],"thead",{},[795,796,797,801,804],"tr",{},[798,799,800],"th",{},"现象",[798,802,803],{},"常见原因",[798,805,806],{},"处理",[808,809,810,827,845,859,877],"tbody",{},[795,811,812,816,824],{},[813,814,815],"td",{},"生产环境读不到环境变量",[813,817,818,819,771,821,823],{},"变量没有 ",[67,820,438],{},[67,822,780],{}," 前缀",[813,825,826],{},"按框架约定加前缀，或在服务端读取无需暴露的变量",[795,828,829,832,835],{},[813,830,831],{},"部署后刷新页面 404",[813,833,834],{},"静态托管未配置 SPA fallback",[813,836,837,838,841,842],{},"Nginx\u002F托管平台配置 ",[67,839,840],{},"try_files"," 回退到 ",[67,843,844],{},"index.html",[795,846,847,850,856],{},[813,848,849],{},"Next.js 服务端组件报错「不能使用 useState」",[813,851,852,853,855],{},"未加 ",[67,854,514],{}," 指令",[813,857,858],{},"需要交互状态的组件标记为客户端组件",[795,860,861,864,874],{},[813,862,863],{},"CRA 迁移到 Vite 后样式\u002F资源路径失效",[813,865,866,869,870,873],{},[67,867,868],{},"public\u002F"," 引用方式、",[67,871,872],{},"process.env"," 用法不同",[813,875,876],{},"按 Vite 约定调整静态资源与环境变量引用",[795,878,879,882,885],{},[813,880,881],{},"开发期接口跨域报错",[813,883,884],{},"未配置代理，前端直接请求跨域后端",[813,886,887,888,891],{},"用 ",[67,889,890],{},"server.proxy"," 或后端开启 CORS",[41,893,894],{"id":894},"延伸阅读",[122,896,897,902,909],{},[125,898,899,901],{},[35,900,38],{"href":37},"：路由、状态、数据请求库选型",[125,903,904,908],{},[35,905,907],{"href":906},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild","构建与分包","：Vite 生产构建优化",[125,910,911,915],{},[35,912,914],{"href":913},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","：理解 Next.js 服务端组件的渲染模型",[41,917,918],{"id":918},"参考文献",[22,920,921],{},"以下链接在编写时均可正常访问：",[789,923,924,934],{},[792,925,926],{},[795,927,928,931],{},[798,929,930],{},"资料",[798,932,933],{},"说明",[808,935,936,948,960,972,984],{},[795,937,938,945],{},[813,939,940],{},[35,941,944],{"href":942,"rel":943},"https:\u002F\u002Fcn.vite.dev\u002Fguide\u002F",[142],"Vite 指南",[813,946,947],{},"中文",[795,949,950,957],{},[813,951,952],{},[35,953,956],{"href":954,"rel":955},"https:\u002F\u002Fnextjs.org\u002Fdocs",[142],"Next.js 文档",[813,958,959],{},"英文",[795,961,962,969],{},[813,963,964],{},[35,965,968],{"href":966,"rel":967},"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fcreate-react-app#readme",[142],"create-react-app README",[813,970,971],{},"维护说明",[795,973,974,981],{},[813,975,976],{},[35,977,980],{"href":978,"rel":979},"https:\u002F\u002Fnextjs.org\u002Fdocs\u002Fapp",[142],"Next.js：App Router",[813,982,983],{},"服务端组件",[795,985,986,993],{},[813,987,988],{},[35,989,992],{"href":990,"rel":991},"https:\u002F\u002Fcn.vite.dev\u002Fguide\u002Fenv-and-mode",[142],"Vite：环境变量",[813,994,995],{},"配置说明",[997,998,999],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZUrc, html code.shiki .sZUrc{--shiki-light:#E53935;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":65,"searchDepth":97,"depth":97,"links":1001},[1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012],{"id":43,"depth":97,"text":43},{"id":57,"depth":97,"text":58},{"id":408,"depth":97,"text":409},{"id":446,"depth":97,"text":447},{"id":518,"depth":97,"text":519},{"id":546,"depth":97,"text":546},{"id":679,"depth":97,"text":679},{"id":753,"depth":97,"text":753},{"id":787,"depth":97,"text":787},{"id":894,"depth":97,"text":894},{"id":918,"depth":97,"text":918},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fproject","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fproject.md","md",{"items":1018},[1019,1021,1025,1029,1031,1035],{"path":913,"title":914,"description":1020,"category":10,"score":237},"React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。",{"path":1022,"title":1023,"description":1024,"category":10,"score":237},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftypescript","TypeScript","React 官方推荐 TypeScript。类型主要落在 Props、事件、Ref 与 Hooks 返回值上。见 组件。",{"path":1026,"title":1027,"description":1028,"category":10,"score":237},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftest","测试","React 组件测试推荐 React Testing Library（RTL）：从用户可见行为断言，而非实现细节。运行器常用 Vitest 或 Jest。涵盖安装配置、查询优先级、异步测试、Mock 网络请求、最佳实践与常见坑。",{"path":906,"title":907,"description":1030,"category":10,"score":237},"通过代码分割与构建配置减小首屏 JavaScript 体积。涵盖 React.lazy\u002FSuspense、路由级分割、Tree Shaking、Vite 生产构建、体积分析、最佳实践与常见坑。运行时优化见 runtime。",{"path":1032,"title":1033,"description":1034,"category":10,"score":237},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Feffect","Effect 与副作用","useEffect 用于在组件渲染后执行与外部系统同步的逻辑（请求、订阅、手动改 DOM 等）。涵盖基本用法、依赖数组、适用\u002F不适用场景、useLayoutEffect、最佳实践与常见坑。",{"path":1036,"title":1037,"description":1038,"category":10,"score":237},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fstate","状态管理","React 组件内 state 用 Hooks；跨组件共享状态可选 Context、Redux Toolkit、Zustand 等。见 Context Hook。",{"series":1040,"title":12,"items":1041,"index":276,"prev":1089,"next":1090},"booklet\u002Fframe\u002Freact",[1042,1046,1048,1052,1056,1058,1060,1064,1068,1069,1071,1075,1079,1083,1085,1087],{"path":1043,"title":1044,"date":1045,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fbasics","React 基础","2020-02-12",{"path":906,"title":907,"date":1047,"pinned":14},"2025-07-23",{"path":1049,"title":1050,"date":1051,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents","组件与 Props","2020-06-11",{"path":1053,"title":1054,"date":1055,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fcontext","Context","2021-10-16",{"path":37,"title":38,"date":1057,"pinned":14},"2023-02-04",{"path":1032,"title":1033,"date":1059,"pinned":14},"2024-10-11",{"path":1061,"title":1062,"date":1063,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fjsx","JSX 与语法","2020-02-29",{"path":1065,"title":1066,"date":1067,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fmemo","Memo、Callback 与 Ref","2021-02-28",{"path":7,"title":8,"date":13,"pinned":14},{"path":913,"title":914,"date":1070,"pinned":14},"2026-07-10",{"path":1072,"title":1073,"date":1074,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter","路由","2023-09-06",{"path":1076,"title":1077,"date":1078,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","运行时优化","2020-02-05",{"path":1080,"title":1081,"date":1082,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate","State 与 Reducer","2021-02-21",{"path":1036,"title":1037,"date":1084,"pinned":14},"2024-02-07",{"path":1026,"title":1027,"date":1086,"pinned":14},"2025-09-26",{"path":1022,"title":1023,"date":1088,"pinned":14},"2026-03-13",{"path":1065,"title":1066,"date":1067,"pinned":14},{"path":913,"title":914,"date":1070,"pinned":14}]