[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fproject":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fproject":1165,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fproject":1189},{"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":1161,"stem":1162,"id":1163,"extension":1164},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fproject","工程化","新建项目推荐使用 Angular CLI（@angular\u002Fcli），统一生成、构建与测试配置。涵盖创建项目、目录结构、构建部署、环境配置、SSR、与 Vite\u002FReact 项目对照、最佳实践与常见坑。","技能小册",[12],"angular","2021-09-01",false,{"type":16,"value":17,"toc":1147},"minimark",[18,21,41,45,72,75,143,229,233,316,328,331,349,356,405,408,422,491,606,609,624,738,742,754,789,800,804,821,893,896,942,945,1043,1046,1068,1071,1074,1143],[19,20,8],"h1",{"id":8},[22,23,24,25,29,30,34,35,40],"p",{},"新建项目推荐使用 ",[26,27,28],"strong",{},"Angular CLI","（",[31,32,33],"code",{},"@angular\u002Fcli","），统一生成、构建与测试配置。见 ",[36,37,39],"a",{"href":38},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fecology","生态","。",[42,43,44],"h2",{"id":44},"核心概念",[22,46,47,48,51,52,55,56,59,60,63,64,67,68,71],{},"Angular CLI 是官方的「一站式」工程工具：项目生成（",[31,49,50],{},"ng new","）、开发服务器（",[31,53,54],{},"ng serve","）、生产构建（",[31,57,58],{},"ng build","）、单测（",[31,61,62],{},"ng test","）、代码生成（",[31,65,66],{},"ng generate","）都由同一个工具完成，底层构建管道基于 esbuild\u002FVite（现代版本），配置集中在 ",[31,69,70],{},"angular.json","。这与 Vue\u002FReact 生态中「自由选择 Vite + 各种插件」的方式不同，好处是团队间配置高度一致，代价是深度自定义构建流程的自由度较低。",[42,73,74],{"id":74},"创建项目",[76,77,82],"pre",{"className":78,"code":79,"language":80,"meta":81,"style":81},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","npm install -g @angular\u002Fcli\nng new my-app --standalone --routing --style=css\ncd my-app\nng serve\n","bash","",[31,83,84,104,125,135],{"__ignoreMap":81},[85,86,89,93,97,101],"span",{"class":87,"line":88},"line",1,[85,90,92],{"class":91},"sbgvK","npm",[85,94,96],{"class":95},"s_sjI"," install",[85,98,100],{"class":99},"stzsN"," -g",[85,102,103],{"class":95}," @angular\u002Fcli\n",[85,105,107,110,113,116,119,122],{"class":87,"line":106},2,[85,108,109],{"class":91},"ng",[85,111,112],{"class":95}," new",[85,114,115],{"class":95}," my-app",[85,117,118],{"class":99}," --standalone",[85,120,121],{"class":99}," --routing",[85,123,124],{"class":99}," --style=css\n",[85,126,128,132],{"class":87,"line":127},3,[85,129,131],{"class":130},"sptTA","cd",[85,133,134],{"class":95}," my-app\n",[85,136,138,140],{"class":87,"line":137},4,[85,139,109],{"class":91},[85,141,142],{"class":95}," serve\n",[144,145,146,159],"table",{},[147,148,149],"thead",{},[150,151,152,156],"tr",{},[153,154,155],"th",{},"文件 \u002F 目录",[153,157,158],{},"作用",[160,161,162,172,185,195,205,215],"tbody",{},[150,163,164,169],{},[165,166,167],"td",{},[31,168,70],{},[165,170,171],{},"构建、测试、样式全局配置",[150,173,174,179],{},[165,175,176],{},[31,177,178],{},"src\u002Fmain.ts",[165,180,181,184],{},[31,182,183],{},"bootstrapApplication"," 入口",[150,186,187,192],{},[165,188,189],{},[31,190,191],{},"src\u002Fapp\u002Fapp.routes.ts",[165,193,194],{},"路由表",[150,196,197,202],{},[165,198,199],{},[31,200,201],{},"tsconfig.json",[165,203,204],{},"TypeScript 严格选项",[150,206,207,212],{},[165,208,209],{},[31,210,211],{},"src\u002Fapp\u002Fapp.config.ts",[165,213,214],{},"应用级 provider 配置（HttpClient、Router 等）",[150,216,217,226],{},[165,218,219,222,223],{},[31,220,221],{},"public\u002F"," 或 ",[31,224,225],{},"src\u002Fassets\u002F",[165,227,228],{},"静态资源（版本相关目录名）",[42,230,232],{"id":231},"常用-cli-命令","常用 CLI 命令",[76,234,236],{"className":78,"code":235,"language":80,"meta":81,"style":81},"ng generate component features\u002Fuser-list   # 生成组件（可简写 ng g c）\nng generate service core\u002Fauth              # 生成服务\nng generate guard core\u002Fauth                # 生成路由守卫\nng lint                                    # 若已配置 ESLint schematics\nng test                                    # 单元测试\nng e2e                                     # 端到端测试（需先配置）\n",[31,237,238,255,270,284,294,305],{"__ignoreMap":81},[85,239,240,242,245,248,251],{"class":87,"line":88},[85,241,109],{"class":91},[85,243,244],{"class":95}," generate",[85,246,247],{"class":95}," component",[85,249,250],{"class":95}," features\u002Fuser-list",[85,252,254],{"class":253},"sutJx","   # 生成组件（可简写 ng g c）\n",[85,256,257,259,261,264,267],{"class":87,"line":106},[85,258,109],{"class":91},[85,260,244],{"class":95},[85,262,263],{"class":95}," service",[85,265,266],{"class":95}," core\u002Fauth",[85,268,269],{"class":253},"              # 生成服务\n",[85,271,272,274,276,279,281],{"class":87,"line":127},[85,273,109],{"class":91},[85,275,244],{"class":95},[85,277,278],{"class":95}," guard",[85,280,266],{"class":95},[85,282,283],{"class":253},"                # 生成路由守卫\n",[85,285,286,288,291],{"class":87,"line":137},[85,287,109],{"class":91},[85,289,290],{"class":95}," lint",[85,292,293],{"class":253},"                                    # 若已配置 ESLint schematics\n",[85,295,297,299,302],{"class":87,"line":296},5,[85,298,109],{"class":91},[85,300,301],{"class":95}," test",[85,303,304],{"class":253},"                                    # 单元测试\n",[85,306,308,310,313],{"class":87,"line":307},6,[85,309,109],{"class":91},[85,311,312],{"class":95}," e2e",[85,314,315],{"class":253},"                                     # 端到端测试（需先配置）\n",[22,317,318,320,321,323,324,327],{},[31,319,66],{}," 会同时创建组件文件、样式文件、测试文件，并遵循团队在 ",[31,322,70],{}," 中配置的 ",[31,325,326],{},"schematics"," 默认项（如统一使用 Standalone、统一样式后缀）。",[42,329,330],{"id":330},"构建与部署",[76,332,334],{"className":78,"code":333,"language":80,"meta":81,"style":81},"ng build --configuration production\n",[31,335,336],{"__ignoreMap":81},[85,337,338,340,343,346],{"class":87,"line":88},[85,339,109],{"class":91},[85,341,342],{"class":95}," build",[85,344,345],{"class":99}," --configuration",[85,347,348],{"class":95}," production\n",[22,350,351,352,355],{},"默认输出到 ",[31,353,354],{},"dist\u002F","，为静态文件，可部署到 Nginx、CDN、Firebase Hosting 等。",[76,357,361],{"className":358,"code":359,"language":360,"meta":81,"style":81},"language-nginx shiki shiki-themes material-theme-lighter github-light github-dark","# Nginx 部署示例：SPA 路由回退到 index.html\nserver {\n  listen 80;\n  root \u002Fvar\u002Fwww\u002Fmy-app\u002Fbrowser;\n  location \u002F {\n    try_files $uri $uri\u002F \u002Findex.html;\n  }\n}\n","nginx",[31,362,363,368,373,378,383,388,393,399],{"__ignoreMap":81},[85,364,365],{"class":87,"line":88},[85,366,367],{},"# Nginx 部署示例：SPA 路由回退到 index.html\n",[85,369,370],{"class":87,"line":106},[85,371,372],{},"server {\n",[85,374,375],{"class":87,"line":127},[85,376,377],{},"  listen 80;\n",[85,379,380],{"class":87,"line":137},[85,381,382],{},"  root \u002Fvar\u002Fwww\u002Fmy-app\u002Fbrowser;\n",[85,384,385],{"class":87,"line":296},[85,386,387],{},"  location \u002F {\n",[85,389,390],{"class":87,"line":307},[85,391,392],{},"    try_files $uri $uri\u002F \u002Findex.html;\n",[85,394,396],{"class":87,"line":395},7,[85,397,398],{},"  }\n",[85,400,402],{"class":87,"line":401},8,[85,403,404],{},"}\n",[42,406,407],{"id":407},"环境配置",[22,409,410,413,414,417,418,421],{},[31,411,412],{},"src\u002Fenvironments\u002Fenvironment.ts"," 中区分开发与生产 API 地址（CLI 通过 ",[31,415,416],{},"fileReplacements"," 在构建时替换）。",[26,419,420],{},"勿","将密钥写入前端环境文件。",[76,423,427],{"className":424,"code":425,"language":426,"meta":81,"style":81},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","export const environment = {\n  production: false,\n  apiUrl: \"http:\u002F\u002Flocalhost:3000\",\n};\n","ts",[31,428,429,451,467,486],{"__ignoreMap":81},[85,430,431,435,439,443,447],{"class":87,"line":88},[85,432,434],{"class":433},"sVHd0","export",[85,436,438],{"class":437},"sbsja"," const",[85,440,442],{"class":441},"s_hVV"," environment",[85,444,446],{"class":445},"smGrS"," =",[85,448,450],{"class":449},"sP7_E"," {\n",[85,452,453,457,460,464],{"class":87,"line":106},[85,454,456],{"class":455},"skxfh","  production",[85,458,459],{"class":449},":",[85,461,463],{"class":462},"syTEX"," false",[85,465,466],{"class":449},",\n",[85,468,469,472,474,478,481,484],{"class":87,"line":127},[85,470,471],{"class":455},"  apiUrl",[85,473,459],{"class":449},[85,475,477],{"class":476},"sjJ54"," \"",[85,479,480],{"class":95},"http:\u002F\u002Flocalhost:3000",[85,482,483],{"class":476},"\"",[85,485,466],{"class":449},[85,487,488],{"class":87,"line":137},[85,489,490],{"class":449},"};\n",[76,492,496],{"className":493,"code":494,"language":495,"meta":81,"style":81},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F angular.json（摘录）：production 配置下替换文件\n\"configurations\": {\n  \"production\": {\n    \"fileReplacements\": [\n      { \"replace\": \"src\u002Fenvironments\u002Fenvironment.ts\", \"with\": \"src\u002Fenvironments\u002Fenvironment.prod.ts\" }\n    ]\n  }\n}\n","json",[31,497,498,503,519,535,550,593,598,602],{"__ignoreMap":81},[85,499,500],{"class":87,"line":88},[85,501,502],{"class":253},"\u002F\u002F angular.json（摘录）：production 配置下替换文件\n",[85,504,505,507,510,512,516],{"class":87,"line":106},[85,506,483],{"class":476},[85,508,509],{"class":95},"configurations",[85,511,483],{"class":476},[85,513,515],{"class":514},"su5hD",": ",[85,517,518],{"class":449},"{\n",[85,520,521,525,529,531,533],{"class":87,"line":127},[85,522,524],{"class":523},"s39Yj","  \"",[85,526,528],{"class":527},"sseR_","production",[85,530,483],{"class":523},[85,532,459],{"class":449},[85,534,450],{"class":449},[85,536,537,540,543,545,547],{"class":87,"line":137},[85,538,539],{"class":523},"    \"",[85,541,416],{"class":542},"sZMiF",[85,544,483],{"class":523},[85,546,459],{"class":449},[85,548,549],{"class":449}," [\n",[85,551,552,555,557,561,563,565,567,569,571,574,576,579,581,583,585,588,590],{"class":87,"line":296},[85,553,554],{"class":449},"      {",[85,556,477],{"class":523},[85,558,560],{"class":559},"srdBf","replace",[85,562,483],{"class":523},[85,564,459],{"class":449},[85,566,477],{"class":476},[85,568,412],{"class":95},[85,570,483],{"class":476},[85,572,573],{"class":449},",",[85,575,477],{"class":523},[85,577,578],{"class":559},"with",[85,580,483],{"class":523},[85,582,459],{"class":449},[85,584,477],{"class":476},[85,586,587],{"class":95},"src\u002Fenvironments\u002Fenvironment.prod.ts",[85,589,483],{"class":476},[85,591,592],{"class":449}," }\n",[85,594,595],{"class":87,"line":307},[85,596,597],{"class":449},"    ]\n",[85,599,600],{"class":87,"line":395},[85,601,398],{"class":449},[85,603,604],{"class":87,"line":401},[85,605,404],{"class":449},[42,607,608],{"id":608},"依赖注入与应用配置",[22,610,611,612,615,616,619,620,623],{},"Standalone 架构下，应用级配置集中在 ",[31,613,614],{},"app.config.ts","，替代旧版 ",[31,617,618],{},"AppModule"," 的 ",[31,621,622],{},"providers","：",[76,625,627],{"className":424,"code":626,"language":426,"meta":81,"style":81},"\u002F\u002F app.config.ts\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideRouter(routes),\n    provideHttpClient(withInterceptors([authInterceptor])),\n    provideAnimations(),\n  ],\n};\n\n\u002F\u002F main.ts\nbootstrapApplication(AppComponent, appConfig);\n",[31,628,629,634,652,661,672,688,698,705,709,716,722],{"__ignoreMap":81},[85,630,631],{"class":87,"line":88},[85,632,633],{"class":253},"\u002F\u002F app.config.ts\n",[85,635,636,638,640,643,645,648,650],{"class":87,"line":106},[85,637,434],{"class":433},[85,639,438],{"class":437},[85,641,642],{"class":441}," appConfig",[85,644,459],{"class":445},[85,646,647],{"class":91}," ApplicationConfig",[85,649,446],{"class":445},[85,651,450],{"class":449},[85,653,654,657,659],{"class":87,"line":127},[85,655,656],{"class":455},"  providers",[85,658,459],{"class":449},[85,660,549],{"class":514},[85,662,663,667,670],{"class":87,"line":137},[85,664,666],{"class":665},"sGLFI","    provideRouter",[85,668,669],{"class":514},"(routes)",[85,671,466],{"class":449},[85,673,674,677,680,683,686],{"class":87,"line":296},[85,675,676],{"class":665},"    provideHttpClient",[85,678,679],{"class":514},"(",[85,681,682],{"class":665},"withInterceptors",[85,684,685],{"class":514},"([authInterceptor]))",[85,687,466],{"class":449},[85,689,690,693,696],{"class":87,"line":307},[85,691,692],{"class":665},"    provideAnimations",[85,694,695],{"class":514},"()",[85,697,466],{"class":449},[85,699,700,703],{"class":87,"line":395},[85,701,702],{"class":514},"  ]",[85,704,466],{"class":449},[85,706,707],{"class":87,"line":401},[85,708,490],{"class":449},[85,710,712],{"class":87,"line":711},9,[85,713,715],{"emptyLinePlaceholder":714},true,"\n",[85,717,719],{"class":87,"line":718},10,[85,720,721],{"class":253},"\u002F\u002F main.ts\n",[85,723,725,727,730,732,735],{"class":87,"line":724},11,[85,726,183],{"class":665},[85,728,729],{"class":514},"(AppComponent",[85,731,573],{"class":449},[85,733,734],{"class":514}," appConfig)",[85,736,737],{"class":449},";\n",[42,739,741],{"id":740},"ssr-与全栈","SSR 与全栈",[22,743,744,29,750,753],{},[36,745,749],{"href":746,"rel":747},"https:\u002F\u002Fangular.dev\u002Fguide\u002Fssr",[748],"nofollow","Angular SSR",[31,751,752],{},"ng add @angular\u002Fssr","）支持服务端渲染与混合渲染，适合 SEO 与首屏性能。与纯 SPA 相比部署更复杂（需 Node 或适配的托管）。",[76,755,757],{"className":78,"code":756,"language":80,"meta":81,"style":81},"ng add @angular\u002Fssr\nnpm run build          # 生成浏览器与 server 两套产物\nnode dist\u002Fmy-app\u002Fserver\u002Fserver.mjs\n",[31,758,759,769,781],{"__ignoreMap":81},[85,760,761,763,766],{"class":87,"line":88},[85,762,109],{"class":91},[85,764,765],{"class":95}," add",[85,767,768],{"class":95}," @angular\u002Fssr\n",[85,770,771,773,776,778],{"class":87,"line":106},[85,772,92],{"class":91},[85,774,775],{"class":95}," run",[85,777,342],{"class":95},[85,779,780],{"class":253},"          # 生成浏览器与 server 两套产物\n",[85,782,783,786],{"class":87,"line":127},[85,784,785],{"class":91},"node",[85,787,788],{"class":95}," dist\u002Fmy-app\u002Fserver\u002Fserver.mjs\n",[22,790,791,792,795,796,799],{},"混合渲染（Angular 17+ 的 ",[31,793,794],{},"ServerRoute"," 配置）允许",[26,797,798],{},"按路由","选择 SSR、预渲染（SSG）或纯客户端渲染，兼顾 SEO 页面与高交互后台页面的不同需求。",[42,801,803],{"id":802},"与-vite-react-项目对照","与 Vite \u002F React 项目对照",[22,805,806,807,810,811,619,813,816,817,820],{},"Angular 使用 ",[26,808,809],{},"esbuild\u002FVite 化构建管道","（现代 CLI 版本，以 ",[31,812,70],{},[31,814,815],{},"builder"," 为准），并非默认 Vite 配置文件；",[26,818,819],{},"React + Vite"," 脚手架更轻。选型按团队规范与是否需要「全家桶」框架决定。",[144,822,823,835],{},[147,824,825],{},[150,826,827,830,832],{},[153,828,829],{},"维度",[153,831,28],{},[153,833,834],{},"Vite（Vue\u002FReact）",[160,836,837,853,866,880],{},[150,838,839,842,847],{},[165,840,841],{},"配置文件",[165,843,844,846],{},[31,845,70],{},"（集中式）",[165,848,849,852],{},[31,850,851],{},"vite.config.ts","（自由插件组合）",[150,854,855,858,863],{},[165,856,857],{},"生成器",[165,859,860,862],{},[31,861,66],{}," 内置多种 schematics",[165,864,865],{},"一般手写或用社区模板",[150,867,868,871,877],{},[165,869,870],{},"升级路径",[165,872,873,876],{},[31,874,875],{},"ng update"," 自动迁移代码",[165,878,879],{},"手动查 changelog 升级依赖",[150,881,882,885,890],{},[165,883,884],{},"SSR",[165,886,887,889],{},[31,888,752],{}," 官方支持",[165,891,892],{},"Next.js \u002F Nuxt 承担",[42,894,895],{"id":895},"最佳实践",[897,898,899,906,917,924,931],"ul",{},[900,901,902,903,905],"li",{},"统一用 ",[31,904,66],{}," 生成文件，保证团队目录结构、命名风格一致。",[900,907,908,909,912,913,916],{},"生产构建务必带 ",[31,910,911],{},"--configuration production","，开启压缩、tree-shaking 与预算检查（",[31,914,915],{},"budgets"," 配置超限会构建失败，及时排查体积增长）。",[900,918,919,920,923],{},"环境变量只放",[26,921,922],{},"非敏感","配置（API 基地址等），密钥类信息放后端或构建时注入的 CI 变量，不写入仓库。",[900,925,926,927,930],{},"升级 Angular 版本用 ",[31,928,929],{},"ng update @angular\u002Fcore @angular\u002Fcli","，让官方迁移脚本自动处理破坏性变更。",[900,932,933,934,937,938,941],{},"中大型项目考虑启用 ESLint（",[31,935,936],{},"ng add @angular-eslint\u002Fschematics","）与统一的 ",[31,939,940],{},"tsconfig"," 严格模式。",[42,943,944],{"id":944},"常见坑",[144,946,947,960],{},[147,948,949],{},[150,950,951,954,957],{},[153,952,953],{},"现象",[153,955,956],{},"常见原因",[153,958,959],{},"处理",[160,961,962,978,992,1011,1024],{},[150,963,964,967,975],{},[165,965,966],{},"生产构建报「超出体积预算」",[165,968,969,971,972,974],{},[31,970,70],{}," 中 ",[31,973,915],{}," 阈值被触发",[165,976,977],{},"分析 bundle，按需拆分或调整合理预算而非直接放宽",[150,979,980,983,989],{},[165,981,982],{},"部署后刷新子路由 404",[165,984,985,986],{},"服务器未把未知路径回退到 ",[31,987,988],{},"index.html",[165,990,991],{},"Nginx\u002F静态托管配置 SPA fallback",[150,993,994,997,1005],{},[165,995,996],{},"环境变量在生产环境未生效",[165,998,999,1001,1002],{},[31,1000,416],{}," 配置错误或忘记指定 ",[31,1003,1004],{},"--configuration",[165,1006,1007,1008,1010],{},"检查 ",[31,1009,70],{}," 对应 configuration 的 fileReplacements",[150,1012,1013,1018,1021],{},[165,1014,1015,1017],{},[31,1016,875],{}," 后编译报错",[165,1019,1020],{},"迁移 schematics 未覆盖全部自定义代码路径",[165,1022,1023],{},"按官方 changelog 手动修正剩余部分",[150,1025,1026,1033,1036],{},[165,1027,1028,1029,1032],{},"SSR 构建后接口请求报错（无 ",[31,1030,1031],{},"window","）",[165,1034,1035],{},"服务端渲染阶段直接使用了浏览器 API",[165,1037,1038,1039,1042],{},"用 ",[31,1040,1041],{},"isPlatformBrowser"," 判断或延迟到客户端执行",[42,1044,1045],{"id":1045},"延伸阅读",[897,1047,1048,1054,1061],{},[900,1049,1050,1053],{},[36,1051,1052],{"href":38},"生态集成","：CLI 与官方全家桶",[900,1055,1056,1060],{},[36,1057,1059],{"href":1058},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Ftest","测试","：CLI 内置测试工具链",[900,1062,1063,1067],{},[36,1064,1066],{"href":1065},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fperformance\u002Fruntime","运行时优化","：构建体积分析",[42,1069,1070],{"id":1070},"参考文献",[22,1072,1073],{},"以下链接在编写时均可正常访问：",[144,1075,1076,1086],{},[147,1077,1078],{},[150,1079,1080,1083],{},[153,1081,1082],{},"资料",[153,1084,1085],{},"说明",[160,1087,1088,1099,1110,1122,1132],{},[150,1089,1090,1096],{},[165,1091,1092],{},[36,1093,28],{"href":1094,"rel":1095},"https:\u002F\u002Fangular.dev\u002Ftools\u002Fcli",[748],[165,1097,1098],{},"官方",[150,1100,1101,1108],{},[165,1102,1103],{},[36,1104,1107],{"href":1105,"rel":1106},"https:\u002F\u002Fangular.dev\u002Freference\u002Fconfigs\u002Fworkspace-config",[748],"工作区配置",[165,1109,70],{},[150,1111,1112,1119],{},[165,1113,1114],{},[36,1115,1118],{"href":1116,"rel":1117},"https:\u002F\u002Fangular.dev\u002Ftools\u002Fcli\u002Fdeployment",[748],"部署",[165,1120,1121],{},"生产部署",[150,1123,1124,1129],{},[165,1125,1126],{},[36,1127,749],{"href":746,"rel":1128},[748],[165,1130,1131],{},"服务端渲染",[150,1133,1134,1140],{},[165,1135,1136],{},[36,1137,875],{"href":1138,"rel":1139},"https:\u002F\u002Fangular.dev\u002Fcli\u002Fupdate",[748],[165,1141,1142],{},"版本迁移",[1144,1145,1146],"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 .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 .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}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":81,"searchDepth":106,"depth":106,"links":1148},[1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160],{"id":44,"depth":106,"text":44},{"id":74,"depth":106,"text":74},{"id":231,"depth":106,"text":232},{"id":330,"depth":106,"text":330},{"id":407,"depth":106,"text":407},{"id":608,"depth":106,"text":608},{"id":740,"depth":106,"text":741},{"id":802,"depth":106,"text":803},{"id":895,"depth":106,"text":895},{"id":944,"depth":106,"text":944},{"id":1045,"depth":106,"text":1045},{"id":1070,"depth":106,"text":1070},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FAngular\u002Fproject","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FAngular\u002Fproject.md","md",{"items":1166},[1167,1169,1173,1177,1181,1185],{"path":38,"title":1052,"description":1168,"category":10,"score":307},"Angular 官方维护路由、HTTP、表单、动画等；UI 与状态扩展由社区与官方 Material 团队提供。涵盖官方\u002F半官方库、常用第三方库、与 Vue\u002FReact 生态对照、选型建议与常见坑。",{"path":1170,"title":1171,"description":1172,"category":10,"score":307},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fsignals\u002Fbasics","Signal 基础","Signals 是 Angular 16 引入的响应式原语：可读写的 signal、派生的 computed、副作用 effect。用于组件内状态与跨组件细粒度更新，涵盖 API、input signal、与变更检测的关系、最佳实践与常见坑。",{"path":1174,"title":1175,"description":1176,"category":10,"score":307},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fsignals\u002Frxjs","RxJS 互操作","Angular 内置 RxJS 处理异步流（HTTP、HttpClient、路由事件、FormControl 的 valueChanges 等）。Signal 适合同步\u002F短链路状态；长生命周期流仍常用 Observable。涵盖 async 管道、toSignal\u002FtoObservable、常用操作符、最佳实践与常见坑。",{"path":1178,"title":1179,"description":1180,"category":10,"score":307},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fforms","表单","Angular 提供 响应式表单（Reactive Forms） 与 模板驱动表单（Template-driven）。复杂表单推荐 Reactive Forms（模型在 TypeScript 中，易测试）。涵盖 FormGroup\u002FFormArray、Typed Forms、自定义校验器、最佳实践与常见坑。",{"path":1182,"title":1183,"description":1184,"category":10,"score":307},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fstate","状态管理","Angular 没有强制单一状态库：服务 + DI、Signal、RxJS 即可覆盖多数场景；大型应用可选用 NgRx。涵盖选型、服务作为状态容器、跨组件共享、NgRx 与 SignalStore、常见坑。",{"path":1186,"title":1187,"description":1188,"category":10,"score":307},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fhttp","HTTP 与数据","Angular 通过 HttpClient（@angular\u002Fcommon\u002Fhttp）发起 HTTP 请求，返回 RxJS Observable。见 RxJS 互操作、依赖注入。",{"series":1190,"title":12,"items":1191,"index":401,"prev":1229,"next":1230},"booklet\u002Fframe\u002Fangular",[1192,1196,1198,1202,1206,1210,1212,1214,1216,1217,1221,1223,1225,1227],{"path":1193,"title":1194,"date":1195,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fbasics","Angular 基础","2020-03-07",{"path":1170,"title":1171,"date":1197,"pinned":14},"2026-06-12",{"path":1199,"title":1200,"date":1201,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fperformance\u002Fchange-detection","变更检测","2023-10-12",{"path":1203,"title":1204,"date":1205,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fcomponents","组件与模板","2021-07-11",{"path":1207,"title":1208,"date":1209,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fdi","依赖注入","2021-01-24",{"path":38,"title":1052,"date":1211,"pinned":14},"2026-07-12",{"path":1178,"title":1179,"date":1213,"pinned":14},"2025-05-26",{"path":1186,"title":1187,"date":1215,"pinned":14},"2024-12-27",{"path":7,"title":8,"date":13,"pinned":14},{"path":1218,"title":1219,"date":1220,"pinned":14},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Frouter","路由","2023-02-04",{"path":1065,"title":1066,"date":1222,"pinned":14},"2022-11-01",{"path":1174,"title":1175,"date":1224,"pinned":14},"2026-04-06",{"path":1182,"title":1183,"date":1226,"pinned":14},"2025-04-29",{"path":1058,"title":1059,"date":1228,"pinned":14},"2021-08-25",{"path":1186,"title":1187,"date":1215,"pinned":14},{"path":1218,"title":1219,"date":1220,"pinned":14}]