首页 web前端 js教程 使用 Angular 中的类型安全路由消除运行时错误

使用 Angular 中的类型安全路由消除运行时错误

Jan 06, 2025 am 07:22 AM

Eliminate Runtime Errors with Type-safe Routes in Angular

重构 Angular 应用程序可能是一把双刃剑。一方面,它允许您提高代码库的可维护性和可扩展性。另一方面,如果您没有采取必要的预防措施来保护您的功能免受意外更改,则可能会导致路由中断。编写广泛的测试或为路由实现可靠的类型概念可以帮助减轻这种风险,但这些方法可能非常耗时,而且可能并不总是可行。在本文中,我们将探索一种更有效的解决方案,该解决方案可以在编译时自动检测损坏的路由,而无需手动测试工作或编写自定义类型注释。我们将通过实现具有嵌套组件的示例 Angular 应用程序并使用 typesafe-routes 库来演示这种方法,以改善开发人员体验并促进参数解析。

为了说明在编译时自动检测损坏的路由的好处,我们将实现一个具有三个嵌套组件的示例 Angular 应用程序:DashboardComponent (/dashboard)、OrgsComponent (/orgs/:orgId) 和 LocationsComponent (/orgs) /:orgId/locations/:locationId)。要设置此示例,我们需要安装 typesafe-routes 库并使用其 createRoutes 函数来定义我们的路由树,如以下代码片段所示。

// app.routes.ts
import { createRoutes, int } from "typesafe-routes";

export const r = createRoutes({
  dashboard: {
    path: ["dashboard"], // ~> "/dashboard"
  },
  orgs: {
    path: ["orgs", int("orgId")], // ~> "/orgs/:orgId"
    children: {
      locations: {
        path: ["locations", int("locationId")], // ~> "locations/:locationId"
        query: [int.optional("page")], // ~> "?page=[number]"
      },
    },
  },
});
登录后复制
登录后复制
登录后复制

让我们仔细看看代码片段。我们从 typesafe-routes 导入 createRoutes 并将我们的路由作为其第一个参数传递。这些路由被定义为一个嵌套对象,在根级别具有两个属性:仪表板和组织。每个属性都分配有一个路径,以数组的形式指定段。例如,[“dashboard”]数组对应于路径/dashboard。 orgs 路径更复杂,因为它包含一个名为 orgId 的整数类型参数。请注意,整数不是原生 JavaScript 类型,而是使用 int 函数定义的自定义类型,它在后台使用数字来模仿整数的特征。 orgs 路由有一个children 属性,它指定一个名为locations 的子路由。 Locations 路由与 orgs 路由类似,但它指定了一个额外的可选 int 类型的搜索参数页面。

createRoutes 使用有关路由的信息来创建包装在 Proxy 对象中的上下文。您不需要了解有关该代理对象的详细信息,但必须了解,由于该对象,您可以访问应用程序中任何位置的所有路由规范来渲染和解析路由和参数。

我们将createRoutes返回的Proxy对象分配给r。这意味着您可以使用 r.dashboard 访问仪表板路径,使用 r.orgs.locations 访问位置路径,等等。

渲染模板

定义了路由后,我们现在可以继续下一步:使用 Angular-router 注册它们。

// app.routes.ts
import { createRoutes, int } from "typesafe-routes";

export const r = createRoutes({
  dashboard: {
    path: ["dashboard"], // ~> "/dashboard"
  },
  orgs: {
    path: ["orgs", int("orgId")], // ~> "/orgs/:orgId"
    children: {
      locations: {
        path: ["locations", int("locationId")], // ~> "locations/:locationId"
        query: [int.optional("page")], // ~> "?page=[number]"
      },
    },
  },
});
登录后复制
登录后复制
登录后复制

代码片段显示了 Angular Router 的嵌套路由的常见设置,它反映了我们之前定义的路由树。但是,我们没有使用典型的纯字符串来指定路径模板(例如 orgs/:orgId),而是从 typesafe-routes/angular-router 导入模板函数并使用它来生成路径模板。对于DashboardComponent和OrgsComponent,我们可以简单地调用template及其对应的路径r.dashboard和r.orgs来获取模板。然而,剩下的组件 LocationsComponent 是 OrgsComponent 的子组件,因此需要一个相对路径,该路径不能通过使用 r.orgs.locations 生成,因为这会导致绝对路径 orgs/:orgId/locations/:locationId,而 Angular Router嵌套路由模板时需要相对路径。

