项目重构_角度重构示例
项目重构
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.
代码设计可能是主观的,并且这些示例可能会倾向于个人喜好。 我们将更改设计,但不会更改基础功能。 无论采用哪种方法,无论重构与否,都应有效。
更换组件包装器元素 (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完成了一些工作:使组件成为块级元素,添加鼠标侦听器,以及动态设置边框颜色。
In the snippet below we have completely removed the outer div
making the following changes:
在下面的代码段中,我们完全删除了外部div
并进行了以下更改:
-
div
has been replaced by a:host
css selector that will declaredisplay: block
and accomplish whatdiv.container
was doing beforediv
已由:host
css选择器替换,该选择器将声明display: block
并完成div.container
之前的操作 -
(mouseenter)
event binding has been replaced by adding theHostListener('mouseenter')
decorator to its corresponding function(mouseenter)
事件绑定已被替换,方法是将HostListener('mouseenter')
装饰器添加到其对应的函数中 -
[style.border-color]
property binding has been replaced by adding theHostBinding(style.border-color)
decorator to its corresponding function[style.border-color]
属性绑定已被替换,方法是将HostBinding(style.border-color)
装饰器添加到其对应的函数中
提示:表单元素和显示内容(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()
来计算总和。
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()
来获取当前页码,我们可以使用扫描运算符将当前页码保存为状态,然后在每次发射时递增或递减该状态。
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的最后一个发射/当前值来进行计算。
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]
下filter
和switchMap
中的参数数组。 —在这种情况下, withLatestFrom
的u
值将是我们将从this.activeUser.getValue()
检索到的this.activeUser.getValue()
。
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$
),则应该使用combineLatest
。 withLatestFrom
与withLatestFrom
一起使用,以避免在saveValueToDb$
observable发出时activeUser
影响。
Caveat, our source observable
saveValueToDb$
will not emit until bothformSaved$
andactiveUser
have emitted a least once.请注意,直到
formSaved$
和activeUser
都至少发出一次之后,我们的源可观察到的saveValueToDb$
才会发出。
容器和演示组件???? (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.
我们原始的组件可以完成所有工作。 它注入依赖关系,处理异步逻辑,定义列表中每个项目的外观/格式,并定义事件的逻辑。
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(...)
逻辑。
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
。
翻译自: https://itnext.io/angular-refactoring-examples-1fc16c4e58ff
项目重构