ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Angular 路由出口指南:使用 RouterOutlet 渲染与组合视图

Angular 路由出口指南:使用 RouterOutlet 渲染与组合视图 Angular 路由出口指南使用 RouterOutlet 渲染与组合视图【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angularRouterOutlet 是 Angular Router 的核心渲染指令它充当模板中的占位符Angular 会根据当前 URL 匹配到的路由配置在该位置动态渲染对应组件。本文将结合本仓库angular/angular中路由模块的源码实现系统讲解 RouterOutlet 的基础用法、子路由嵌套、命名出口辅助路由、生命周期事件以及通过routerOutletData传递上下文数据等完整方案帮助你理解路由视图渲染的原理并应用到真实项目中。RouterOutlet 是什么在 RouterOutlet 指令源码 中可以看到它通过selector: router-outlet注册为模板标签本质是一个占位符placeholderRouterOutlet指令是模板中标记路由应在何处为当前 URL 渲染组件的占位符。关于它的官方完整描述可参考 参考文档 与 angular.dev 路由出口指南。基本用法非常简单在模板中放入router-outlet /Angular 会将路由组件作为该元素的紧邻兄弟节点插入。也就是说路由内容会渲染在 outlet 元素之后而router-outlet元素本身始终保留在 DOM 中作为未来导航时的参照锚点。app-header / !-- Angular 在此处插入你的路由内容 -- router-outlet / app-footer /在独立组件standalone component模式下使用出口需要在组件装饰器中显式导入RouterOutletimport {Component} from angular/core; import {RouterOutlet} from angular/router; Component({ selector: app-root, imports: [RouterOutlet], templateUrl: ./app.html, styleUrl: ./app.css, }) export class App {}例如应用定义了如下路由import {Routes} from angular/router; import {Home} from ./home; import {Products} from ./products; const routes: Routes [ { path: , component: Home, title: Home Page, }, { path: products, component: Products, title: Our Products, }, ];当用户访问/products时Angular 渲染的结果如下app-products /被插到出口元素之后app-header / app-products / app-footer /当用户返回首页时则渲染app-home /其余布局头部、页脚保持不变。这正是 RouterOutlet 带来的核心体验只有出口区域随 URL 变化页面其他部分无需整页刷新。子路由与嵌套出口随着应用复杂度提升你可能希望创建相对于根组件以外某组件的路由——这类嵌套路由称为子路由child routes。子路由意味着需要添加第二个router-outlet父组件拥有自己的出口以渲染子组件。子路由场景中一个典型特征是组件往往自带导航和自己的出口。例如Settings组件根据用户选择展示对应面板h1Settings/h1 nav ul lia routerLinkprofileProfile/a/li lia routerLinksecuritySecurity/a/li /ul /nav router-outlet /子路由与普通路由一样需要path和component唯一区别是子路由被放在父路由的children数组中const routes: Routes [ { path: settings, component: Settings, // 这是模板中含 router-outlet 的组件 children: [ { path: profile, // 子路由路径 component: Profile, // 路由器渲染的子路由组件 }, { path: security, component: Security, // 另一个被渲染的子路由组件 }, ], }, ];配置好routes与router-outlet后应用便启用了嵌套路由。出口的注册与匹配机制从源码层面看子路由的渲染依赖一套按名字注册的出口上下文。router_outlet_context.ts 中的ChildrenOutletContexts维护了一个Mapstring, OutletContext出口指令实例化时调用onChildOutletCreated(childName, outlet)把自己注册到父级上下文出口销毁时调用onChildOutletDestroyed(childName)但保留 context——因为出口可能位于*ngIf内部之后还会被重建OutletContext同时记录了出口、当前ActivatedRoute、可重挂载的组件引用attachRef以及子级上下文。在 RouterOutlet.initializeOutletWithName 中可以看到如果出口在路由激活后才被实例化例如位于条件渲染块内会从parentContexts中取出已保存的context.route走attach已有组件引用或activateWith按配置新建组件流程补齐渲染。这就是出口被ngIf延迟出现时路由内容仍能正确填充的底层原因。命名出口辅助路由一个页面可以拥有多个出口。通过给router-outlet赋予name属性即可把内容定向到指定出口。每个出口必须唯一命名且名字不能被动态设置或更改默认名字为primary对应主路由出口router-outlet / !-- Primary 主出口 -- router-outlet namesidebar / !-- Secondary 辅助出口 --Angular 会把出口名称与路由配置上的outlet属性进行匹配。outlet是 Route 接口 的可选字段其注释明确写着匹配到 path 时组件应放置到哪个RouterOutlet对象中{ path: user/:id, component: UserDetails, outlet: additional-actions }辅助路由的 URL 形态与导航多个命名出口意味着路由器会为每个出口维护独立的导航分支。从源码注释与 create_url_tree.ts 中的示例 可以确认其 URL 语法http://base-path/primary-route-path(outlet-name:route-path)例如构建/team/33/(user/11//right:chat)这类 URL——括号内outlet-name:route-path表示辅助分支多个辅助分支之间用//分隔。源码中的isCommandWithOutlets检查见 create_url_tree.ts表明当你通过编程式导航或指令传入带outlets键值对的对象时路由器会逐出口地把命令应用到既有段上且约束{outlets:{}}必须是命令序列中的最后一项。因此可用RouterLink或命令式导航同时驱动多个出口// 同时驱动主出口与名为 sidebar 的辅助出口 this.router.navigate([ {outlets: {primary: [products], sidebar: [chat]}}, ]);PRIMARY_OUTLET常量被 RouterOutlet 的name输入作为默认值见 router_outlet.ts由此印证默认名字为 primary这一行为。出口生命周期事件RouterOutlet在组件切换时会发出四类生命周期事件EventDescriptionactivate当新的组件被实例化时触发发出组件实例deactivate当组件被销毁时触发attach当RouteReuseStrategy指示出口重新挂载attach子树时触发detach当RouteReuseStrategy指示出口分离detach子树时触发使用标准的模板事件绑定即可监听这些事件router-outlet (activate)onActivate($event) (deactivate)onDeactivate($event) (attach)onAttach($event) (detach)onDetach($event) /这些事件在源码中分别对应activateEvents、deactivateEvents、attachEvents、detachEvents四个EventEmitter输出见 router_outlet.tsactivateWith在通过location.createComponent(...)创建并激活组件后发出activateEvents.emit(this.activated.instance)见 L404-L413deactivate销毁当前组件后发出deactivateEvents见 L374-L382detach在RouteReuseStrategy要求保留而非销毁子树时调用先location.detach()再从视图容器摘下宿主视图组件实例被返回以便后续重新挂载见 L349-L361attach则把之前分离的ComponentRef通过location.insert(ref.hostView)重新插回出口位置见 L366-L372。attach/detach是视图复用RouteReuseStrategy机制的关键支撑例如实现列表滚动位置保留、表单状态暂存等场景。通过 routerOutletData 向路由组件传递上下文向路由组件传递上下文数据通常需要全局状态或复杂的路由配置。为简化这一痛点每个RouterOutlet都支持routerOutletData输入。被路由到的组件及其子级可以通过ROUTER_OUTLET_DATA注入令牌以signal形式读取这份数据从而在不改动路由定义的前提下实现出口级的定制配置。在父组件模板中传入数据import {Component} from angular/core; import {RouterOutlet} from angular/router; Component({ selector: app-dashboard, imports: [RouterOutlet], template: h2Dashboard/h2 router-outlet [routerOutletData]{layout: sidebar} / , }) export class Dashboard {}在被路由的子组件中通过注入读取import {Component, inject} from angular/core; import {ROUTER_OUTLET_DATA} from angular/router; Component({ selector: app-stats, template: pStats view (layout: {{ outletData().layout }})/p, }) export class Stats { outletData inject(ROUTER_OUTLET_DATA) as Signal{layout: string}; }当 Angular 在对应出口中激活Stats时它会收到注入数据{ layout: sidebar }。注意当routerOutletData输入未设置时注入值默认为null源码中 ROUTER_OUTLET_DATA 令牌的文档注释 亦如此说明。源码中的实现机制routerOutletData是使用新的input()函数声明的 signal 输入见 router_outlet.ts L246。当激活路由组件时RouterOutlet会构造一个自定义的OutletInjector见 L417-L440其get()逻辑做了三类特殊解析请求ActivatedRoute→ 返回当前激活的路由请求ChildrenOutletContexts→ 返回当前出口的子级上下文请求ROUTER_OUTLET_DATA→ 返回routerOutletData这个 signal 本身。随后该注入器被传给location.createComponent(component, { injector })因此被路由组件能够通过inject(ROUTER_OUTLET_DATA)拿到一个 signal。由于数据本身是 signal当父级改变routerOutletData绑定时子组件中的outletData()会自动响应式更新——无需重新导航。单元测试也验证了这一行为见 router_outlet.spec.ts 中更新输入后子组件读取到新值的用例。此外测试还覆盖了嵌套出口的就近覆盖语义见 L785-L852当嵌套的子出口也设置了routerOutletData时最内层出口提供的值会覆盖外层父级提供的值若某级出口未提供数据则该级读取到默认空值。这意味着你可以在应用的不同层级独立注入上下文子级永远以距离最近的出口数据为准。小结主出口router-outlet /是路由视图的挂载锚点内容渲染在出口元素之后子路由通过父组件模板中的嵌套router-outlet /结合路由children数组实现局部刷新体验命名出口支持一页多视图路由通过outlet属性匹配目标出口辅助路由 URL 形如primary-route(outlet-name:route-path)四个生命周期事件activate/deactivate/attach/detach覆盖了组件创建、销毁与视图复用的完整流程routerOutletDataROUTER_OUTLET_DATA为出口提供信号化上下文注入能力无需污染路由定义。如果希望进一步了解路由间如何跳转与传参可继续阅读仓库中的导航相关指南如 navigate-to-routes或直接查看 RouterLink 指令源码 与 共享常量 PRIMARY_OUTLET 定义加深理解。【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表