一种自定义 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 插件,并屏蔽了默认的 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 installyarn 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"
}
点赞 (6)

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code