一種自定義 Angular-cli 6.x/7.x 預設 webpack 配置的方法
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 css sprites
注意兩點:
- 預設對
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" }