要生成相对路径,我们可以使用 _ 链接,它有效地忽略下划线字符之前的所有内容。在这种情况下,我们可以使用 template(r.orgs._.locations) 来生成相对路径。这是一个方便的功能,因为它允许我们在需要渲染绝对路径的场景以及需要相对路径的情况下重用相同的路由树。

此时,我们已经在我们最喜欢的 IDE(例如 Visual Studio Code)中利用了自动完成和拼写错误预防功能。未来的变化将提醒我们路由路径中的任何拼写错误或拼写错误,因为所有类型都可以使用 createRoutes 追溯到初始路由定义。

渲染链接

现在我们已经指定了路线模板,我们要继续进行链接渲染。为此,我们希望创建一个简单的组件,利用渲染函数来渲染这些链接,包括类型序列化和类型检查。下一个示例显示了一个组件,该组件呈现引用我们应用程序中其他组件的锚元素列表。

// app.routes.ts
import { Routes } from "@angular/router";
import { template } from "typesafe-routes/angular-router";

export const routes: Routes = [
  {
    path: template(r.dashboard), // ~> "dashboard"
    component: DashboardComponent,
  },
  {
    path: template(r.orgs), // ~> "orgs/:orgId"
    component: OrgsComponent,
    children: [
      {
        path: template(r.orgs._.locations), // ~> "locations/:locationId"
        component: LocationsComponent,
      },
    ],
  },
];
登录后复制
登录后复制

代码示例从 typesafe-routes/angular-router 导入 render 和 renderPath。 renderPath 渲染路径,而 render 还序列化链接列表的查询参数。我们还导入 r,代理对象,它允许我们访问有关先前定义的路线的信息并定义要渲染的所需路线。

首先,我们使用 renderPath 函数创建dashboardLink 和orgsLink。作为第一个参数,它采用前面提到的代表要渲染的路线的路径的代理对象。第二个参数是一条记录,其参数值与之前在 app.routes.ts 中使用 createRoutes 定义的参数的名称和类型相匹配。返回值是一个字符串,包含属于相应组件的路径。

第三个示例中的渲染函数渲染路径和搜索参数,因此在参数定义中需要路径和查询属性。这里的返回值是一个对象,有path和query两个属性。我们将这两个属性设置为 [routerLink] 和 [queryParams] 属性的值。

解析参数

参数解析是类型安全路由的重要组成部分。在上面的路由定义过程中,我们定义了几个参数并给它们一个类似整数的类型 int。但是,由于参数值来自各种来源(例如 Location 对象),因此它们是基于字符串的。方便的是,typesafe-routes 导出解析这些字符串并将其转换为所需类型的辅助函数。解析基于我们之前创建的代理对象 r,这意味着我们必须告诉库参数属于哪个路由。下一个示例通过显示两种常见的解析场景来演示这一点。

// app.routes.ts
import { createRoutes, int } from "typesafe-routes";

export const r = createRoutes({
  dashboard: {
    path: ["dashboard"], // ~> "/dashboard"
  },
  orgs: {
    path: ["orgs", int("orgId")], // ~> "/orgs/:orgId"
    children: {
      locations: {
        path: ["locations", int("locationId")], // ~> "locations/:locationId"
        query: [int.optional("page")], // ~> "?page=[number]"
      },
    },
  },
});
登录后复制
登录后复制
登录后复制

给定 location.href orgs/1/location/2?page=5,在 Angular 中,我们可以使用 this.route.snapshot.queryParams 访问基于字符串的查询参数,并且通过此提供基于字符串的路径参数。路线.快照.参数。将 parseQuery 与 r.orgs.locations 和 this.route.snapshot.queryParams 结合使用,我们可以检索页面参数为数字的对象。将 parsePath 与 r.orgs._.locations 和 this.route.snapshot.params 一起使用,我们得到解析后的 locationId。在这种情况下,r.orgs._.locations 是相对路径,并且 _ 链接之前的所有段都被省略,导致 orgId 不存在于结果对象中。

typesafe-routes 中的解析函数是通用的,我们还可以使用 parse 直接从 location.href 字符串中一次性提取所有参数。

// app.routes.ts
import { Routes } from "@angular/router";
import { template } from "typesafe-routes/angular-router";

