RouterModule.forRoot (ROUTES) เทียบกับ RouterModule.forChild (ROUTES)


112

อะไรคือความแตกต่างระหว่างสองสิ่งนี้และอะไรคือความแตกต่างของแต่ละกรณี?

เอกสารจะไม่เป็นประโยชน์ว่า:

forRoot สร้างโมดูลที่มีคำสั่งทั้งหมดเส้นทางที่กำหนดและบริการเราเตอร์เอง

forChild สร้างโมดูลที่มีคำสั่งทั้งหมดและเส้นทางที่กำหนด แต่ไม่รวมบริการเราเตอร์

การเดาที่คลุมเครือของฉันคือโมดูลหนึ่งสำหรับโมดูล 'หลัก' และอีกโมดูลหนึ่งสำหรับโมดูลที่นำเข้า (เนื่องจากพวกเขาจะมีบริการจากโมดูลหลักอยู่แล้ว) แต่ฉันไม่สามารถนึกถึงกรณีการใช้งานได้จริงๆ


1
คุณสามารถเจาะจงมากขึ้นเกี่ยวกับสิ่งที่คุณไม่เข้าใจได้ไหม คำพูดที่คุณใส่ไว้จะบอกคุณอย่างแท้จริงว่าความแตกต่างคืออะไร
jonrsharpe

2
ฉันไม่เข้าใจว่าจุดที่ใช้. forChild () คืออะไร ฉันต้องการคำสั่งและเส้นทางที่ไม่มีบริการเมื่อใด ระหว่างนี้โปรดตอบคำถามที่คุณลบออกจากโพสต์ ...
VSO

18
ควรมีเพียงหนึ่งเดียวRouterServiceสำหรับแอปพลิเคชัน Angular2 เดียว forRootจะเริ่มต้นบริการนั้นและลงทะเบียนกับ DI พร้อมกับการกำหนดค่าเส้นทางบางอย่างในขณะที่forChildจะลงทะเบียนเฉพาะการกำหนดเส้นทางเพิ่มเติมและบอกให้ Angular2 นำสิ่งRouterServiceที่forRootสร้างขึ้นมาใช้ซ้ำ
Harry Ninh

@HarryNinh: ขอบคุณ - นั่นคือสิ่งที่ฉันกำลังมองหา คุณต้องการลงทะเบียนเส้นทางเพิ่มเติมนอกการลงทะเบียนครั้งแรกเมื่อใด ดูเหมือนจะงี่เง่า ฉันเดาว่าไม่มีวิธีสร้างเส้นทางแบบไดนามิก
VSO

1
ดูสิ่งนี้โดยผู้ชนะผู้เขียนเราเตอร์เชิงมุม
nikhil mehta

คำตอบ:


124

ฉันขอแนะนำให้อ่านบทความนี้:

โมดูลกับผู้ให้บริการ

เมื่อคุณนำเข้าโมดูลคุณมักจะใช้การอ้างอิงไปยังคลาสโมดูล:

@NgModule({
    providers: [AService]
})
export class A {}

-----------------------------------

@NgModule({
    imports: [A]
})
export class B

ด้วยวิธีนี้ผู้ให้บริการทั้งหมดที่ลงทะเบียนในโมดูลAจะถูกเพิ่มไปยังหัวฉีดรากและพร้อมใช้งานสำหรับแอปพลิเคชันทั้งหมด

แต่มีอีกวิธีหนึ่งในการลงทะเบียนโมดูลกับผู้ให้บริการเช่นนี้:

@NgModule({
    providers: [AService]
})
class A {}

export const moduleWithProviders = {
    ngModule: A,
    providers: [AService]
};

----------------------

@NgModule({
    imports: [moduleWithProviders]
})
export class B

สิ่งนี้มีความหมายเหมือนกับข้อก่อนหน้า

คุณอาจรู้ว่าโมดูลโหลดแบบขี้เกียจมีหัวฉีดของตัวเอง ดังนั้นสมมติว่าคุณต้องการลงทะเบียนAServiceให้พร้อมใช้งานสำหรับแอปพลิเคชันทั้งหมด แต่บางส่วนBServiceจะพร้อมใช้งานสำหรับโมดูลที่โหลดแบบขี้เกียจเท่านั้น คุณสามารถ refactor โมดูลของคุณได้ดังนี้:

@NgModule({
    providers: [AService]
})
class A {}

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [AService]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [BService]
};

------------------------------------------

@NgModule({
    imports: [moduleWithProvidersForRoot]
})
export class B

// lazy loaded module    
@NgModule({
    imports: [moduleWithProvidersForChild]
})
export class C

