[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fdi":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fdi":721,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fdi":747},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":717,"stem":718,"id":719,"extension":720},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fdi","依赖注入","依赖注入（DI） 是 Angular 的核心：由框架创建并注入服务（Service）实例，便于测试与解耦。见 Angular 基础。","技能小册",[12,13],"angular","di","2021-01-24",false,{"type":17,"value":18,"toc":710},"minimark",[19,22,36,40,223,392,398,401,455,462,465,569,575,578,585,645,651,654,657,706],[20,21,8],"h1",{"id":8},[23,24,25,29,30,35],"p",{},[26,27,28],"strong",{},"依赖注入（DI）"," 是 Angular 的核心：由框架创建并注入服务（Service）实例，便于测试与解耦。见 ",[31,32,34],"a",{"href":33},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fbasics","Angular 基础","。",[37,38,39],"h2",{"id":39},"定义与注入服务",[41,42,47],"pre",{"className":43,"code":44,"language":45,"meta":46,"style":46},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { Injectable } from \"@angular\u002Fcore\";\n\n@Injectable({ providedIn: \"root\" })\nexport class UserService {\n  getUsers() {\n    return fetch(\"\u002Fapi\u002Fusers\").then((r) => r.json());\n  }\n}\n","ts","",[48,49,50,87,94,129,146,158,211,217],"code",{"__ignoreMap":46},[51,52,55,59,63,67,70,73,77,81,84],"span",{"class":53,"line":54},"line",1,[51,56,58],{"class":57},"sVHd0","import",[51,60,62],{"class":61},"sP7_E"," {",[51,64,66],{"class":65},"su5hD"," Injectable",[51,68,69],{"class":61}," }",[51,71,72],{"class":57}," from",[51,74,76],{"class":75},"sjJ54"," \"",[51,78,80],{"class":79},"s_sjI","@angular\u002Fcore",[51,82,83],{"class":75},"\"",[51,85,86],{"class":61},";\n",[51,88,90],{"class":53,"line":89},2,[51,91,93],{"emptyLinePlaceholder":92},true,"\n",[51,95,97,100,104,107,110,114,117,119,122,124,126],{"class":53,"line":96},3,[51,98,99],{"class":61},"@",[51,101,103],{"class":102},"sGLFI","Injectable",[51,105,106],{"class":65},"(",[51,108,109],{"class":61},"{",[51,111,113],{"class":112},"skxfh"," providedIn",[51,115,116],{"class":61},":",[51,118,76],{"class":75},[51,120,121],{"class":79},"root",[51,123,83],{"class":75},[51,125,69],{"class":61},[51,127,128],{"class":65},")\n",[51,130,132,135,139,143],{"class":53,"line":131},4,[51,133,134],{"class":57},"export",[51,136,138],{"class":137},"sbsja"," class",[51,140,142],{"class":141},"sbgvK"," UserService",[51,144,145],{"class":61}," {\n",[51,147,149,153,156],{"class":53,"line":148},5,[51,150,152],{"class":151},"sVXei","  getUsers",[51,154,155],{"class":61},"()",[51,157,145],{"class":61},[51,159,161,164,167,169,171,174,176,179,182,185,187,189,193,195,198,201,203,206,209],{"class":53,"line":160},6,[51,162,163],{"class":57},"    return",[51,165,166],{"class":102}," fetch",[51,168,106],{"class":112},[51,170,83],{"class":75},[51,172,173],{"class":79},"\u002Fapi\u002Fusers",[51,175,83],{"class":75},[51,177,178],{"class":112},")",[51,180,181],{"class":61},".",[51,183,184],{"class":102},"then",[51,186,106],{"class":112},[51,188,106],{"class":61},[51,190,192],{"class":191},"s99_P","r",[51,194,178],{"class":61},[51,196,197],{"class":137}," =>",[51,199,200],{"class":65}," r",[51,202,181],{"class":61},[51,204,205],{"class":102},"json",[51,207,208],{"class":112},"())",[51,210,86],{"class":61},[51,212,214],{"class":53,"line":213},7,[51,215,216],{"class":61},"  }\n",[51,218,220],{"class":53,"line":219},8,[51,221,222],{"class":61},"}\n",[41,224,226],{"className":43,"code":225,"language":45,"meta":46,"style":46},"import { Component, inject } from \"@angular\u002Fcore\";\nimport { UserService } from \".\u002Fuser.service\";\n\n@Component({\n  selector: \"app-user-list\",\n  standalone: true,\n  template: `...`,\n})\nexport class UserListComponent {\n  private userService = inject(UserService);\n  \u002F\u002F 或构造函数：constructor(private userService: UserService) {}\n}\n",[48,227,228,255,276,280,292,309,322,340,347,359,380,387],{"__ignoreMap":46},[51,229,230,232,234,237,240,243,245,247,249,251,253],{"class":53,"line":54},[51,231,58],{"class":57},[51,233,62],{"class":61},[51,235,236],{"class":65}," Component",[51,238,239],{"class":61},",",[51,241,242],{"class":65}," inject",[51,244,69],{"class":61},[51,246,72],{"class":57},[51,248,76],{"class":75},[51,250,80],{"class":79},[51,252,83],{"class":75},[51,254,86],{"class":61},[51,256,257,259,261,263,265,267,269,272,274],{"class":53,"line":89},[51,258,58],{"class":57},[51,260,62],{"class":61},[51,262,142],{"class":65},[51,264,69],{"class":61},[51,266,72],{"class":57},[51,268,76],{"class":75},[51,270,271],{"class":79},".\u002Fuser.service",[51,273,83],{"class":75},[51,275,86],{"class":61},[51,277,278],{"class":53,"line":96},[51,279,93],{"emptyLinePlaceholder":92},[51,281,282,284,287,289],{"class":53,"line":131},[51,283,99],{"class":61},[51,285,286],{"class":102},"Component",[51,288,106],{"class":65},[51,290,291],{"class":61},"{\n",[51,293,294,297,299,301,304,306],{"class":53,"line":148},[51,295,296],{"class":112},"  selector",[51,298,116],{"class":61},[51,300,76],{"class":75},[51,302,303],{"class":79},"app-user-list",[51,305,83],{"class":75},[51,307,308],{"class":61},",\n",[51,310,311,314,316,320],{"class":53,"line":160},[51,312,313],{"class":112},"  standalone",[51,315,116],{"class":61},[51,317,319],{"class":318},"syTEX"," true",[51,321,308],{"class":61},[51,323,324,327,329,332,335,338],{"class":53,"line":213},[51,325,326],{"class":112},"  template",[51,328,116],{"class":61},[51,330,331],{"class":75}," `",[51,333,334],{"class":79},"...",[51,336,337],{"class":75},"`",[51,339,308],{"class":61},[51,341,342,345],{"class":53,"line":219},[51,343,344],{"class":61},"}",[51,346,128],{"class":65},[51,348,350,352,354,357],{"class":53,"line":349},9,[51,351,134],{"class":57},[51,353,138],{"class":137},[51,355,356],{"class":141}," UserListComponent",[51,358,145],{"class":61},[51,360,362,365,369,373,375,378],{"class":53,"line":361},10,[51,363,364],{"class":137},"  private",[51,366,368],{"class":367},"sucvu"," userService",[51,370,372],{"class":371},"smGrS"," =",[51,374,242],{"class":151},[51,376,377],{"class":65},"(UserService)",[51,379,86],{"class":61},[51,381,383],{"class":53,"line":382},11,[51,384,386],{"class":385},"sutJx","  \u002F\u002F 或构造函数：constructor(private userService: UserService) {}\n",[51,388,390],{"class":53,"line":389},12,[51,391,222],{"class":61},[23,393,394,397],{},[48,395,396],{},"providedIn: 'root'"," 表示应用级单例（树摇友好，未使用可不打包）。",[37,399,400],{"id":400},"提供者作用域",[402,403,404,417],"table",{},[405,406,407],"thead",{},[408,409,410,414],"tr",{},[411,412,413],"th",{},"配置",[411,415,416],{},"作用",[418,419,420,430,441],"tbody",{},[408,421,422,427],{},[423,424,425],"td",{},[48,426,396],{},[423,428,429],{},"全应用单例",[408,431,432,438],{},[423,433,434,435],{},"组件 ",[48,436,437],{},"providers: [MyService]",[423,439,440],{},"每个组件实例一份",[408,442,443,452],{},[423,444,445,448,449],{},[48,446,447],{},"bootstrapApplication"," \u002F 路由的 ",[48,450,451],{},"providers",[423,453,454],{},"模块级或路由级",[23,456,457,458,461],{},"子组件注入时，",[26,459,460],{},"最近","的 provider 优先。",[37,463,464],{"id":464},"函数式提供者",[41,466,468],{"className":43,"code":467,"language":45,"meta":46,"style":46},"bootstrapApplication(AppComponent, {\n  providers: [\n    { provide: API_URL, useValue: \"https:\u002F\u002Fapi.example.com\" },\n    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },\n  ],\n});\n",[48,469,470,481,491,522,554,561],{"__ignoreMap":46},[51,471,472,474,477,479],{"class":53,"line":54},[51,473,447],{"class":102},[51,475,476],{"class":65},"(AppComponent",[51,478,239],{"class":61},[51,480,145],{"class":61},[51,482,483,486,488],{"class":53,"line":89},[51,484,485],{"class":112},"  providers",[51,487,116],{"class":61},[51,489,490],{"class":65}," [\n",[51,492,493,496,499,501,505,507,510,512,514,517,519],{"class":53,"line":96},[51,494,495],{"class":61},"    {",[51,497,498],{"class":112}," provide",[51,500,116],{"class":61},[51,502,504],{"class":503},"s_hVV"," API_URL",[51,506,239],{"class":61},[51,508,509],{"class":112}," useValue",[51,511,116],{"class":61},[51,513,76],{"class":75},[51,515,516],{"class":79},"https:\u002F\u002Fapi.example.com",[51,518,83],{"class":75},[51,520,521],{"class":61}," },\n",[51,523,524,526,528,530,533,535,538,540,543,545,548,550,552],{"class":53,"line":131},[51,525,495],{"class":61},[51,527,498],{"class":112},[51,529,116],{"class":61},[51,531,532],{"class":503}," HTTP_INTERCEPTORS",[51,534,239],{"class":61},[51,536,537],{"class":112}," useClass",[51,539,116],{"class":61},[51,541,542],{"class":65}," AuthInterceptor",[51,544,239],{"class":61},[51,546,547],{"class":112}," multi",[51,549,116],{"class":61},[51,551,319],{"class":318},[51,553,521],{"class":61},[51,555,556,559],{"class":53,"line":148},[51,557,558],{"class":65},"  ]",[51,560,308],{"class":61},[51,562,563,565,567],{"class":53,"line":160},[51,564,344],{"class":61},[51,566,178],{"class":65},[51,568,86],{"class":61},[23,570,571,574],{},[48,572,573],{},"InjectionToken"," 用于注入非类类型（字符串、配置对象）。",[37,576,577],{"id":577},"测试",[23,579,580,581,584],{},"在单元测试中可用 ",[48,582,583],{},"TestBed.configureTestingModule"," 覆盖 provider：",[41,586,588],{"className":43,"code":587,"language":45,"meta":46,"style":46},"TestBed.configureTestingModule({\n  providers: [{ provide: UserService, useValue: mockUserService }],\n});\n",[48,589,590,604,637],{"__ignoreMap":46},[51,591,592,595,597,600,602],{"class":53,"line":54},[51,593,594],{"class":65},"TestBed",[51,596,181],{"class":61},[51,598,599],{"class":102},"configureTestingModule",[51,601,106],{"class":65},[51,603,291],{"class":61},[51,605,606,608,610,613,615,617,619,621,623,625,627,630,632,635],{"class":53,"line":89},[51,607,485],{"class":112},[51,609,116],{"class":61},[51,611,612],{"class":65}," [",[51,614,109],{"class":61},[51,616,498],{"class":112},[51,618,116],{"class":61},[51,620,142],{"class":65},[51,622,239],{"class":61},[51,624,509],{"class":112},[51,626,116],{"class":61},[51,628,629],{"class":65}," mockUserService ",[51,631,344],{"class":61},[51,633,634],{"class":65},"]",[51,636,308],{"class":61},[51,638,639,641,643],{"class":53,"line":96},[51,640,344],{"class":61},[51,642,178],{"class":65},[51,644,86],{"class":61},[23,646,647,648,35],{},"见 ",[31,649,577],{"href":650},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Ftest",[37,652,653],{"id":653},"参考文献",[23,655,656],{},"以下链接在编写时均可正常访问：",[402,658,659,669],{},[405,660,661],{},[408,662,663,666],{},[411,664,665],{},"资料",[411,667,668],{},"说明",[418,670,671,683,695],{},[408,672,673,680],{},[423,674,675],{},[31,676,8],{"href":677,"rel":678},"https:\u002F\u002Fangular.dev\u002Fguide\u002Fdi",[679],"nofollow",[423,681,682],{},"官方指南",[408,684,685,692],{},[423,686,687],{},[31,688,691],{"href":689,"rel":690},"https:\u002F\u002Fangular.dev\u002Fapi\u002Fcore\u002Finject",[679],"inject()",[423,693,694],{},"函数式注入",[408,696,697,703],{},[423,698,699],{},[31,700,573],{"href":701,"rel":702},"https:\u002F\u002Fangular.dev\u002Fapi\u002Fcore\u002FInjectionToken",[679],[423,704,705],{},"令牌",[707,708,709],"style",{},"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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}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 .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}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 .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":46,"searchDepth":89,"depth":89,"links":711},[712,713,714,715,716],{"id":39,"depth":89,"text":39},{"id":400,"depth":89,"text":400},{"id":464,"depth":89,"text":464},{"id":577,"depth":89,"text":577},{"id":653,"depth":89,"text":653},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FAngular\u002Fdi","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FAngular\u002Fdi.md","md",{"items":722},[723,727,731,735,739,743],{"path":724,"title":725,"description":726,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fecology","生态集成","Angular 官方维护路由、HTTP、表单、动画等；UI 与状态扩展由社区与官方 Material 团队提供。涵盖官方\u002F半官方库、常用第三方库、与 Vue\u002FReact 生态对照、选型建议与常见坑。",{"path":728,"title":729,"description":730,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fsignals\u002Fbasics","Signal 基础","Signals 是 Angular 16 引入的响应式原语：可读写的 signal、派生的 computed、副作用 effect。用于组件内状态与跨组件细粒度更新，涵盖 API、input signal、与变更检测的关系、最佳实践与常见坑。",{"path":732,"title":733,"description":734,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fsignals\u002Frxjs","RxJS 互操作","Angular 内置 RxJS 处理异步流（HTTP、HttpClient、路由事件、FormControl 的 valueChanges 等）。Signal 适合同步\u002F短链路状态；长生命周期流仍常用 Observable。涵盖 async 管道、toSignal\u002FtoObservable、常用操作符、最佳实践与常见坑。",{"path":736,"title":737,"description":738,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fforms","表单","Angular 提供 响应式表单（Reactive Forms） 与 模板驱动表单（Template-driven）。复杂表单推荐 Reactive Forms（模型在 TypeScript 中，易测试）。涵盖 FormGroup\u002FFormArray、Typed Forms、自定义校验器、最佳实践与常见坑。",{"path":740,"title":741,"description":742,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fstate","状态管理","Angular 没有强制单一状态库：服务 + DI、Signal、RxJS 即可覆盖多数场景；大型应用可选用 NgRx。涵盖选型、服务作为状态容器、跨组件共享、NgRx 与 SignalStore、常见坑。",{"path":744,"title":745,"description":746,"category":10,"score":160},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fhttp","HTTP 与数据","Angular 通过 HttpClient（@angular\u002Fcommon\u002Fhttp）发起 HTTP 请求，返回 RxJS Observable。见 RxJS 互操作、依赖注入。",{"series":748,"title":12,"items":749,"index":131,"prev":787,"next":788},"booklet\u002Fframe\u002Fangular",[750,752,754,758,762,763,765,767,769,773,777,781,783,785],{"path":33,"title":34,"date":751,"pinned":15},"2020-03-07",{"path":728,"title":729,"date":753,"pinned":15},"2026-06-12",{"path":755,"title":756,"date":757,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fperformance\u002Fchange-detection","变更检测","2023-10-12",{"path":759,"title":760,"date":761,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fcomponents","组件与模板","2021-07-11",{"path":7,"title":8,"date":14,"pinned":15},{"path":724,"title":725,"date":764,"pinned":15},"2026-07-12",{"path":736,"title":737,"date":766,"pinned":15},"2025-05-26",{"path":744,"title":745,"date":768,"pinned":15},"2024-12-27",{"path":770,"title":771,"date":772,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fproject","工程化","2021-09-01",{"path":774,"title":775,"date":776,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Frouter","路由","2023-02-04",{"path":778,"title":779,"date":780,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fangular\u002Fperformance\u002Fruntime","运行时优化","2022-11-01",{"path":732,"title":733,"date":782,"pinned":15},"2026-04-06",{"path":740,"title":741,"date":784,"pinned":15},"2025-04-29",{"path":650,"title":577,"date":786,"pinned":15},"2021-08-25",{"path":759,"title":760,"date":761,"pinned":15},{"path":724,"title":725,"date":764,"pinned":15}]