export const routes: Routes = [
  {
    path: template(r.dashboard), // ~> "dashboard"
    component: DashboardComponent,
  },
  {
    path: template(r.orgs), // ~> "orgs/:orgId"
    component: OrgsComponent,
    children: [
      {
        path: template(r.orgs._.locations), // ~> "locations/:locationId"
        component: LocationsComponent,
      },
    ],
  },
];
登录后复制
登录后复制

可以通过 InferQueryParams、InferPathParams 或 InferParams 提取有关参数的类型信息。这是 InferQueryParams 实用程序类型的演示。

// app.component.ts
import { render, renderPath } from "typesafe-routes/angular-router";
import { r } from "./app.routes";

@Component({
  selector: "app-root",
  imports: [RouterOutlet, RouterLink],
  template: `
    <h1>Absolute Links</h1>
    <ul>
      <li><a [routerLink]="dashboardLink">Dashboard</a></li>
      <li><a [routerLink]="orgsLink">Org</a></li>
      <li>
        <a [routerLink]="locationLink.path" [queryParams]="locationLink.query">
          Location
        </a>
      </li>
    </ul>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent {
  dashboardLink = renderPath(r.dashboard, {}); // ~> dashboard
  orgsLink = renderPath(r.orgs, { orgId: 123 }); // ~> orgs/123

  locationLink = render(r.orgs.locations, {
    path: { orgId: 321, locationId: 654 },
    query: { page: 42 },
  }); // ~> { path: "orgs/321/location/654", query: { page: "42" }}
}
// ...
登录后复制

总结

为了结束本教程,我们创建了一个路由树 r,它是我们路由的唯一事实来源。基于此,我们渲染了用于向 Angular Router 注册组件的模板。我们使用动态路径段和查询参数渲染路径。我们解析参数,将它们从字符串值转换为相应的类型。我们以类型安全的方式完成了所有事情,甚至没有编写一个类型定义。我们建立了一个强大的路由树,可以在开发新功能时轻松防止错误,并进一步促进未来的重构。

但是,typesafe-routes 还有更多功能,例如许多不同的内置参数类型、自定义参数类型的轻松集成、子路径的操作、定义自定义模板字符串等等。不幸的是,我们无法在本教程中涵盖所有内容,但您可以通过访问官方文档了解更多信息。

支持项目

当然,还可以对本教程中所示的示例实施许多潜在的改进。例如,用于链接呈现的自定义指令,它采用基于代理对象的路径定义,例如 r.orgs.locations。另一个例子是一个为 Angular Router 自动生成 Routes 数组的函数,有效地消除了重复的代码,并且无需使路由与第一个代码块中使用 createRoutes 创建的路由树保持同步。

但是,这些只是众多贡献方式中的几种。当然,最常见的方式是在我们的 GitHub 存储库中共享、报告错误或打开 PR。如果你使用这个库并认为它改善了你的开发体验,你也可以请我喝杯咖啡。我们还有一个 Discord 频道,您可以在其中留下反馈或提出问题。

以上是使用 Angular 中的类型安全路由消除运行时错误的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1655
14
CakePHP 教程
1414
52
Laravel 教程
1307
25
PHP教程
1255
29
C# 教程
1228
24
神秘的JavaScript:它的作用以及为什么重要 神秘的JavaScript:它的作用以及为什么重要 Apr 09, 2025 am 12:07 AM

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

JavaScript的演变:当前的趋势和未来前景 JavaScript的演变:当前的趋势和未来前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

JavaScript引擎:比较实施 JavaScript引擎:比较实施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript:探索网络语言的多功能性 JavaScript:探索网络语言的多功能性 Apr 11, 2025 am 12:01 AM

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

如何使用Next.js(前端集成)构建多租户SaaS应用程序 如何使用Next.js(前端集成)构建多租户SaaS应用程序 Apr 11, 2025 am 08:22 AM

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

从C/C到JavaScript:所有工作方式 从C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

如何安装JavaScript? 如何安装JavaScript? Apr 05, 2025 am 12:16 AM

JavaScript不需要安装,因为它已内置于现代浏览器中。你只需文本编辑器和浏览器即可开始使用。1)在浏览器环境中,通过标签嵌入HTML文件中运行。2)在Node.js环境中,下载并安装Node.js后,通过命令行运行JavaScript文件。

See all articles