ตอนนี้BServiceจะพร้อมใช้งานสำหรับโมดูลโหลดเด็กขี้เกียจเท่านั้นและAServiceจะพร้อมใช้งานสำหรับแอปพลิเคชันทั้งหมด

คุณสามารถเขียนด้านบนใหม่เป็นโมดูลที่ส่งออกได้ดังนี้:

@NgModule({
    providers: [AService]
})
class A {
    forRoot() {
        return {
            ngModule: A,
            providers: [AService]
        }
    }

    forChild() {
        return {
            ngModule: A,
            providers: [BService]
        }
    }
}

--------------------------------------

@NgModule({
    imports: [A.forRoot()]
})
export class B

// lazy loaded module
@NgModule({
    imports: [A.forChild()]
})
export class C

เกี่ยวข้องกับ RouterModule อย่างไร?

สมมติว่าทั้งคู่เข้าถึงได้โดยใช้โทเค็นเดียวกัน:

export const moduleWithProvidersForRoot = {
    ngModule: A,
    providers: [{provide: token, useClass: AService}]
};

export const moduleWithProvidersForChild = {
    ngModule: A,
    providers: [{provide: token, useClass: BService}]
};

ด้วยการกำหนดค่าแยกต่างหากเมื่อคุณขอtokenจากโมดูลที่โหลดแบบขี้เกียจคุณจะได้รับBServiceตามที่วางแผนไว้

RouterModule ใช้ROUTESโทเค็นเพื่อรับเส้นทางทั้งหมดที่เฉพาะเจาะจงไปยังโมดูล เนื่องจากต้องการให้มีเส้นทางเฉพาะสำหรับโมดูลที่โหลดแบบ lazy เพื่อให้พร้อมใช้งานภายในโมดูลนี้ (อะนาล็อกกับ BService ของเรา) จึงใช้การกำหนดค่าที่แตกต่างกันสำหรับโมดูลลูกที่โหลดแบบ lazy:

static forChild(routes: Routes): ModuleWithProviders {
    return {
        ngModule: RouterModule, 
        providers: [{provide: ROUTES, multi: true, useValue: routes}]
    };
}

3
ดังนั้นกล่าวอีกนัยหนึ่งเราควรเรียก forChild () ในโมดูลคุณลักษณะเนื่องจาก forRoot () ถูกเรียกในโมดูลรูทแล้วและมีการเพิ่มบริการที่จำเป็นทั้งหมด ดังนั้นการเรียก forRoot () อีกครั้งจะนำไปสู่สถานะที่คาดเดาไม่ได้?
Wachburn

7
@Wachburn การโทรforRootในโมดูลที่โหลดแบบขี้เกียจจะสร้างอินสแตนซ์ใหม่ของบริการทั่วโลกทั้งหมดในหัวฉีดโมดูลที่โหลดแบบขี้เกียจ ใช่สิ่งนี้จะนำไปสู่ผลลัพธ์ที่คาดเดาไม่ได้ อ่านบทความนี้ด้วยการหลีกเลี่ยงความสับสนทั่วไปกับโมดูลใน Angular
Max Koretskyi

1
@ คุณ Willwsharp ทำไม?
Max Koretskyi

1
ฉันเข้าใจเรื่องนี้อย่างสมบูรณ์ Routermodule.foRoot, VS Routermodule.forChild ต่างกันอย่างไร? forRoot & forChild เป็นเพียงวิธีการแบบคงที่ซึ่งให้ผลตอบแทนตามค่าที่ส่งผ่าน แล้วในโมดูลแอพทำไมฉันถึงใช้ forChild ไม่ได้ ?? เหตุใดจึงเกิดข้อผิดพลาดในการขว้างปา ทำไมฉันไม่สามารถใช้ forRoot หลายตัวได้
Subhadeep

2
ในบางครั้งการตอบให้ตรงประเด็นก็เป็นเรื่องดี ข้อมูลที่มากเกินไปส่วนใหญ่มักนำไปสู่ความสับสนมากขึ้น
AdépòjùOlúwáségun

33

ฉันคิดว่าคำตอบนั้นถูกต้อง แต่ฉันคิดว่ามีบางอย่างหายไป
สิ่งที่หายไปคือ "ทำไมและแก้อะไรได้บ้าง"
โอเคเริ่มได้.

ก่อนอื่นเรามาพูดถึงข้อมูลบางอย่าง:

