React 开发者,学习 Angular
在 Angular 中创建组件
假设我们要创建一个CustomButton组件。当我们在 React 中创建这个组件时,它看起来类似于:
//imports
export const CustomButton = () => {
const onClick= () => {}
return (
<button
onClick={onClick}
className=”button-class”>
Click me
</button>
)
};
我们有组件的名称、它的标记和它可以在 JSX 中处理的逻辑——如果我们使用的是 typescript 扩展文件,则为 TSX。
在 Angular 中,要创建完全相同的组件,我们可以使用以下命令:
ng generate component custom-button
上面的命令将创建一个文件夹,其中包含三个文件: ustom-button.component.html、custom-button.component.scss和custom-button.component.ts。Angular 如何处理这三个文件以将它们作为一个组件使用?该组件的配置在.component.ts文件中。它有@Component装饰器告诉 Angular 如何处理和实例化组件以及在哪里找到每一层信息。这与 Angular 中的示例相同:
//custom-button.component.html
<button
class="button-class"
(click)="onClick()">
Click me
</button>
//custom-button.component.ts
//imports
@Component({
selector: 'custom-button',
templateUrl: './custom-button.component.html',
styleUrls: ['custom-button.component.scss]
})
export class CustomButton {
onClick = () => {}
}
如您所见,它们看起来非常相似,但由于 React 使用 JSX 而 Angular 使用 HTML 作为标记,因此存在一些明显的语法差异:
- 在 Angular 中,类由属性类设置。
- Angular 组件在浏览器检查器中可见。在这个例子中,我们会看到类似的东西:
<custom-button class=”button-class”><button>Click me</button><custom-button>
- 请注意,Angular 在代表组件本身的标记中创建了一个附加层。在 React 中,你会直接看到按钮。
向组件添加属性
在 React 中,我们使用 props 使组件可定制和动态。例如,我们可能会传递property text=“Click me”使按钮更通用:
export const CustomButton = ({text}) => {
const onClick= () => {}
return (
<button ...>
{text}
</button>
)
};
在 Angular 中,我们需要@Input()在 .ts 文件中使用装饰器并初始化它:
@Component({
selector: 'custom-button',
templateUrl: './custom-button.component.html',
styleUrls: ['custom-button.component.scss]
})
export class CustomButton {
@Input() text = ‘’ //Note that it is mandatory to initialise it either here or in the class constructor
onClick = () => {}
}
并在 HTML 中使用双括号:
<button
class="button-class"
(click)="onClick()">
{{ text }}
</button>
调用父组件函数
当使用纯 UI 组件时,我们可能希望将一个函数从父组件传递给子组件。在 React 中,它在子组件中看起来像这样:
export const CustomButton = ({onClick}) => {
handleClick = () => {
//some internal logic
onClick();//function passed from the parent component
}
return (
<button
onClick={handleClick}
className=”button-class”>
Click me
</button>
)
};
在 Angular 中,我们会使用@Output()装饰器:
@Component({
selector: 'custom-button',
templateUrl: './custom-button.component.html',
styleUrls: ['custom-button.component.scss]
})
export class CustomButton {
@Output() onClick: new EventEmitter<any>();
handleClick = () => {
//some logic
this.onClick.emit()//here we can also pass the event or other data that we want to receive in the parent component
}
}
创建一个 NgModule
我们现在对如何创建 Angular 组件有了一些了解,但是它在 Angular 生态系统中的什么位置?当我们在 Angular 中创建组件时,需要在 NgModule 中声明它。NgModule 是一个代码块,它有自己的配置、路由和@NgModule装饰器指定的依赖项,可以通过路由延迟加载。当您来自 React 时,乍一听这可能有点棘手,但让我们通过一个例子来看一下。当我们启动一个 Angular 项目时,它会附带一个名为的模块app.module.ts,其中包含以下基础知识:
// imports
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
这是最基本的 NgModule:
- declarations 数组指示它将包含哪些表示组件,例如,如果我们要在此模块中加载自定义按钮,我们将在此处包含它。
- 导入:加载当前模块所需的模块。在内部,Angular 将模块的服务提供者列表添加到应用程序根注入器中。在这种情况下,BrowserModule具有特定于浏览器的服务,例如 DOM 呈现、清理和位置。
- Providers:当前模块拥有的服务依赖列表。例如,这可以是加载数据的服务或处理身份验证的服务。
- 导出:此处未显示,但它是一个数组,您可以在其中导出当前模块的组件,以便能够在其他模块中使用它们。
- Bootstrap:Angular 将插入的根组件index.html。
为什么我们需要这些模块?
应用程序总是被划分为可维护性的功能块。在 React 中,这个划分由开发人员决定,没有特定的约定可循;但是,在 Angular 中,代码可以分为 NgModules。装饰器指定了这部分代码的@NgModule配置,包括它的外部依赖,它的内部依赖应该如何注入,它应该如何编译。这会创建具有自己的 UI 组件和路由的内聚代码块。
并非所有模块都将成为按路线显示的展示组件。非演示组件内置模块的一个示例是RouterModuleAngular 提供的一个经常使用的模块,用于定义现有路由以及它们应该加载哪些组件。它允许您跟踪当前页面是否对应于特定路由 ( RouterActiveLink) 或 URL 是否需要某些条件才能访问 ( CanActivate)。
Angular 团队也在致力于简化此配置,最终消除对 NgModule 的需求,并通过路由进行此配置,我们将在下一节中讨论。
减少 NgModules 需求的新 API
为了改进 tree-shaking 并降低复杂性,Angular 正在致力于创建新的 API,其中:
- 依赖注入将直接使用 providers 和 providers 数组处理。RouterModule.forRoot(routes, config)与其在 NgModule中配置 Angular 路由器,不如在路由器中配置。
- Bootstrapping——当前在 NgModule 中配置——将由一个新bootstrapApplication函数处理。
- importProvidersFrom功能将允许与以前的 NgModules 兼容。
- 延迟加载组件不仅适用于 NgModules,而且适用于单个组件和子路由。
- 每个路由的依赖关系可以单独指定,而不是按模块指定。