RouterModule.forRoot(МАРШРУТЫ) против RouterModule.forChild(МАРШРУТЫ)
Каковы различия между этими двумя и каковы варианты использования для каждого?
Документы не совсем полезны:
forRoot создает модуль, который содержит все директивы, данные маршруты и сам сервис маршрутизатора.
forChild создает модуль, который содержит все директивы и данные маршруты, но не включает службу маршрутизатора.
Мое смутное предположение состоит в том, что один предназначен для "основного" модуля, а другой - для любых импортированных модулей (поскольку у них уже будет служба, доступная из основного модуля), но я не могу вспомнить вариант использования.
8 ответов
Я настоятельно рекомендую прочитать эту статью:
Модуль с провайдерами
При импорте модуля вы обычно используете ссылку на класс модуля:
@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
быть доступным только для лениво загруженных модулей. Вы можете изменить свой модуль следующим образом:
@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
токен, чтобы получить все маршруты, специфичные для модуля. Поскольку он хочет, чтобы маршруты, специфичные для лениво загруженного модуля, были доступны внутри этого модуля (аналог нашего BService), он использует другую конфигурацию для лениво загруженных дочерних модулей:
static forChild(routes: Routes): ModuleWithProviders {
return {
ngModule: RouterModule,
providers: [{provide: ROUTES, multi: true, useValue: routes}}]
}
}
Я думаю, что ответы правильные, но я думаю, что чего-то не хватает.
То, чего не хватает, это "почему и что это решает?".
Хорошо, давайте начнем.
Сначала давайте упомянем некоторую информацию:
Все модули имеют доступ к корневым сервисам.
Так что даже лениво загруженные модули могут использовать сервис, который был предоставлен в app.module
,
Что произойдет, если лениво загруженный модуль предоставит себе услугу, которую уже предоставил модуль приложения? будет 2 экземпляра.
Это не проблема, но иногда это так.
Как мы можем решить это? просто не импортируйте модуль с этим провайдером в лениво загруженные модули.
Конец истории.
Это было просто для того, чтобы показать, что у лениво загруженных модулей есть собственная точка впрыска (в отличие от не лениво загружаемых модулей).
Но что происходит, когда общий (!) Модуль объявил providers
и этот модуль импортируется ленивым и app.module
? Опять же, как мы уже говорили, два случая.
Итак, как мы можем решить это в общем модуле POV? нам нужен способ не использовать providers:[]
! Зачем? потому что они будут автоматически импортированы как в lazy, так и в app.module, и мы не хотим этого, поскольку мы увидели, что у каждого будет свой экземпляр.
Что ж, получается, что мы можем объявить общий модуль, который не будет иметь providers:[]
, но все же, предоставим продиверов (извините:))
Как? Как это:
Обратите внимание, нет поставщиков.
Но
что теперь будет, когда app.module будет импортировать общий модуль с POV сервиса? НИЧЕГО ТАКОГО.
что будет теперь, когда ленивый модуль будет импортировать общий модуль с POV службы? НИЧЕГО ТАКОГО.
Ввод ручного механизма через соглашение:
Вы заметите, что провайдеры на фотографиях имеют service1
а также service2
Это позволяет нам импортировать service2
для лениво загруженных модулей и service1
для не ленивых модулей. (кашель... роутер.... кашель)
Кстати, никто не мешает вам позвонить forRoot
в ленивом модуле. но у вас будет 2 случая, потому что app.module
также следует сделать это - так что не делайте этого в ленивых модулях.
Также - если app.module
звонки forRoot
(и никто не звонит forchild
) - это нормально, но корневой инжектор будет иметь только service1
, (доступно для всех приложений)
Так зачем нам это? Я бы сказал:
Это позволяет совместно используемому модулю разделять своих разных провайдеров для использования с готовыми модулями и ленивыми модулями - через
forRoot
а такжеforChild
условность. Я повторяю: конвенция
Вот и все.
ПОДОЖДИТЕ! ни слова о синглтоне?? так почему я везде читаю синглтон?
Ну, это скрыто в предложении выше ^
Это позволяет совместно используемому модулю разделять своих разных провайдеров для использования с готовыми модулями и ленивыми модулями - через forRoot и forChild.
Соглашение (!!!) позволяет ему быть одноэлементным - или быть более точным - если вы не будете следовать соглашению - вы НЕ получите одноэлементный.
Так что, если вы только загружаете forRoot
в app.module
, тогда вы получите только один экземпляр, потому что вы должны только позвонить forRoot
это в app.module
,
Кстати - на данный момент вы можете забыть о forChild
, ленивый загруженный модуль не должен / не будет вызывать forRoot
- так что вы в безопасности от первого лица.
forRoot и forChild не являются одним неразрушимым пакетом, просто нет смысла вызывать Root, который, очевидно, будет загружен только в app.module
не давая возможности для ленивых модулей, иметь свои собственные сервисы, не создавая новые сервисы, которые должны быть одноразовыми.
Это соглашение даст вам хорошую способность под названием forChild
- потреблять "сервисы только для лениво загруженных модулей".
В документации четко указано, какова цель этого различия здесь: https://angular.io/docs/ts/latest/guide/ngmodule.html
Вызывать Root можно только в корневом модуле приложения AppModule. Вызов его в любом другом модуле, особенно в лениво загруженном модуле, противоречит цели и может вызвать ошибку во время выполнения.
Не забудьте импортировать результат; не добавляйте его в любой другой список @NgModule.
Каждое приложение имеет ровно одну отправную точку (корень), где основная служба маршрутизации должна быть инициализирована с forRoot
в то время как маршруты для определенных "дочерних" функций должны быть зарегистрированы дополнительно с forChild
, Это чрезвычайно полезно для субмодулей и лениво загруженных модулей, которые не нужно загружать при запуске приложения, и, как сказал @Harry Ninh, им говорят, что вместо повторной регистрации новой службы необходимо повторно использовать RouterService, что может вызвать ошибку во время выполнения.
В
RouterModule.forRoot(routes)
Статический метод forRoot — это метод, который настраивает корневой модуль маршрутизации для вашего приложения. Когда вы вызываете RouterModule.forRoot(routes), вы просите Angular создать экземпляр класса Router глобально. Точно так же, как Angular создает новый базовый AppModule для импорта всех ваших функциональных модулей, он также предоставляет AppRoutingModule для импорта всех ваших дочерних маршрутов.
В новом приложении, которое вы создали с помощью Angular CLI, метод forRoot фактически уже используется внутри app-routing.module.ts. В вашем приложении вы хотите использовать метод forRoot только один раз. Это связано с тем, что этот метод указывает Angular создавать экземпляр класса Router под капотом, а в вашем приложении может быть только один маршрутизатор. Статический метод forRoot является частью шаблона, который гарантирует, что вы используете одноэлементные классы.
Детские маршруты:
RouterModule.forChild(routes)
Когда вы используете статический метод forChild, вы, по сути, сообщаете Angular: «В приложении уже есть экземпляр Router, поэтому просто зарегистрируйте все эти маршруты в этом экземпляре». Метод forChild — это метод, который вы будете вызывать для регистрации маршрутов в вашем приложении, и вы будете использовать его внутри дочерних модулей маршрутизации, которые вы создаете.
Статический метод forChild полезен, потому что он играет основную часть функциональности модуля Angular, позволяя вам поддерживать разделение задач в вашем приложении.
Предположим, вы хотите создать новый функциональный модуль для пользовательских настроек в своем приложении, и эта функция будет содержать несколько маршрутов. Вместо того, чтобы добавлять эти маршруты напрямую в AppRoutingModule, что в конечном итоге станет несостоятельным по мере роста вашего приложения, вы можете поддерживать разделение проблем в своем приложении, используя метод forChild. Сначала создайте новый UserSettingsRoutingModule.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserSettingsComponent } from './user-settings.component';
import { UserProfileComponent } from './user-profile/user-profile.component';
const routes: Routes = [
{
path: 'settings',
component: UserSettingsComponent,
children: [
{
path: 'profile',
component: UserProfileComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UserSettingsRoutingModule { }
Обратите внимание на использование метода forChild выше. Поскольку вы уже использовали метод forRoot, вам просто нужно зарегистрировать свои маршруты к уже созданному экземпляру маршрутизатора приложения.
Теперь вам нужно создать свой UserSettingsModule следующим образом:
import { NgModule, CommonModule } from '@angular/core';
import { UserSettingsRoutingModule } from './user-settings-routing.module';
@NgModule({
imports: [
CommonModule,
UserSettingsRoutingModule
],
// Configure the rest of your module here
})
export class UserSettingsModule { }
И вот оно! Теперь все, что вам нужно сделать, это импортировать этот UserSettingsModule в ваш корень, AppModule, и ваши дочерние маршруты, указывающие на соответствующие компоненты, будут настроены в вашем приложении.
Я надеюсь, что это руководство помогло вам понять, как статические методы Angular Router forRoot и forChild могут помочь вам создать хорошо структурированные маршруты в вашем приложении. Для получения дополнительной информации ознакомьтесь с документацией
Считайте это реализацией модуля маршрутизатора, отложенные маршруты и общие маршруты должны обрабатываться отдельными способами, поддерживая отложенную загрузку.
@NgModule({
declarations: [
],
exports: [],
})
export class RouteModule {
static forRoot(): ModuleWithProviders<RouteModule> {
return { ngModule: RouteModule, providers: [routerHistoryService,handleCommonRoutesService] };
}
static forChild(): ModuleWithProviders<RouteModule> {
return {
ngModule: RouteModule, providers: [handleChildRouterService]
};
}
}
forroot -> добавит routerModule с поставщиками (службами), поэтому все службы, необходимые для общей угловой маршрутизации (например, routerHistoryService), будут введены в корневой инжектор из модуля приложения (корневой модуль)
forchild -> добавит routerModule с поставщиками (службами), но поскольку общие службы (exrouterHistoryService) уже добавлены в корневой инжектор, из ленивых загружаемых модулей мы можем использовать их и не нужно добавлять снова, если мы добавим их создаст два экземпляра. но могут быть службы, специально необходимые для обработки дочерних маршрутов. поэтому в том случае, когда мы вызываем forchild, мы можем предоставить их (например: handleChildRouterService)
если routerModule не использует этот шаблон, подумайте
1) в корневых инжекторах routerHistoryService предыдущий маршрут - "home / lazymodule"
2) но в ленивом модуле с новой службой истории предыдущий маршрут равен нулю
поэтому у него нет данных для возврата при нажатии кнопки "Назад". Вот почему routerModule реализовал этот шаблон, чтобы гарантировать, что модуль маршрутизатора получил только один экземпляр во всем приложении.
Если 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},
]
Спасибо, Ройи. Это было полезно для меня. Демонстрация больше не запускается для меня, поэтому я обновил ее здесь: https://stackblitz.com/edit/forroot-forchild-example?embed=1&file=src/app/app.component.ts
https://www.freelancermap.com/freelancer-tips/12255-forroot-forchild-angular
Это статья об использовании forRoot и forChild в Angular; Я не особо разбираюсь в этой теме, но, возможно, это может быть полезно!