โมดูลทั้งหมดสามารถเข้าถึงรูทเซอร์วิสได้
ดังนั้นแม้แต่โมดูลที่โหลดขี้เกียจก็สามารถใช้บริการที่มีให้ในapp.module.
จะเกิดอะไรขึ้นหากโมดูลที่โหลดแบบขี้เกียจจะให้บริการแก่ตัวเองซึ่งโมดูลแอปให้มาแล้ว จะมี2อินสแตนซ์
มันไม่ได้เป็นปัญหาแต่บางครั้งก็คือ
เราจะแก้ได้อย่างไร? อย่านำเข้าโมดูลกับผู้ให้บริการนั้นไปยังโมดูลที่โหลดแบบขี้เกียจ

ตอนจบของเรื่อง.

นี่เป็นเพียงการแสดงให้เห็นว่าโมดูลที่โหลดแบบขี้เกียจมีจุดฉีดของตัวเอง (เมื่อเทียบกับโมดูลที่ไม่ได้โหลดแบบขี้เกียจ)

แต่จะเกิดอะไรขึ้นเมื่อมีการประกาศโมดูลที่ใช้ร่วมกัน (!)providersและโมดูลนั้นถูกนำเข้าโดย lazy และ app.module ? อีกครั้งเช่นที่เรากล่าวไปสองกรณี

แล้วเราจะแก้ปัญหานี้ใน POV โมดูลที่แชร์ได้อย่างไร? เราต้องการวิธีที่จะไม่ใช้providers:[]! ทำไม? เพราะมันจะถูกนำเข้าโดยอัตโนมัติทั้งในการใช้งาน lazy และ app.module และเราไม่ต้องการสิ่งนั้นเนื่องจากเราเห็นว่าแต่ละตัวจะมีอินสแตนซ์ที่แตกต่างกัน

ปรากฎว่าเราสามารถประกาศโมดูลที่ใช้ร่วมกันที่ไม่มีproviders:[]แต่ยังคงให้ผู้ให้บริการ (ขออภัย :))

อย่างไร? แบบนี้ :

ใส่คำอธิบายภาพที่นี่

สังเกตไม่มีผู้ให้บริการ

แต่

  • จะเกิดอะไรขึ้นเมื่อ app.module นำเข้าโมดูลที่ใช้ร่วมกันด้วย POV ของบริการ ไม่มีอะไร

  • จะเกิดอะไรขึ้นเมื่อโมดูลขี้เกียจจะนำเข้าโมดูลที่ใช้ร่วมกันด้วย POV ของบริการ ไม่มีอะไร

การเข้าสู่กลไกแบบแมนนวลผ่านการประชุม:

คุณจะสังเกตเห็นว่าผู้ให้บริการในภาพมีservice1และservice2

สิ่งนี้ช่วยให้เราสามารถนำเข้าservice2สำหรับโมดูลที่โหลดแบบขี้เกียจและservice1สำหรับโมดูลที่ไม่ขี้เกียจ ( ไอ ... เตอร์ .... ไอ )

BTW ไม่มีใครหยุดให้คุณโทรforRootภายในโมดูลที่ขี้เกียจ แต่คุณจะมี 2 อินสแตนซ์เพราะapp.moduleควรทำเช่นกันดังนั้นอย่าทำในโมดูลขี้เกียจ

นอกจากนี้ยัง - ถ้าapp.moduleโทรforRoot(และไม่มีใครโทรforchild) - ที่ดี service1แต่หัวฉีดรากจะมีเพียง (ใช้ได้กับทุกแอพ)

แล้วทำไมเราถึงต้องการ? ผมว่า :

ช่วยให้โมดูลที่ใช้ร่วมกันสามารถแยกผู้ให้บริการที่แตกต่างกันเพื่อใช้กับโมดูลที่กระตือรือร้นและโมดูลที่ขี้เกียจ - ผ่านforRootและforChildการประชุม ฉันพูดซ้ำ: การประชุม

แค่นั้นแหละ.

รอ !! ไม่ใช่คำเดียวเกี่ยวกับซิงเกิลตัน ?? แล้วทำไมฉันถึงอ่านซิงเกิลตันทุกที่?

ดี - มันซ่อนอยู่ในประโยคด้านบน ^

จะช่วยให้โมดูลที่ใช้ร่วมกันเพื่อให้สามารถแยกผู้ให้บริการที่แตกต่างกันที่จะใช้กับโมดูลกระตือรือร้นและโมดูลขี้เกียจ - ผ่านforRoot และ forChild

