RouterModule.forRoot (ROUTES) so với RouterModule.forChild (ROUTES)


111

Sự khác biệt giữa hai điều này là gì và các trường hợp sử dụng cho mỗi loại là gì?

Tài liệu không thực sự hữu ích:

forRoot tạo một mô-đun chứa tất cả các chỉ thị, các tuyến đã cho và chính dịch vụ bộ định tuyến.

forChild tạo một mô-đun chứa tất cả các chỉ thị và các tuyến đã cho, nhưng không bao gồm dịch vụ bộ định tuyến.

Dự đoán mơ hồ của tôi là một cái dành cho mô-đun 'chính' và cái kia dành cho bất kỳ mô-đun nào được nhập (vì chúng đã có sẵn dịch vụ từ mô-đun chính), nhưng tôi thực sự không thể nghĩ ra trường hợp sử dụng.


1
Bạn có thể nói cụ thể hơn về những gì bạn không hiểu? Trích dẫn bạn đã bao gồm theo nghĩa đen cho bạn biết sự khác biệt là gì.
jonrsharpe

2
Tôi không hiểu mục đích của việc sử dụng .forChild () là gì. Khi nào tôi muốn các chỉ thị và các tuyến đường không có dịch vụ? Trong thời gian chờ đợi, vui lòng trả lời câu hỏi bạn đã xóa khỏi bài đăng ...
VSO

18
Chỉ nên có một RouterServicecho một ứng dụng Angular2. forRootsẽ khởi tạo dịch vụ đó và đăng ký nó tới DI cùng với một số tuyến đường cấu hình, trong khi forChildsẽ chỉ đăng ký configs tuyến đường bổ sung và nói Angular2 để tái sử dụng RouterServiceforRootđã tạo ra.
Harry Ninh

@HarryNinh: Cảm ơn - đó là những gì tôi đang tìm kiếm. Mặc dù vậy, khi nào bạn muốn đăng ký các tuyến đường bổ sung ngoài đăng ký ban đầu? Có vẻ hơi ngớ ngẩn. Tôi đoán không có cách nào để tạo các tuyến đường một cách linh hoạt.
VSO

1
xem điều này bởi tác giả bộ định tuyến góc cạnh victor.
nikhil mehta

Câu trả lời:


123

Tôi thực sự khuyên bạn nên đọc bài viết này:

Mô-đun với các nhà cung cấp

Khi bạn nhập một mô-đun, bạn thường sử dụng một tham chiếu đến lớp mô-đun:

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

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

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

Bằng cách này, tất cả các nhà cung cấp đã đăng ký trên mô-đun Asẽ được thêm vào bộ phun gốc và có sẵn cho toàn bộ ứng dụng.

Nhưng có một cách khác để đăng ký một mô-đun với các nhà cung cấp như sau:

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

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

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

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

Điều này có cùng ý nghĩa như trước đó.

Bạn có thể biết rằng các mô-đun được tải lười biếng có bộ phun riêng. Vì vậy, giả sử bạn muốn đăng ký AServiceđể có sẵn cho toàn bộ ứng dụng, nhưng một số BServicechỉ khả dụng cho các mô-đun được tải chậm. Bạn có thể cấu trúc lại mô-đun của mình như sau:

@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

Bây giờ BServicesẽ chỉ có sẵn cho các mô-đun được tải lười biếng trẻ em và AServicesẽ có sẵn cho toàn bộ ứng dụng.

Bạn có thể viết lại phần trên dưới dạng mô-đun được xuất như thế này:

@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

Điều đó có liên quan đến RouterModule như thế nào?

Giả sử cả hai đều được truy cập bằng cùng một mã thông báo:

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

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

Với các cấu hình riêng biệt khi bạn yêu cầu tokentừ một mô-đun được tải chậm, bạn sẽ nhận được BServiceđúng như kế hoạch.

RouterModule sử dụng ROUTESmã thông báo để nhận tất cả các tuyến đường cụ thể cho một mô-đun. Vì nó muốn các tuyến đường cụ thể cho mô-đun được tải lười biếng có sẵn bên trong mô-đun này (tương tự với BService của chúng tôi) nên nó sử dụng cấu hình khác nhau cho các mô-đun con được tải lười biếng:

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

