在前端的编程实践中,Angular 是一个不能忽视分支。它的强大功能和丰富的特性,使开发者能够快速地构建复杂的单页面应用。然而与任何强大的工具一样,如果使用不当,也可能导致效率低下,甚至出现严重错误。我以前一直是一个 react 使用者,但是在过去的一年多,因为项目的原因,我开始了解和使用 Angular,在此我想分享一些在使用 Angular 时应该避免的坏实践。无论你是 Angular 的新手,还是有一定经验的开发者,我希望这篇文章都能给你带来新的启示和帮助。让我们一起来看看如何更好地利用 Angular,避免那些可能阻碍我们编程效率的坏习惯吧。
❌ 不要把逻辑处理写在 tap 里
在你的 observable 有许多订阅者的情况下,不要将你要实现的逻辑放在 tap 操作符内部,反而去让订阅者的 subscribe 为空。 如果你把你的逻辑放在那里,就算有的地方不需要它,每个订阅者都会调用 tap 逻辑。这会造成不必要的性能浪费。 但是这其实也不是一个通用的规则,还是要取决于你的用例,如果你真的想让所有的订阅者都有这个逻辑,或者你只有一个订阅者,你想使用 async pipe,那么这个规则就不适用。 在一般的情况之下,我还是强烈推荐使用 subscribe 处理逻辑,不要使用 tap。
❌ 订阅中的错误处理只使用 console.error
不要使用 console.error。对于错误处理,它只记录错误并不抛出错误,而且要知道有些事情失败了,你的 subscriber 就不能继续执行了 解决方案: 使用 Rxjs 提供的错误处理的 Rxjs 操作符来优雅地处理错误和重试逻辑。
javascript
.pipe(
catchError(err => {
console.warn('I want to display this error globally');
return throwError(err);
})
)
.subscribe(data) => {
this.data=data
});
❌ 不要把 component 文件搞到 3-4k 行
这样做的坏处在于
- 很难做重构
- 对其他开发者来说会是灾难
- 破坏了组件设计思想的规则
解决方案就是始终考虑到可重复使用的组件和关注点的分离 父/子组件
❌ 不要在模板中大量的逻辑内联代码
不要在模板内调用函数,因为这会:
- 影响你的应用程序的性能
- 难以在以后进行重构
坏的例子:
javascript
@Component({
template: ` <p>Welcome {{ fullName() }}!</p> `,
})
export class PersonComponent {
@Input() person: { firstName: string, lastName: string };
constructor() {}
fullName() {
return this.person.firstName + " " + this.person.lastName;
}
}
解决办法是对你的数据进行手动计算或组合,或使用管道(pipe),这将增加代码的可读性和可重复使用性。
javascript
@Component({
template: `
<p>Welcome {{ name | nameToString }}!</p>
`
})
export class PersonComponent {
@Input() person: { firstName: string, lastName: string };
constructor() { }
}
// use Pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'nameToString'
})
export class IdPathPipe implements PipeTransform {
transform(value): string {
return value.firstName = value.lastName
❌ 不要用 Promise 去代替 observable
如果你在 HTTP 请求处理或其他方面使用 Promise,你会有这些限制:
- 你不能轻易取消一个 Promise。
- 你不能轻易地重复或重试一个 Promise
❌ 不要在订阅方法中映射或转换数据
我们不建议在 subscribe 块中处理你的数据。 为什么?想象一下,当你有很多订阅者需要相同的转换时,就会出现很多代码冗余的情况 而订阅是一个同步调用,你会被阻断,直到转换完成。
javascript
this.http.get("https://swapi.dev/api/people/1")
.subscribe(luke => {{
name: response.name,
birthYear: response.birth_year,
height: Number(response.height),
weight: Number(response.mass),
eyeColor: response.eye_color
}
解决方案: 你应该使用 Rxjs 的 map 操作符来预处理你的数据,因为许多 subscriber 可以得到你的结果,而且你会获得一些代码逻辑(可重用性)。这可以提高性能。
javascript
this.http
.get("https://swapi.dev/api/people/1")
.pipe(
map((response) => ({
name: response.name,
birthYear: response.birth_year,
height: Number(response.height),
weight: Number(response.mass),
eyeColor: response.eye_color,
}))
)
.subscribe((luke) => console.log(luke));
❌ 不要把所有东西都塞到 SharedModule
SharedMoudle 是一种推荐的 Angular 架构模式,用来组织常用的管道、指令和组件。 但是到时候,当应用规模增长时,你会发现自己在组件中导入了不需要的依赖,这将影响你的项目捆绑规模
✅ 为可观察的变量类型添加$后缀
后缀$是用来表示某个变量是可观察的。它有助于区分正常变量和可观察变量。
✅ 在一个单独的文件中定义常量
建议定义全局常量,这些常量将在多个组件之间普遍使用。 当你想在整个应用程序中使用共同的行为时,就会有帮助。
javascript
export class DataTableConstants {
public static ItemPerPage: number = 50;
public static PageSize: number[] = [10, 50, 100, 200, 500];
public static AllowFiltering: boolean = true;
}
你可以在一个单独的组件中使用这些常量文件:
javascript
import { Component, OnInit } from '@angular/core';
import { DataTableConstants } from './globals/datatable-constants';
@Component({
selector: 'app-example',
templateUrl: './app.example.html',
styleUrls: ['./app.example.css']
})
export class AppExampleComponent implements OnInit{
itemPerPage = DataTableConstants.ItemPerPage;
pageSize = DataTableConstants.PageSize;
allowFiltering = DataTableConstants.AllowFiltering;
ngOnInit() {
console.log(this.itemPerPage);
console.log(this.pageSize);
console.log(this.allowFiltering);
}
}
view raw