目录
[隐藏]
Angular-cli
通过 angular.json
实现构建配置,对于复杂配置需求的支持,可以通过 ng eject
输出 webpack 的配置文件,然后自定义 webpack 来达到目的。
当前对于 Angular 6.x/7.x
的项目,Angular-cli
需采用 6.x/7.x
版本,而这些版本不支持 ng eject
命令,于是希望自定义 webpack 配置实现更多复杂需求就无法下手。
1 自定义 Angular-cli 7.x 默认 webpack 的方案思路
由于 Angular-cli
内部是封装 webpack 来实现的,如果我们能够 hacker 一下,在其 webpack 配置封装过程中注入自定义配置,就也可以实现自定义需求。
按照这个思路,通过翻阅 Angular-cli 的源码,得到如下注入方法。该注入方法采用的 angular
相关工具链各版本为:
{
"@angular-devkit/build-angular": "0.10.4",
"@angular/cli": "7.0.4"
}
2 新建注入代码的脚本
新建注入代码的脚本 scripts/insert-to-cli-webpack.js
,内容参考:
const chalk = require('chalk');
const fs = require('fs');
const path = require('path');
const rootDir = path.resolve(__dirname, '../');
const buildNgSrcPathList = {
browser: path.resolve(__dirname, '../node_modules/@angular-devkit/build-angular/src/browser/index.js'),
karma: path.resolve(__dirname, '../node_modules/@angular-devkit/build-angular/src/karma/index.js'),
server: path.resolve(__dirname, '../node_modules/@angular-devkit/build-angular/src/server/index.js'),
};
const webpackCliPath = path.resolve(__dirname, '../config/webpack-cli-inject.js').replace(/\\/g, '\\\\');
const findStr = 'webpackMerge(webpackConfigs)';
try {
Object.keys(buildNgSrcPathList).forEach(type => {
const filePath = buildNgSrcPathList[type];
const filePathShort = filePath.replace(rootDir, '');
const replaceStr = `require('${webpackCliPath}')(webpackMerge(webpackConfigs), wco, '${type}')`;
const configText = fs.readFileSync(filePath, 'utf-8');
if (configText.includes(replaceStr)) {
return;
}
if (!configText.includes(findStr)) {
console.log(chalk.red.bold(`文件 ${chalk.yellow.bold(filePathShort)} 中未发现可替换的字符串: ${findStr}`));
return;
}
console.log(chalk.yellow.bold(` Inserting to: `), chalk.yellow(filePathShort));
const output = configText.replace(findStr, replaceStr);
fs.writeFileSync(filePath, output);
});
console.log(chalk.green.bold(' well done!'));
} catch(err) {
console.log(err);
}
3 新建自定义的 webpack 配置文件
如文件名为 config/webpack.cli-inject.js
,内容参考:
const webpackMerge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const pkg = require('../package.json');
const isProd = ENV === 'prod' || ENV === 'production';
module.exports = (commonConfig, wco, cfgType) => {
const customCfg = {
module: {
rules: [
{
test: /\.pug$/,
use: [
'raw-loader',
{
loader: 'pug-html-loader',
options: {
doctype: 'html'
}
}
]
},
// {
// test: /\.css$/,
// use: ['to-string-loader', { loader: 'css-loader', options: { url: false } }]
// },
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader', 'sprites-loader', 'less-loader' ]
},
]
},
plugins: [
/*
* See: https://github.com/ampedandwired/html-webpack-plugin
*/
new HtmlWebpackPlugin({
pkg: pkg,
// ----以上为自定义参数,用于模板中调用----
xhtml: true,
template: 'src/index.html',
filename: 'index.html',
chunksSortMode: function(a, b) {
const entryPoints = ['inline', 'polyfills', 'sw-register', 'styles', 'vendor', 'main'];
return entryPoints.indexOf(a.names[0]) - entryPoints.indexOf(b.names[0]);
},
inject: 'body',
minify: isProd
? {
caseSensitive: true,
collapseWhitespace: true,
keepClosingSlash: true
}
: false
}),
],
/**
* Disable performance hints
* See: https://github.com/a-tarasyuk/rr-boilerplate/blob/master/webpack/dev.config.babel.js#L41
*/
// performance: {
// hints: false
// },
// devtool: 'inline-cheap-source-map'
};
// 过滤一些默认的规则,使用自定义的
commonConfig.module.rules = commonConfig.module.rules.filter(rule => {
if (rule.test) {
// 过滤 html 的解析
if (rule.test.test('index.html') && rule.loader === 'raw-loader') {
return false;
}
// // 过滤 less 的解析
if (rule.test.test('index.less')) {
return false;
}
}
return true;
});
const mergedCfg = webpackMerge([commonConfig, customCfg]);
// console.log(cfgType, wco);
// console.log(mergedCfg.module.rules)
return mergedCfg;
};
如上内容示例,我们自定义了如下的 webpack 配置功能:
- 添加了
pug
模板编译支持; - 我们希望在模板内引用一些变量,使用了
HtmlWebpackPlugin
插件,并屏蔽了默认的 html 解析配置; - 我们使用 less 并希望实现
css sprites
,于是自定义了 less 的规则。
注意两点:
- 默认对
html
文件的解析 loader 必须屏蔽,否则模板会全部解析为字符串,ejs 语法不会生效; less-loader
当前不支持less 3.x
版本,应当安装less^2.7.x
。angular-cli 内部是采用postcss
实现的 less 编译。
4 在 package.json
配置 postinstall
钩子
在每次执行 npm install
或 yarn install
后,立即执行 node scripts/insert-to-cli-webpack.js
,即可实现自定义 webpack 配置的需求。
我们可以在 package.json
中配置执行注入脚本的命令 webpack.inject
,示例参考:
"scripts": {
"postinstall": "npm run webpack.inject",
"webpack.inject": "node scripts/insert-to-cli-webpack.js"
}