MVC
在后端开发中,MVC(Model-View-Controller)架构是一种经典的设计模式。它将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller),每个部分都有其特定的职责。
- 模型(Model) :负责与数据库交互,处理数据的存储、检索和更新。
- 视图(View) :负责呈现数据,通常是用户界面。
- 控制器(Controller) :负责接收用户输入,调用模型处理数据,并将结果传递给视图。
MVC架构的主要优点在于它将应用程序的不同部分分离,使代码更具模块化和可维护性。然而,随着应用程序的复杂性增加,单纯的MVC架构可能不足以应对所有需求。此时,引入一些更高级的设计模式和框架是必要的。
NestJS不仅支持MVC架构,还引入了许多现代开发理念,如依赖注入(Dependency Injection)、面向切面编程(AOP)等,使开发者能够更高效地管理和组织代码。
Controller、Provider和Module
NestJS中的目录结构如下:
在NestJS中,控制器(Controller)负责处理传入的请求并返回响应,Provider负责处理业务逻辑和数据操作,模块(Module)用于组织和管理应用程序的各个部分。
示例代码
// cats.controller.ts
import { Controller, Get } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Get()
findAll(): string[] {
return this.catsService.findAll();
}
}
// cats.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
findAll(): string[] {
return ['Tom', 'Jerry'];
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
IOC/DI
NestJS最出名的就是IOC(Inverse of Control)机制了,就是不需要手动创建实例,框架会自动扫描需要加载的类,并创建它们的实例放到容器里,实例化时还会根据该类的构造器参数自动注入依赖。
关于IOC和DI可以参考:了不起的IOC
IOC翻译成中文是控制反转,控制的是实例化过程。反转是将实例化交给 IoC 容器,由 IoC 容器 控制对象的创建。
如上边的代码,其中 CatsController 依赖 CatsService 中的方法。当代码运行的时候会生成一个IOC容器,IOC容器负责实例化,当要实例化 CatsController 时,会分析出 CatsController 依赖了 CatsService 然后实例化 CatsService 实例,并将这个实例注入到 new CatsController() 。
一些前置知识
装饰器
装饰器(Decorator)用来增强 JavaScript 类(class)的功能。装饰器是一种在代码运行时动态添加功能的方式,它通常用于修改类或函数的行为。
在TypeScript中启用装饰器需要将experimentalDecorators 设置为 true。
装饰器使用 @expression 的形式,其中 expression 必须是一个可以返回函数的 expression,该函数在运行时会被调用,并传递有关被装饰声明的信息。
装饰器本质上是一个函数,用于修改类或类成员的行为。装饰器可以应用于类、类的方法、访问器、属性和参数。
普通装饰器,代码如下:
function color(target: Function) {
target.prototype.color = 'red';
}
@color
class D {
color: string;
}
const d = new D();
console.log(d.color); // 'red'
装饰器工厂的形式可以增加参数,代码更加灵活:
function color(col: string) {
return function (target: Function) {
target.prototype.color = col;
};
}
@color('red')
class D {
color: string;
}
const d = new D();
console.log(d.color); // 'red'
装饰器的类型
- 类装饰器:用于修饰整个类。
- 方法装饰器:用于修饰类的方法。
- 访问器装饰器:用于修饰类的 getter 或 setter 方法。
- 属性装饰器:用于修饰类的属性。
- 参数装饰器:用于修饰方法的参数。
关于装饰器参考:装饰器
Reflect Metadata
Reflect API 提供了一些操作对象的属性、方法、构造器的API,如 Reflect.get(obj, 'a')、Reflect.has(obj, 'a')、Reflect.apply、Reflect.construct。这些API已经是ES标准了,而metadata API还在草案阶段,需要引入 reflect-metadata 包。
import 'reflect-metadata';
const car = {
brand: 'BMW',
price: 10,
};
// 给对象添加元数据
Reflect.defineMetadata('desc', 'cheap', car);
// 给对象中的属性添加元数据
Reflect.defineMetadata('desc', 'cheap', car, 'price');
// 检查是否存在元数据
Reflect.hasMetadata('desc', car);
// 检查是否存在元数据
Reflect.hasMetadata('desc', car, 'price');
// 读取元数据
Reflect.getMetadata('desc', car);
// 读取元数据
Reflect.getMetadata('desc', car, 'price');
Reflect Metadata 的主要 API:
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?):定义元数据。Reflect.getMetadata(metadataKey, target, propertyKey?):获取元数据。Reflect.hasMetadata(metadataKey, target, propertyKey?):检查是否存在元数据。Reflect.deleteMetadata(metadataKey, target, propertyKey?):删除元数据。Reflect.getOwnMetadata(metadataKey, target, propertyKey?):获取自有元数据(不包括继承的元数据)。Reflect.getMetadataKeys(target, propertyKey?):获取所有元数据键。Reflect.getOwnMetadataKeys(target, propertyKey?):获取自有元数据键。
它也支持装饰器的方式使用:
@Reflect.metadata('key', 111)
class C {
@Reflect.metadata('metadataKey', 'metadataValue')
method() {}
}
Reflect.metadata 装饰器用装饰器工厂再封装一层:
function Type(type) {
return Reflect.metadata('design:type', type);
}
function ParamTypes(...types) {
return Reflect.metadata('design:paramtypes', types);
}
function ReturnType(type) {
return Reflect.metadata('design:returntype', type);
}
// 为class添加类型元数据
@ParamTypes(String, Number)
class User {
constructor(text, i) {}
@Type(String)
get name() {
return 'text';
}
@Type(Function)
@ParamTypes(Number, Number)
@ReturnType(Number)
add(x, y) {
return x + y;
}
}
然后我们就可以通过 Reflect metadata 的API获取这个类和对象的元数据了:
let obj = new User('a', 1);
let paramTypes = Reflect.getMetadata('design:paramtypes', obj, 'add');
obj.add();
// 获取add 方法的参数类型 [Number, Number]
Nest的实现原理就是使用 Reflect Metadata API 通过装饰器给 Class 添加一些元数据,然后初始化的时候取出这些元数据,进行依赖的分析,然后创建对应的实例对象。
但是,我们在代码中并没有添加 MetaData,这是依靠强大的 TypeScript 。TypeScript 有一个编译选项叫做 emitDecoratorMetadata,开启它编译时自动添加一些 metadata 数据。我们开启 emitDecoratorMetadata,定义一个 Class。
// TypeScript代码
class Example {
constructor(public name: string) {}
}
编译后的 JavaScript 代码如下:
// 编译后的JavaScript代码
var Example = /** @class */ (function () {
function Example(name) {
this.name = name;
}
Example = __decorate(
[
__param(0, String),
__metadata('design:paramtypes', [String]),
],
Example
);
return Example;
})();
TypeScript 自动注入了一些 metadata,分别是:属性类,参数类型,返回值类型。NestJS通过利用装饰器和 Reflect Metadata API,从而在运行时能够自动解析依赖关系并注入所需的依赖。
基本概念
Controllers
Controller 是用于处理传入的 HTTP 请求并返回响应的类。它们是 NestJS 框架的核心部分之一,负责处理路由、请求和响应。
Providers
在 NestJS 中,Provider 是用于处理业务逻辑的核心概念。它们通过依赖注入(Dependency Injection)机制在整个应用中提供共享的功能。
Modules
在 NestJS 中,Module 是用于组织应用程序结构的基本单位。每个 Module 封装了一组相关的功能,并且可以导入和导出其他 Module。
AOP
Nest 还提供了 AOP(Aspect Oriented Programming)的能力,也就是面向切面编程的能力。面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。这有助于增强模块化,使得代码更易于理解、维护和扩展。
NestJS是一个基于Node.js的现代、高性能的框架,它采用了多种编程范式,包括AOP。在NestJS中,AOP主要通过以下几个概念实现:
中间件(Middleware)
类似于 Express 或 Koa 中的中间件,中间件函数可以访问请求对象(req)、响应对象(res)以及应用的 next 函数,next 函数是一个当被调用时将控制权交给下一个中间件的函数。
创建中间件
在 NestJS 中创建中间件有两种方式:使用类和使用函数。
- 使用类创建中间件
使用类创建中间件时,需要实现 NestMiddleware 接口,并定义 use 方法。
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}
}
- 使用函数创建中间件
也可以直接使用函数来创建中间件。
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}
如何使用中间件
中间件可以在模块中配置,以应用于特定的路由、控制器,或者全局范围内。
- 在模块中配置中间件
要在模块中配置中间件,需要实现 NestModule 接口,并定义 configure 方法。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { LoggerMiddleware } from './logger.middleware';
@Module({
controllers: [CatsController],
})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes(CatsController);
}
}
- 应用于特定路由
可以将中间件应用于特定的路由。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { LoggerMiddleware } from './logger.middleware';
@Module({
controllers: [CatsController],
})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('cats');
}
}
- 全局范围内使用中间件
要在全局范围内使用中间件,可以在主模块中配置。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}
NestJS中的守卫(Guard)
守卫(Guard)是 NestJS 框架中用于处理权限控制的一个重要概念。守卫是一种用于确定当前请求是否有权限继续执行的函数。它们在路由处理程序之前执行,可以对请求进行验证和授权操作。守卫在 NestJS 中有着广泛的应用场景,如身份验证、角色授权等。
如何定义守卫
在 NestJS 中创建守卫需要实现 CanActivate 接口,并定义 canActivate 方法。
下面是一个简单的身份验证守卫示例:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
validateRequest(request: any): boolean {
// 在这里添加你的验证逻辑
return true; // 如果验证通过,返回true,否则返回false
}
}
如何使用守卫
守卫可以在方法范围内、控制器范围内或全局范围内使用。
- 在方法范围内使用守卫
要在特定方法上使用守卫,可以使用 @UseGuards 装饰器。
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
export class CatsController {
@Get()
@UseGuards(AuthGuard)
findAll() {
return 'This action returns all cats';
}
}
- 在控制器范围内使用守卫
要在整个控制器上使用守卫,可以将 @UseGuards 装饰器应用于控制器类。
import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
- 在全局范围内使用守卫
要在全局范围内使用守卫,可以在主模块中配置。
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}
守卫的应用场景
- 身份验证:检查请求是否包含有效的身份验证信息,如JWT令牌。
- 角色授权:检查用户是否具有执行特定操作的权限。
- 访问控制:限制特定IP或用户组的访问权限。
NestJS中的管道(Pipe)
管道(Pipe)是 NestJS 框架中用于转换和验证数据的一个重要概念。管道在请求到达控制器处理程序之前执行,可以对请求数据进行转换、验证和过滤操作。管道在 NestJS 中有着广泛的应用场景,如数据验证、数据转换等。
如何定义管道
在 NestJS 中创建管道需要实现 PipeTransform 接口,并定义 transform 方法。
下面是一个简单的数据验证管道示例:
import { Injectable, PipeTransform, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (!this.isValid(value)) {
throw new BadRequestException('Validation failed');
}
return value;
}
isValid(value: any): boolean {
// 在这里添加你的验证逻辑
return true; // 如果验证通过,返回true,否则返回false
}
}
如何使用管道
管道可以在方法参数、控制器范围内或全局范围内使用。
- 在方法参数上使用管道
要在特定方法参数上使用管道,可以使用 @UsePipes 装饰器或直接在参数装饰器中使用管道。
import { Controller, Get, Query, UsePipes } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';
@Controller('cats')
export class CatsController {
@Get()
@UsePipes(ValidationPipe)
findAll(@Query('age') age: number) {
return `This action returns all cats of age ${age}`;
}
}
或者直接在参数装饰器中使用管道:
import { Controller, Get, Query } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Query('age', ValidationPipe) age: number) {
return `This action returns all cats of age ${age}`;
}
}
- 在控制器范围内使用管道
要在整个控制器上使用管道,可以将 @UsePipes 装饰器应用于控制器类。
import { Controller, UsePipes } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';
@Controller('cats')
@UsePipes(ValidationPipe)
export class CatsController {
@Get()
findAll(@Query('age') age: number) {
return `This action returns all cats of age ${age}`;
}
}
- 在全局范围内使用管道
要在全局范围内使用管道,可以在主模块中配置。
import { Module, ValidationPipe } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
管道的应用场景
- 数据验证:验证请求数据的合法性,如检查必填字段、数据格式等。
- 数据转换:将请求数据转换为所需的格式,如将字符串转换为整数。
- 数据过滤:过滤掉不需要的数据或字段。
示例:数据转换管道
下面是一个数据转换管道的示例,它将请求中的字符串转换为整数。
import { Injectable, PipeTransform, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
NestJS中的拦截器(Interceptor)
拦截器(Interceptor)是 NestJS 框架中用于处理请求和响应的一个重要概念。拦截器在请求到达处理程序之前和响应返回客户端之前执行,可以对请求和响应进行处理、修改等操作。拦截器在 NestJS 中有着广泛的应用场景,如日志记录、响应映射、异常处理等。
如何定义拦截器
在 NestJS 中创建拦截器需要实现 NestInterceptor 接口,并定义 intercept 方法。
下面是一个简单的日志记录拦截器示例:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next.handle().pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
如何使用拦截器
拦截器可以在方法范围内、控制器范围内或全局范围内使用。
- 在方法范围内使用拦截器
要在特定方法上使用拦截器,可以使用 @UseInterceptors 装饰器。
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';
@Controller('cats')
export class CatsController {
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {
return 'This action returns all cats';
}
}
- 在控制器范围内使用拦截器
要在整个控制器上使用拦截器,可以将 @UseInterceptors 装饰器应用于控制器类。
import { Controller, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';
@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
- 在全局范围内使用拦截器
要在全局范围内使用拦截器,可以在主模块中配置。
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './logging.interceptor';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
拦截器的应用场景
- 日志记录:记录每个请求的详细信息,如时间戳、请求路径、请求方法等。
- 响应映射:将响应数据转换为所需的格式。
- 异常处理:在响应返回之前捕获和处理异常。
- 缓存:在请求处理之前检查缓存,以减少不必要的处理。
示例:响应映射拦截器
下面是一个响应映射拦截器的示例,它将响应数据包装在一个统一的格式中。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({ data })),
);
}
}
要使用这个拦截器,可以在方法或控制器上应用:
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { TransformInterceptor } from './transform.interceptor';
@Controller('cats')
@UseInterceptors(TransformInterceptor)
export class CatsController {
@Get()
findAll() {
return ['cat1', 'cat2', 'cat3'];
}
}
异常过滤器
异常过滤器是 NestJS 中用于处理异常的机制。它们可以捕获应用程序中的异常,并对其进行处理,以便向客户端返回适当的响应。异常过滤器在 NestJS 中有着广泛的应用场景,如统一错误处理、自定义错误响应等。
如何定义异常过滤器
在 NestJS 中创建异常过滤器需要实现 ExceptionFilter 接口,并定义 catch 方法。
下面是一个简单的异常过滤器示例:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
如何使用异常过滤器
异常过滤器可以在方法范围内、控制器范围内或全局范围内使用。
- 在方法范围内使用异常过滤器
要在特定方法上使用异常过滤器,可以使用 @UseFilters 装饰器。
import { Controller, Get, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';
@Controller('cats')
export class CatsController {
@Get()
@UseFilters(HttpExceptionFilter)
findAll() {
throw new HttpException('Forbidden', 403);
}
}
- 在控制器范围内使用异常过滤器
要在整个控制器上使用异常过滤器,可以将 @UseFilters 装饰器应用于控制器类。
import { Controller, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';
@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
@Get()
findAll() {
throw new HttpException('Forbidden', 403);
}
}
- 在全局范围内使用异常过滤器
要在全局范围内使用异常过滤器,可以在主模块中配置。
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from './http-exception.filter';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
异常过滤器的应用场景
- 统一错误处理:捕获应用程序中的所有异常,并返回统一的错误响应格式。
- 自定义错误响应:根据异常类型或内容,返回自定义的错误响应。
- 日志记录:记录异常信息,方便调试和分析。
示例:全局异常过滤器
下面是一个全局异常过滤器的示例,它捕获所有未处理的异常,并返回统一的错误响应。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception instanceof HttpException ? exception.message : 'Internal server error',
});
}
}
要使用这个全局异常过滤器,可以在主模块中配置:
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionsFilter } from './all-exceptions.filter';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
],
})
export class AppModule {}
整体执行流程
NestJS 的整体执行流程可以分为以下几个主要步骤:
-
请求进入应用:当客户端发送 HTTP 请求时,请求首先进入 NestJS 应用。
-
中间件处理:请求经过应用配置的中间件。中间件可以对请求进行预处理,例如日志记录、身份验证等。
-
守卫检查:在进入控制器处理程序之前,NestJS 会执行守卫(Guard)来检查请求是否有权限继续执行。如果守卫返回
false,请求将被拒绝。 -
拦截器处理:请求经过拦截器(Interceptor)。拦截器可以对请求和响应进行处理,例如日志记录、响应映射等。
-
管道处理:请求经过管道(Pipe)。管道可以对请求数据进行转换和验证,例如数据验证、数据转换等。
-
控制器处理:请求最终到达控制器处理程序。控制器处理程序处理请求,并调用相应的服务(Provider)来执行业务逻辑。
-
响应处理:控制器处理程序将结果返回给客户端。在返回响应之前,响应会再次经过拦截器和异常过滤器进行处理。
-
异常处理:如果在处理请求的过程中发生异常,异常过滤器会捕获异常,并返回适当的错误响应。
通过上述内容,我们可以看到 NestJS 是如何通过中间件、守卫、拦截器、管道和异常过滤器等机制来实现请求的完整处理流程的。这些机制使得 NestJS 能够提供强大的功能和灵活性,帮助开发者更高效地构建和维护应用程序。