markdown 角度结构和最佳实践
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 角度结构和最佳实践相关的知识,希望对你有一定的参考价值。
# Angular Project Structure and Best Practices
[SOURCE](https://medium.com/@michelestieven/organizing-angular-applications-f0510761d65a), [SOURCE](https://stackoverflow.com/a/46622924/1602807), [SOURCE](https://medium.com/@tomastrajan/6-best-practices-pro-tips-for-angular-cli-better-developer-experience-7b328bc9db81)
## Feature Modules
In Angular, every module which is not the AppModule is technically a Feature Module, and it has the following caveats:
- It must declare all the components, directives and pipe it needs.
- It must import `CommonModule` instead of `BrowserModule`:
While `BrowserModule` must be imported in `AppModule` (it’s required in order to run the app in the browser), this module must not be imported elsewhere: instead, we must import `CommonModule`, which contains Angular’s common directives, such as `ngIf`, `ngFor`, `ngClass`, etc… `BrowserModule` also re-exports `CommonModule`, so that you can use this directives in AppModule too.
- It doesn’t bootstrap anything:
The only module responsible for bootstrapping a component is, obviously, `AppModule`
- We use Feature Modules to define one unique feature of our app which could be a collection of screens. In general, **each module should have its own routing**. By organizing this way , there’s no need for the parent module to import your child module’s components to put them in the routing configuration. For example, from `localhost/contacts/` on, the `ContactsModule` will be responsible for its routes.
In addition to that, by having its own routing, modules can be lazily loaded ([Gist](https://gist.github.com/vxhviet/ebac8f6dbe820d859b3d63b67bab3d68), [Cacher](https://snippets.cacher.io/snippet/ad2d6b985d84139d34bc))
- **Advance**: see the [SOURCE](https://medium.com/@michelestieven/organizing-angular-applications-f0510761d65a) to see how we can use the `loadChildren` keyword without actually Lazy Loading the module and define a preloading strategy for lazily loaded modules:
Example of a feature module:
```typescript
import { ProductComponent } from "./product.component";
import { BestProductComponent } from "./best-product/best-product.component";
import { ProductListComponent } from "./product-list/product-list.component";
import { ProductDetailComponent } from "./product-detail/product-detail.component";
import { SharedModule } from "@shared/shared.module";
const ProductRoutes: Routes = [
{
path: "",
component: ProductListComponent,
pathMatch: "full"
},
{
path: "all-products",
component: ProductListComponent
},
{
path: "product/:id",
component: ProductDetailComponent
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(ProductRoutes),
SharedModule,
],
declarations: [
ProductComponent,
BestProductComponent,
ProductListComponent,
ProductDetailComponent,
],
exports: [BestProductComponent]
})
export class ProductModule {}
```
**Summary:**
- Feature modules should only import services from `CoreModule`. If feature module A needs to import service from feature module B consider moving that service into core.
- Rule of thumb is to try to create features which **don’t depend on any other features** just on services provided by `CoreModule` and components provided by `SharedModule`.
- We should lazy load our feature modules whenever possible. Theoretically only one feature module should be loaded synchronously during the app startup to show initial content. Every other feature module should be loaded lazily after user triggered navigation.
- **Extra:** Ideally feature module will only need have access to services from the `CoreModule` and components from `SharedModule`. Sometimes this might not be enough to solve particular business case and we will also need some kind of *shared feature module* which is providing functionality for a limited subset of other feature modules. Like this:
![image](https://cdn-images-1.medium.com/max/1600/1*6iBDGXV1ONrIMXm664kKHg.png)
## Core Module
The answer to the question “Where should I put all my global services?” would be: `AppModule`. This is because services are **(in general) app-scoped, which means that they can be accessed from every module**.
As briefly described in the Lazy Loading article ([Gist](https://gist.github.com/vxhviet/ebac8f6dbe820d859b3d63b67bab3d68), [Cacher](https://snippets.cacher.io/snippet/ad2d6b985d84139d34bc)), **every lazy module has its own injector**! What that means is that a service provided in a lazy module is only accessible in that module. But it can still access the services previously provided by non-lazy modules (such as `AppModule`)!
So technically, global singleton services such as `AuthService` or `UserService` in `AppModule`, since they’ll be available to everyone. However, we really don’t want our `AppModule` to be a complete mess… What Angular recommends is to put all of our global services in a separated module, called `CoreModule`, and import it **ONLY** in `AppModule`. This way is the same as providing the services in AppModule directly!
In order to prevent inexperienced developers from re importing the `CoreModule` we use this little trick: inside our `CoreModule`, we can… **inject CoreModule**! If Angular injects it correctly, it means that a `CoreModule` has been already created, and we can throw an error:
```
@NgModule({
providers: [
// Your services
]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() core: CoreModule) {
if (core) {
throw new Error('CoreModule should only be imported once in App Module');
}
}
}
```
**Summary:**
- Consider making `CoreModule` a **pure services module with no declarations** (no components, pipes, etc)
- Should be imported only **ONCE** in AppModule.
## Shared Modules
A shared module is the perfect place to declare components, pipes, directives in order to make them reusable: this way, you won’t re-import the same components in every module, you’ll just import the shared module.
Example shared module:
```typescript
@NgModule({
imports: [
CommonModule,
FormsModule,
HttpClientModule,
RouterModule,
],
declarations: [
NoProductsFoundComponent,
PriceFormatPipe,
ProductCellComponent,
ProductCarouselComponent
],
exports: [
NoProductsFoundComponent,
PriceFormatPipe,
ProductCellComponent,
ProductCarouselComponent
],
providers: [
// services should not be declared here
]
})
export class SharedModule {
// this should be use in AppModule only.
// for lazy loaded modules, we should import SharedModule normally.
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [
// If you have a very good reason to have services in shared module,
// use this to declare your services. Otherwise, consider moving the service
// to Core Module.
// For more explanation, See: https://stackoverflow.com/a/46622924/1602807
]
};
}
}
```
Still remember that:
- Lazy modules have their own injector which means if they import a module which provides some services, they’ll create their own instances of those services.
- Services and components have different scopes.
So how can we import only the component, directive, pipe part of shared module into lazy modules and import the service part to `AppModule`?
Angular gives us a special interface we can use to **attach services to modules**, it’s called `ModuleWithProviders`, here it is:
```
export interface ModuleWithProviders {
ngModule: Type<any>;
providers?: Provider[];
}
```
So we don’t provide our services in the `SharedModule` metadata; instead, we define a static method inside the module, which returns the `SharedModule` istelf **AND** the array of providers just like in the example!
Now in the `AppModule` you can import `SharedModule.forRoot()`, while in all the other modules you can import `SharedModule` (this is exactly how `RouterModule` works).
This trick is for the case you absolutely need to have a mixed shared module. To make matters simpler, just try to keep Shared Module free from services.
**Summary:**
- All the *dumb* components, directives and pipes should be implemented here.
- These components **don’t import and inject services from core or other feature modules** in their constructors.
- They should **receive all data though attributes in the template** of the component using them (using `@Input()` and `@Output()` decorator).
- This all sums up to the fact that `SharedModule` doesn’t have any dependency to the rest of our application.
- It is also the perfect place to import and re-export Angular Material components.
- You should import this `SharedModule` into the specific Feature Modules as needed. You **DO NOT** import the `SharedModule` into your main `AppModule` or `CoreModule`.
## Note for App wide components:
For app wide components such as Header and Footer, we can consider putting them in AppModule. If we want to clean up AppModule even more, we can put those in CoreModule.
Only app wide components should be put in AppModule or CoreModule.
For the first page, Index or Home, we should put it in AppModule.
以上是关于markdown 角度结构和最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
markdown 用Python开发的“最佳实践最佳”(BOBP)指南。
markdown 用Python开发的“最佳实践最佳”(BOBP)指南。
markdown 用Python开发的“最佳实践最佳”(BOBP)指南。
markdown 用Python开发的“最佳实践最佳”(BOBP)指南。