ประชุม (!!!) ช่วยให้สามารถเดี่ยว - หรือจะแม่นยำมากขึ้น - ถ้าคุณจะไม่ติดตามการประชุม - คุณจะไม่ได้รับเดี่ยว
ดังนั้นหากคุณเพียงโหลดforRootในapp.module แล้วคุณจะได้รับเพียงหนึ่งตัวอย่างเพราะคุณควรจะเรียกมันในforRoot BTW - ณ จุดนี้คุณลืมไปได้เลย โมดูลที่โหลดแบบขี้เกียจไม่ควร / ไม่โทร- ดังนั้นคุณจึงปลอดภัยใน POV ของซิงเกิลตันapp.module
forChildforRoot

forRoot และ forChild ไม่ใช่แพ็คเกจที่ไม่สามารถแตกได้เพียงแค่ไม่มีจุดใดที่จะเรียกหา Root ซึ่งเห็นได้ชัดว่าจะโหลดเข้ามาเท่านั้นapp.module โดยไม่ให้ความสามารถสำหรับโมดูลขี้เกียจมีบริการของตัวเองโดยไม่ต้องสร้างบริการใหม่ที่ควรจะเป็น - เดี่ยว.

อนุสัญญานี้ให้ความสามารถที่ดีที่เรียกว่าforChild- ใช้ "บริการเฉพาะสำหรับโมดูลที่โหลดแบบขี้เกียจ"

นี่คือผู้ให้บริการรูทสาธิตให้ตัวเลขบวกโมดูลที่โหลดแบบขี้เกียจจะให้จำนวนลบ


1
1. POV ที่นี่คืออะไร? 2. stackblitz ไม่ทำงานอีกต่อไป
Nicholas K

@NicholasK stackblitz นี้มักจะทำให้สิ่งต่างๆยุ่งเหยิงหลังจากนั้นไม่นาน ฉันจะพยายามอัปโหลดเวอร์ชันใหม่
Royi Namir

26

เอกสารระบุชัดเจนว่าอะไรคือจุดประสงค์ของความแตกต่างนี้ที่นี่: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

เรียก forRoot เฉพาะในโมดูลแอ็พพลิเคชันรูท AppModule การเรียกมันในโมดูลอื่น ๆ โดยเฉพาะอย่างยิ่งในโมดูลที่โหลดแบบขี้เกียจจะขัดกับเจตนาและมีแนวโน้มที่จะทำให้เกิดข้อผิดพลาดรันไทม์

อย่าลืมนำเข้าผลลัพธ์ อย่าเพิ่มลงในรายการ @NgModule อื่น ๆ

แอพลิเคชันทุกคนมีตรงหนึ่งจุดเริ่มต้น (root) ที่ให้บริการเส้นทางหลักควรจะเริ่มต้นได้ด้วยforRootในขณะที่เส้นทางโดยเฉพาะอย่างยิ่งคุณสมบัติ "เด็ก" ควรจะลงทะเบียน additionaly forChildกับ เป็นประโยชน์อย่างยิ่งสำหรับโมดูลย่อยและโมดูลที่โหลดแบบขี้เกียจซึ่งไม่จำเป็นต้องโหลดเมื่อเริ่มต้นแอปพลิเคชันและตามที่ @Harry Ninh กล่าวว่าพวกเขาได้รับคำสั่งให้นำ RouterService มาใช้ใหม่แทนการลงทะเบียนบริการใหม่ซึ่งอาจทำให้เกิดข้อผิดพลาดรันไทม์


2
เอกสารเหล่านั้นดูเหมือนจะถูกย้ายv2.angular.io/docs/ts/latest/guide/…
PeterS

1

หาก appRoutes มีเส้นทางไปยังฟังก์ชันต่างๆในไซต์ (admin crud, user crud, book crud) และเราต้องการแยกมันออกเราสามารถทำได้:

 imports: [
    BrowserModule, HttpModule,
    AppRoutingModule,
    RouterModule.forRoot(categoriesRoutes),
    RouterModule.forRoot(auteursRoutes),
  ],

และสำหรับเส้นทาง:

const auteursRoutes:Routes=[
  {path:'auteurs/ajouter',component:CreerAuteurComponent},
]
const categoriesRoutes: Routes = [


  {path:'categories/consulter',component:ConsultercategoriesComponent},
  {path:'categories/getsouscategoriesbyid/:id',component:GetsouscategoriesbyIDComponent},
  {path:'categories/ajout',component:CreerCategorieComponent},
 {path:'categories/:id',component:ModifiercategorieComponent},
 {path:'souscategories/ajout/:id',component:AjoutersouscategorieComponent},
 {path:'souscategories/lecture/:id1',component:SouscategoriesComponent},
 {path:'souscategories/modifier/:id1',component:ModifiersupprimersouscategorieComponent},
  {path:'uploadfile',component:UploadfileComponent},
  {path:'categories',component:ConsultercategoriesComponent},



]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.