依赖注入(Dependency Injection, DI) 是一种通过外部容器管理组件/类间依赖关系的设计模式,其核心是控制反转(Inversion of Control, IoC)。在前端开发中,DI通过将服务、配置、工具类等依赖注入到组件中,替代组件直接实例化依赖的方式,实现以下目标:
Angular通过层级注入器树和装饰器语法提供完整的DI支持,是前端DI的典型实现。
核心机制:
Root Injector
(应用级)、Module Injector
(模块级)、Component Injector
(组件级)三级注入器,允许按需共享依赖。@Injectable()
装饰器标记服务类,并使用providedIn
属性指定注入范围(如'root'
表示单例)。代码示例:
// 1. 定义服务(单例)
@Injectable({ providedIn: 'root' })
export class UserService {
getUser() { return { id: 1, name: 'John' }; }
}
// 2. 在组件中注入服务
@Component({
selector: 'app-order',
template: `订单ID: {{ order.id }}`
})
export class OrderComponent {
constructor(private userService: UserService) {} // 构造函数注入
order = { id: 101, userId: this.userService.getUser().id };
}
// 3. 动态依赖配置(使用Factory)
@Injectable()
export class ConfigService {
constructor(@Inject('API_URL') private apiUrl: string) {}
}
@NgModule({
providers: [
{ provide: 'API_URL', useValue: 'https://api.example.com' } // 使用值注入
]
})
export class AppModule {}
关键特性:
providedIn: 'root'
确保服务全局唯一。@Optional()
装饰器标记非必需依赖。@Inject
装饰器为依赖指定别名(如动态配置)。React本身未内置DI系统,但可通过以下方式实现:
Context API:适合跨层级组件共享依赖,避免层层传递props。
第三方库:如inversify
、tsyringe
等提供完整的DI容器支持。
代码示例(Context API):
// 1. 创建上下文
const UserContext = React.createContext();
// 2. 提供依赖的Provider
function UserProvider({ children }) {
const userService = {
getUser: () => ({ id: 1, name: 'Alice' }),
};
return (
<UserContext.Provider value={userService}>
{children}
</UserContext.Provider>
);
}
// 3. 注入依赖的组件
function OrderPage() {
const userService = React.useContext(UserContext);
return <div>当前用户: {userService.getUser().name}</div>;
}
// 4. 使用
function App() {
return (
<UserProvider>
<OrderPage />
</UserProvider>
);
}
第三方库示例(inversify):
import 'reflect-metadata';
import { Container, injectable, inject } from 'inversify';
// 1. 定义接口和实现
interface IUserService {
getUser(): { id: number; name: string };
}
@injectable()
class UserService implements IUserService {
getUser() { return { id: 1, name: 'Bob' }; }
}
// 2. 配置容器
const container = new Container();
container.bind<IUserService>('IUserService').to(UserService);
// 3. 注入依赖的组件
@injectable()
class OrderComponent {
constructor(@inject('IUserService') private userService: IUserService) {}
render() {
return `订单用户: ${this.userService.getUser().name}`;
}
}
// 4. 使用
const order = container.get<OrderComponent>(OrderComponent);
console.log(order.render()); // 输出: 订单用户: Bob
Vue通过Provide/Inject
和插件机制实现DI:
Provide/Inject:在祖先组件中提供依赖,在后代组件中注入。
插件机制:通过app.use()
全局注入依赖。
代码示例(Provide/Inject):
// 1. 祖先组件提供依赖
export default {
provide() {
return {
authService: {
isLoggedIn: () => true,
},
};
},
template: '<ChildComponent />',
};
// 2. 后代组件注入依赖
export default {
inject: ['authService'],
template: `<div>登录状态: {{ authService.isLoggedIn() ? '已登录' : '未登录' }}</div>`,
};
代码示例(插件全局注入):
// 1. 定义插件
const authPlugin = {
install(app) {
app.config.globalProperties.$auth = {
isLoggedIn: () => true,
};
app.provide('authService', { isLoggedIn: () => true }); // 同时支持Provide/Inject
},
};
// 2. 注册插件
const app = createApp(App);
app.use(authPlugin);
// 3. 在组件中使用
export default {
inject: ['authService'], // 或通过this.$auth访问
template: `<div>全局认证: {{ authService.isLoggedIn() }}</div>`,
};
@Injectable()
将服务与组件分离。TestBed
)原生支持DI的Mock。Provide/Inject
统一管理跨层级依赖。useFactory
实现依赖的动态创建。providedIn: 'root'
共享全局服务。inversify
动态加载插件。Provide/Inject
传递数据。authService
。forwardRef
)。providedIn: 'root'
实现)。useMemo
优化Context)。@Optional()
和@Inject
避免未注册依赖的错误。inversify
等库。Provide/Inject
,复杂场景用插件系统。// 1. 定义接口和实现
export interface ILogger {
log(message: string): void;
}
@Injectable({ providedIn: 'root' })
export class ConsoleLogger implements ILogger {
log(message: string) { console.log(message); }
}
@Injectable()
export class AppService {
constructor(private logger: ILogger) {} // 注入接口
process() { this.logger.log('Processing...'); }
}
// 2. 在组件中使用
@Component({
selector: 'app-root',
template: '<button (click)="run()">Run</button>'
})
export class AppComponent {
constructor(private appService: AppService) {}
run() { this.appService.process(); } // 输出: Processing...
}
import 'reflect-metadata';
import { Container, injectable, inject } from 'inversify';
// 1. 定义依赖
interface IOrderService {
getOrders(): string[];
}
@injectable()
class OrderService implements IOrderService {
getOrders() { return ['Order1', 'Order2']; }
}
// 2. 配置容器
const container = new Container();
container.bind<IOrderService>('IOrderService').to(OrderService);
// 3. 注入依赖的组件
@injectable()
class OrderList {
constructor(@inject('IOrderService') private orderService: IOrderService) {}
render() {
return this.orderService.getOrders().join(', ');
}
}
// 4. 使用
const list = container.get<OrderList>(OrderList);
console.log(list.render()); // 输出: Order1, Order2
// 1. 祖先组件提供依赖
export default {
data() {
return { theme: 'dark' };
},
provide() {
return { theme: this.theme };
},
template: '<ChildComponent />',
};
// 2. 后代组件注入依赖
export default {
inject: ['theme'],
template: `<div>当前主题: {{ theme }}</div>`, // 输出: 当前主题: dark
};
通过合理使用依赖注入,前端开发者可以显著提升代码的灵活性、可维护性和可测试性,构建更健壮的应用架构。
Copyright © 2019- zgxue.com 版权所有 京ICP备2021021884号-5
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务