3
vì vậy nói cách khác, chúng ta nên gọi forChild () trong các mô-đun tính năng vì forRoot () đã được gọi trong mô-đun gốc và tất cả các dịch vụ cần thiết đã được thêm vào. Vì vậy, việc gọi lại forRoot () sẽ dẫn đến các trạng thái không thể đoán trước?
Wachburn

7
@Wachburn, việc gọi forRoottrong một mô-đun được tải lười biếng sẽ tạo ra các phiên bản mới của tất cả các dịch vụ toàn cầu trong bộ phun mô-đun được tải lười biếng. Vâng, điều này sẽ dẫn đến kết quả không thể đoán trước. Cũng đọc bài viết này Tránh những nhầm lẫn phổ biến với các module trong góc
Max Koretskyi

1
@Willwsharp, tại sao?
Max Koretskyi

1
Tôi hoàn toàn hiểu điều này. Nhưng sự khác biệt giữa Routermodule.foRoot, VS Routermodule.forChild là gì? forRoot & forChild chỉ là phương thức tĩnh trả lại đối tượng dựa trên giá trị được truyền vào. Vì vậy, trong mô-đun ứng dụng, tại sao tôi không thể sử dụng forChild ?? tại sao lỗi ném của nó? Tại sao tôi không thể sử dụng nhiều forRoot?
Subhadeep

2
Đôi khi, chỉ cần đi thẳng vào vấn đề với các câu trả lời. quá tải thông tin hầu hết các lần dẫn đến nhiều nhầm lẫn.
Adépòjù Olúwáségun

33

Tôi nghĩ các câu trả lời là đúng nhưng tôi nghĩ rằng một cái gì đó còn thiếu.
Điều còn thiếu là "tại sao và nó giải quyết được gì?".
Ok, bắt đầu thôi.

Đầu tiên, hãy đề cập đến một số thông tin:

Tất cả các mô-đun đều có quyền truy cập vào các dịch vụ gốc.
Vì vậy, ngay cả các mô-đun được tải lười biếng cũng có thể sử dụng một dịch vụ được cung cấp trong app.module.
Điều gì sẽ xảy ra nếu một mô-đun được tải chậm sẽ cung cấp cho chính nó một dịch vụ mà mô-đun ứng dụng đã cung cấp? sẽ có 2 trường hợp.
Nó không phải là một vấn đề nhưng đôi khi nó là như vậy .
Làm thế nào chúng ta có thể giải quyết nó ? chỉ cần không nhập một mô-đun với nhà cung cấp đó vào các mô-đun được tải chậm.

Kết thúc câu chuyện.

^ Này chỉ để cho thấy rằng các mô-đun được tải lười biếng có điểm tiêm riêng của chúng (trái ngược với các mô-đun không được tải lười biếng).

Nhưng điều gì sẽ xảy ra khi một mô-đun chia sẻ (!) Đã khai báo providersvà mô-đun đó được nhập bởi lazy app.module ? Một lần nữa, như chúng tôi đã nói, hai trường hợp.

Vậy làm thế nào chúng ta có thể giải quyết điều này trong POV mô-đun chia sẻ? Chúng tôi cần một cách để không sử dụng providers:[]! Tại sao? bởi vì chúng sẽ được tự động nhập vào cả lazy và app.module và chúng tôi không muốn điều đó như chúng ta đã thấy rằng mỗi cái sẽ có một phiên bản khác nhau.

Chà, hóa ra chúng ta có thể khai báo một mô-đun được chia sẻ sẽ không có providers:[], nhưng vẫn sẽ cung cấp cho các nhà cung cấp (xin lỗi :))

Làm sao? Như thế này :

nhập mô tả hình ảnh ở đây

Thông báo, không có nhà cung cấp.

Nhưng

  • điều gì sẽ xảy ra bây giờ khi app.module sẽ nhập mô-đun được chia sẻ với POV của dịch vụ? KHÔNG CÓ GÌ.

  • điều gì sẽ xảy ra bây giờ khi một mô-đun lười biếng sẽ nhập mô-đun được chia sẻ với POV của dịch vụ? KHÔNG CÓ GÌ.

Nhập cơ chế Thủ công thông qua quy ước:

Bạn sẽ nhận thấy rằng các nhà cung cấp trong hình ảnh có service1service2

Điều này cho phép chúng tôi nhập service2cho các mô-đun được tải chậm và service1cho các mô-đun không lười. ( khụ ... bộ định tuyến .... khụ )

