项目重构_角度重构示例

项目重构

Source Code: StackBlitz 🚀

源代码: StackBlitz🚀

Article Goal: Learn about common patterns we can potentially refactor in our Angular applications

文章目标:了解我们可以在Angular应用程序中重构的常见模式

Article Topics (What We’ll Learn)

文章主题(我们将学习的内容)

  • Replacing component “wrappers” with :host/HostBinding/HostListener

    :host / HostBinding / HostListener替换组件“包装器”

  • Replacing BehaviorSubject.getValue() usages with more declarative techniques.

    用更具说明性的技术替换BehaviorSubject.getValue()用法。

  • Replacing a Component that does everything into Container & Presentational Components

    替换将所有功能都包含在容器演示组件中的组件

Refactoring is a technique for improving the design of existing code while preserving it’s behavior. This article focuses on concrete examples of refactoring and not so much how to refactor conceptually.

重构是一种在保留现有代码行为的同时改进其设计的技术。 本文重点关注重构的具体示例,而不是如何从概念上进行重构。

Code design can be subjective, and these examples can lean towards personal preference. We will change the design but not the underlying functionality. Either approach, refactored or not, should be valid.

代码设计可能是主观的,并且这些示例可能会倾向于个人喜好。 我们将更改设计,但不会更改基础功能。 无论采用哪种方法,无论重构与否,都应有效。

Same function, different look 😀
功能相同,外观different

更换组件包装器元素 (Replacing Component Wrapper Elements)

A common situation we may see in Angular apps is wrapping all of a component’s elements in a single “wrapper” element.

我们可能在Angular应用程序中看到的一种常见情况是将组件的所有元素都包装在一个“包装”元素中。

Here, we are focused on refactoring the outer div wrapper in the template. This div is accomplishing a few things: Making the component a block-level element, adding a mouse listener, and dynamically setting border-color.

在这里,我们专注于重构模板中的外部div包装器。 该div完成了一些工作:使组件成为块级元素,添加鼠标侦听器,以及动态设置边框颜色。

Image for post
source 资源

In the snippet below we have completely removed the outer divmaking the following changes:

在下面的代码段中,我们完全删除了外部div并进行了以下更改:

  • div has been replaced by a :host css selector that will declare display: block and accomplish what div.container was doing before

    div已由:host css选择器替换,该选择器将声明display: block并完成div.container之前的操作

  • (mouseenter) event binding has been replaced by adding the HostListener('mouseenter') decorator to its corresponding function

    (mouseenter)事件绑定已被替换,方法是将HostListener('mouseenter')装饰器添加到其对应的函数中

  • [style.border-color] property binding has been replaced by adding the HostBinding(style.border-color) decorator to its corresponding function

    [style.border-color]属性绑定已被替换,方法是将HostBinding(style.border-color)装饰器添加到其对应的函数中

Image for post
source 资源

提示:表单元素和显示内容(Tip: Form Element & Display Contents)

If a form element is the first and only child of your component (wrapper). Set the form element to display: contents to treat the form’s children as if they are the host’s children. Warning: Experimental 🧪

如果表单元素是组件(包装器)的第一个也是唯一的子元素。 将form元素设置为display: contents以将表单的子级视为宿主的子级。 警告:实验🧪

删除BehaviorSubject.getValue()的用法 (Removing Usages of BehaviorSubject.getValue())

Angular utilizes and promotes the use of the library RxJS; which follows a declarative programming paradigm.

Angular利用并促进了RxJS库的使用; 遵循声明式编程范例。

As a developer who spent all of their time writing imperative Java code before working with Angular, BehaviorSubject.getValue() was a quick answer for when I didn’t know how to achieve something in a declarative/RxJS way.

作为一个开发人员,在与Angular一起工作之前花了所有时间编写命令式Java代码,当我不知道如何以声明式/ RxJS方式实现某些目标时, BehaviorSubject.getValue()是一个快速的答案。

Here are some ways we could lean into the declarative paradigm and replace usages of BehaviorSubject.getValue().

我们可以通过以下几种方法来探究声明式范例并替换BehaviorSubject.getValue()用法。

scan —将值保存并检索为可观察状态 (scan — Save & Retrieve Value as Observable State)

Current Page Number Example 📄

当前页码示例📄

Keeping track of a count, like a current page number in a paginator. We want to increment or decrement the count when the user changes the page. Notice the usage of currentPageNumber.getValue() to calculate the sum.

