本文发布于:2018-11-09,最后更新于:2022-12-20,如果内容失效请留言告知。
目录
[隐藏]
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
相关工具链各版本为:
1 2 3 4 | { "@angular-devkit/build-angular" : "0.10.4" , "@angular/cli" : "7.0.4" } |
2 新建注入代码的脚本
新建注入代码的脚本 scripts/insert-to-cli-webpack.js
,内容参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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
,内容参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 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: [ /* */ 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 */ // 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
,示例参考:
1 2 3 4 | "scripts" : { "postinstall" : "npm run webpack.inject" , "webpack.inject" : "node scripts/insert-to-cli-webpack.js" } |