BTW, không ai ngăn cản bạn gọi forRoottrong mô-đun lười biếng. nhưng bạn sẽ có 2 trường hợp vì app.modulecũng nên làm điều đó - vì vậy đừng làm điều đó trong các mô-đun lười biếng.

Ngoài ra - nếu app.modulecuộc gọi forRoot(và không có ai gọi forchild) - điều đó tốt, nhưng bộ phun gốc sẽ chỉ cóservice1 . (có sẵn cho tất cả các ứng dụng)

Vậy tại sao chúng ta cần nó? Tôi sẽ nói :

Nó cho phép một mô-đun được chia sẻ, có thể phân chia các nhà cung cấp khác nhau của nó để được sử dụng với các mô-đun háo hức và mô-đun lười biếng - thông qua forRootforChildquy ước. Tôi nhắc lại: quy ước

Đó là nó.

CHỜ ĐỢI !! không một từ nào về singleton ?? vậy tại sao tôi đọc singleton ở khắp mọi nơi?

Chà - nó ẩn trong câu trên ^

Nó cho phép một mô-đun được chia sẻ, có thể phân chia các nhà cung cấp khác nhau của nó để được sử dụng với các mô-đun háo hức và mô-đun lười biếng - thông qua forRoot và forChild .

Quy ước (!!!) cho phép nó là singleton - hay chính xác hơn - nếu bạn không tuân theo quy ước - bạn sẽ KHÔNG nhận được singleton.
Vì vậy, nếu bạn chỉ tải forRoottrong app.module , thì bạn chỉ nhận được một thể hiện vì bạn chỉ nên gọi forRootnó trong app.module.
BTW - tại thời điểm này bạn có thể quên forChild. mô-đun được tải lười biếng không nên / sẽ không gọiforRoot - vì vậy bạn an toàn trong POV của singleton.

forRoot và forChild không phải là một gói không thể phá vỡ - chỉ là không có điểm nào để gọi Root mà rõ ràng là sẽ chỉ được tải trong app.module mà không cung cấp khả năng cho các mô-đun lười biếng, có các dịch vụ của riêng họ, mà không cần tạo dịch vụ mới-mà-nên-có -singleton.

Quy ước này cung cấp cho bạn một khả năng tuyệt vời được gọi là forChild- sử dụng "dịch vụ chỉ dành cho các mô-đun được tải chậm".

Đây là một bản demo Các nhà cung cấp gốc mang lại số dương, các mô-đun được tải chậm cho ra số âm.


1. POV ở đây là gì? 2. stackblitz không hoạt động nữa.
Nicholas K

@NicholasK stackblitz này luôn làm mọi thứ rối tung lên sau một thời gian. tôi sẽ cố gắng tải lên phiên bản mới
Royi Namir

26

Tài liệu nêu rõ mục đích của sự khác biệt này là gì tại đây: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Chỉ gọi forRoot trong mô-đun ứng dụng gốc, AppModule. Gọi nó trong bất kỳ mô-đun nào khác, đặc biệt là trong một mô-đun được tải chậm, là trái với mục đích và có khả năng tạo ra lỗi thời gian chạy.

Hãy nhớ nhập kết quả; không thêm nó vào bất kỳ danh sách @NgModule nào khác.

Mọi ứng dụng đều có chính xác một điểm bắt đầu (gốc) nơi dịch vụ định tuyến chính nên được khởi tạo forRoot, trong khi các tuyến đường cho các tính năng "con" cụ thể phải được đăng ký bổ sung với forChild. Nó cực kỳ hữu ích cho các mô-đun con và mô-đun tải lười biếng không phải tải khi bắt đầu ứng dụng và như @Harry Ninh cho biết họ được yêu cầu sử dụng lại RouterService thay vì đăng ký dịch vụ mới, điều này có thể gây ra lỗi thời gian chạy.


2
Những tài liệu đó dường như đã được chuyển đi, v2.angular.io/docs/ts/latest/guide/…
PeterS

1

Nếu appRoutes có chứa đường dẫn đến các chức năng khác nhau trong trang web (thông tin quản trị, thông tin người dùng, tài liệu giới thiệu sách) và chúng tôi muốn tách chúng ra, chúng tôi có thể chỉ cần thực hiện điều đó:

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

Và cho các tuyến đường:

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},



]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.