Angular生产包中条件注入服务时如何剔除Mock服务代码?
你之前的两种方式之所以会把Mock代码打包进生产产物,核心原因是编译时Mock服务的代码仍被模块引用,即便运行时不会使用,tree-shaking也无法识别并移除这些被引用但未执行的代码。下面是几种可靠的解决方案:
方案一:使用Angular原生fileReplacements配置(推荐)
利用Angular构建时的文件替换功能,直接在生产构建时替换掉Mock服务的引用文件,从根源上避免Mock代码被打包。
准备两个服务文件:
api.service.ts:开发环境下导出Mock服务import { Injectable } from '@angular/core'; @Injectable() export class ApiService { // Mock实现逻辑 }api.service.prod.ts:生产环境下导出真实服务import { Injectable } from '@angular/core'; @Injectable() export class ApiService { // 真实接口调用逻辑 }
修改
angular.json的生产构建配置,添加文件替换规则:"projects": { "your-project-name": { "architect": { "build": { "configurations": { "production": { // 其他生产配置... "fileReplacements": [ { "replace": "src/app/services/api.service.ts", "with": "src/app/services/api.service.prod.ts" }, // 原有的environment文件替换规则... ] } } } } } }
这样生产构建时,Angular会直接用真实服务文件替换Mock服务文件,Mock代码完全不会进入打包流程。
方案二:动态条件导入+Tree-shaking
通过动态导入结合环境变量,让生产环境下Mock服务的导入代码被tree-shaking移除。
在环境文件中定义服务获取函数:
environment.ts(开发环境):export const environment = { production: false, getApiService: () => import('./mock-api.service').then(m => m.MockApiService) };environment.prod.ts(生产环境):export const environment = { production: true, getApiService: () => import('./api.service').then(m => m.ApiService) };
在模块中使用
useFactory异步加载服务:import { environment } from '../environments/environment'; import { ApiService } from './api.service'; @NgModule({ providers: [ { provide: ApiService, useFactory: async () => { const ServiceClass = await environment.getApiService(); return new ServiceClass(); } } ] }) export class AppModule {}
生产环境下,getApiService只会引用真实服务,Mock服务的导入代码会被webpack的tree-shaking完全移除。
方案三:Webpack DefinePlugin 条件编译
通过webpack的DefinePlugin注入环境变量,让TypeScript在编译时剔除Mock服务的分支代码。
安装
@angular-builders/custom-webpack(如果未安装),用于自定义webpack配置:npm install @angular-builders/custom-webpack --save-dev修改
angular.json的构建器为自定义webpack:"projects": { "your-project-name": { "architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./webpack.config.js" }, // 其他配置... } } } } }创建
webpack.config.js,添加DefinePlugin:const webpack = require('webpack'); module.exports = { plugins: [ new webpack.DefinePlugin({ 'USE_MOCK': JSON.stringify(process.env.NODE_ENV !== 'production') }) ] };在模块中使用条件判断:
import { ApiService } from './api.service'; let mockService; if (USE_MOCK) { // 仅开发环境下导入Mock服务 mockService = require('./mock-api.service').MockApiService; } @NgModule({ providers: [ { provide: ApiService, useClass: USE_MOCK ? mockService : ApiService } ] }) export class AppModule {}
生产环境下,USE_MOCK会被替换为false,Mock服务的导入和分支代码会被tree-shaking移除。
内容的提问来源于stack exchange,提问作者F. Scholz