跟踪计数,例如分页器中的当前页码。 我们想在用户更改页面时增加或减少计数。 注意使用currentPageNumber.getValue()来计算总和。

Image for post
source 资源

Instead of using .getValue() to get the current page number every time, we can use the scan operator to save the current page number as state, and then increment or decrement that state on each emission.

不用.getValue()使用.getValue()来获取当前页码,我们可以使用扫描运算符将当前页码保存为状态,然后在每次发射时递增或递减该状态。

Image for post
source 资源

In this context, acc, is our accumulated value. Really, acc is the value returned from scan during the previous emission (Or what would have been the value from getValue()). curr is the value from the current emission/next().

在这种情况下, acc是我们的累积价值。 实际上, acc是前一次发射期间从scan返回的值(或者应该是getValue()的值)。 curr是当前的mission / next()中的值。

withLatestFrom&CombineLatests —在排放期间获取价值 (withLatestFrom & combineLatests — Get Value During Emission)

Saving Active-User Data Example ️🤸‍♀️

保存活动用户数据示例️🤸‍♀️

Sometimes we have side-effects during Observable A’s emission, and we need the last-emission/current-value from Observable B to make the calculation.

有时我们在可观测A的发射过程中会产生副作用,因此我们需要可观测B的最后一个发射/当前值来进行计算。

Image for post

withLatestFrom will get the latest value from our observable, and add it to the next operator. Notice the array of parameters in filter and switchMap below [v, u]. — In this context, the value of u from withLatestFrom will be what we would have retrieved from this.activeUser.getValue().

withLatestFrom将从我们的可观察值中获取最新值,并将其添加到下一个运算符。 注意[v, u]filterswitchMap中的参数数组。 —在这种情况下, withLatestFromu值将是我们将从this.activeUser.getValue()检索到的this.activeUser.getValue()

Image for post

If we want our observable (saveValueToDb$) to emit when our activeUser changes/emits, we should use combineLatest. Else use withLatestFrom to avoid activeUser effecting when the saveValueToDb$ observable emits.

如果我们希望当我们的activeUser更改/发出时发出我们的observable( saveValueToDb$ ),则应该使用combineLatestwithLatestFromwithLatestFrom一起使用,以避免在saveValueToDb$ observable发出时activeUser影响。

Caveat, our source observable saveValueToDb$ will not emit until both formSaved$ and activeUser have emitted a least once.

请注意,直到formSaved$activeUser都至少发出一次之后,我们的源可观察到的saveValueToDb$才会发出。

Image for post

容器和演示组件📦 (Container and Presentational Components 📦)

Sometimes it can be beneficial to breakout a component(s) into container components (concerned with how things work) and presentational components (concerned with how things look). — Here is an example of making this distinction in an Angular app:

有时,将组件分解为容器组件(与事物的工作方式有关)表示性组件(与事物的外观有关)可能是有益的。 —这是在Angular应用程序中进行区分的示例:

Our original component does everything. It injects dependencies, handles asynchronous logic, defines how each item in the list looks/formatted, and defines logic for events.

我们原始的组件可以完成所有工作。 它注入依赖关系,处理异步逻辑,定义列表中每个项目的外观/格式,并定义事件的逻辑。

Image for post
source 资源

Our new container component will handle: injecting dependencies / data sources constructor(private ps: InputRefactorService), asynchronous logic people$ | async, and logic for events this.ps.deleteEvent.next(...).

我们的新容器组件将处理:注入依赖项/数据源constructor(private ps: InputRefactorService) ,异步逻辑people$ | async people$ | async ,以及事件this.ps.deleteEvent.next(...)逻辑。

Image for post
source 资源

Our presentational component accepts data from the container as Input(s) and delegates logic to the container with Output(s). It doesn’t know about any dependencies, whether person is asynchronous/synchronous, or what logic is executed on remove.

我们的演示性组件接受来自容器的数据作为输入,并使用输出将逻辑委托给容器。 它不知道任何依赖关系,无论人员是异步/同步,还是在remove上执行什么逻辑。

I don’t think this has to be strict. We can usually defer to what makes the most sense for the use case.

我认为这不必严格。 通常,我们可以根据用例选择最有意义的方法。

As a bonus, presentational components like this make it easier to set ChangeDetectionStrategy.OnPush with confidence.

另外,像这样的演示组件可以更轻松地设置ChangeDetectionStrategy.OnPush

Image for post
source 资源

翻译自: https://itnext.io/angular-refactoring-examples-1fc16c4e58ff

项目重构

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值