Angular 团队一直忙于对 Angular 路由器进行一些有意义的更新,这些更新从 Angular v14.2 开始可用。我们很高兴与大家分享一些最近的改进。继续阅读以了解更多信息。
我们引入了一种无需RouterModule
即可使用 Router 的方法,并改进了树摇动以减小包大小。首先,让我们关注新的路由器集成 API。
以下是如何在没有RouterModule
情况下使用路由器。在bootstrapApplication
函数中,我们使用provideRouter
函数向providers
数组提供路由器配置。该函数接受一组应用程序路由。这是一个例子:
// Bootstrap the main application component with routing capabilities
const appRoutes: Routes = […];
bootstrapApplication(AppComponent, {
providers: [
provideRouter(appRoutes),
]
});
将此与在需要模块的应用程序中设置路由器的现有方法进行比较:
const appRoutes: Routes = […];
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(RouterModule.forRoot(appRoutes)),
]
});
新的provideRouter
API 允许开发人员对路由器API 的主要部分进行tree-shake。这是我们第一次能够在 Angular 路由器中启用这种级别的树摇动。现在,团队可以享受更小的捆绑包尺寸。
RouteModule
API 的设计阻止捆绑程序删除未使用的路由器功能。使用RouteModule
API,即使未启用,以下功能也包含在生产包中:
HashLocationStrategy
— 通常用于通过更新片段而不是路径来进行传统路由新的provideRouter
API 改变了这种行为。如果未启用,上面列表中的功能将不再包含在生产包中。
在使用新 API 进行的测试中,我们发现,在未启用任何功能时,从捆绑包中删除这些未使用的功能会导致应用程序包中的路由器代码大小减少 11%。
以下是使用withPreloading
函数选择预加载的示例:
const appRoutes: Routes = [];
bootstrapApplication(AppComponent, {
providers: [
provideRouter(appRoutes, withPreloading(PreloadAllModules))
]
});
路由器防护需要太多的样板文件……
– 大多数 Angular 开发者可能在某个时候
我们收到了多个开发人员的反馈,这些反馈表明开发人员希望减少样板文件并提高生产力。让我们来介绍一些使我们更接近这一目标的令人兴奋的新变化。
使用inject()
功能性路由器防护是轻量级的、符合人体工程学的,并且比基于类的防护更易于组合。
下面是一个功能防护示例,它将组件作为参数,并根据组件的hasUnsavedChanges
属性返回是否可以停用路由:
const route = {
path: ‘edit’,
component: EditCmp,
canDeactivate: [
(component: EditCmp) => !component.hasUnsavedChanges
]
};
此外,这些功能守卫仍然可以通过@angular/core
inject
依赖项:
const route = {
path: ‘admin’,
canActivate: [() => inject(LoginService).isLoggedIn()]
};
Router API 之前要求 Angular 的依赖注入中存在防护程序和解析器。这导致了不必要的样板代码。让我们考虑两个例子。
在第一个示例中,以下是使用InjectionToken提供防护所需的代码:
const UNSAVED_CHANGES_GUARD = new InjectionToken<any>('my_guard', {
providedIn: 'root',
factory: () => (component: EditCmp) => !component.hasUnsavedChanges
});
const route = {
path: 'edit',
component: EditCmp,
canDeactivate: [UNSAVED_CHANGES_GUARD]
};
在第二个示例中,以下是提供防护作为可注入类所需的代码:
@Injectable({providedIn: 'root'})
export class LoggedInGuard implements CanActivate {
constructor(private loginService: LoginService) {}
canActivate() {
return this.loginService.isLoggedIn();
}
}
const route = {
path: 'admin',
canActivate: [LoggedInGuard]
};
请注意,即使我们想要编写一个没有依赖项的简单防护(如第一个示例中所示),我们仍然必须编写InjectionToken
或Injectable
类。通过功能性守卫,开发人员可以创建守卫,甚至是具有依赖关系的守卫,而样板代码要少得多。
函数式守卫也更具可组合性。对路由器的一个常见功能请求是为防护提供按顺序执行而不是一次性执行的选项。如果路由器中没有此选项,则在应用程序中实现此类行为将很困难。使用功能防护可以更顺利地实现这一点。路由器代码中甚至有一个示例测试来演示此行为。
让我们回顾一下代码,以更好地了解其工作原理:
function runSerially(guards: CanActivateFn[]|
CanActivateChildFn[]):
CanActivateFn|CanActivateChildFn {
return (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot) => {
const injector = inject(EnvironmentInjector);
const observables = guards.map(guard => {
const guardResult = injector.runInContext(
() => guard(route, state));
return wrapIntoObservable(guardResult).pipe(first());
});
return concat(…observables).pipe(
takeWhile(v => v === true), last());
};
}
使用功能防护,您可以创建类似工厂的函数来接受配置并返回防护或解析器函数。通过这种模式,我们现在还拥有可配置的守卫和解析器,这是另一个常见的功能请求。 runSerially
函数是一个类似工厂的函数,它接受一个守卫函数列表并返回一个新的守卫。
function runSerially(guards: CanActivateFn[]|CanActivateChildFn[]): CanActivateFn|CanActivateChildFn
首先,我们通过以下方式获取对当前注入器的引用:
const injector = inject(EnvironmentInjector);
当功能防护想要使用依赖注入(DI)时,它们必须同步inject
。每个守卫可以异步执行,并且可以注入它们自己的依赖项。我们需要立即引用当前注入器,以便我们可以在同一注入上下文中继续运行它们:
const guardResult = injector.runInContext(() => guard(route, state));
这也正是路由器启用功能防护功能的方式。
因为守卫可以返回Observable
、 Promise
或同步结果,所以我们使用wrapIntoObservable
帮助器将所有结果转换为Observable
并仅获取第一个发出的值:
return wrapIntoObservable(guardResult).pipe(first());
我们获取所有这些更新的保护函数,依次执行它们(使用concat
),并且仅订阅结果,同时它们中的每一个都指示可以激活路由(即返回true
):
concat(…observables).pipe(takeWhile(v => v === true), last());
就是这样。我们用不到 10 行代码启用了按顺序运行功能防护。我们现在可以在“Route”配置中调用它,如下所示:
{
path: ‘hello-world’,
component: HelloWorld,
canActivate: [runSerially(guard1, guard2, guard3)]
}
所有这些更改均从 Angular v14.2 开始提供。我们希望您喜欢路由器的这些更新,我们很想听听您的想法。您可以在线找到我们。
感谢您继续成为 Angular 社区的一员。
原文链接:https://blog.angular.dev/advancements-in-the-angular-router-5d69ec4c032