高级配置

这篇文章介绍一些高级配置的配置项,这些配置项虽然在日常项目中配置得较少,但是对于项目来说这些配置项往往是可以解决日常常见的问题。

使用拆包

在项目中,不合理的 Bundle 是致命的。在 Webpack 中,总共提供了三种方式来实现代码拆分(Code Splitting):

  • entry 配置:通过多个 entry 文件来实现;
  • 动态加载(按需加载):通过写代码时主动使用import()或者require.ensure来动态加载;
  • 抽取公共代码:使用splitChunks配置来抽取公共代码。

在 San CLI 中可以通过splitChunks抽取公共代码。splitChunks的配置项跟 Webpack 中 optimizationsplitChunks是完全相同的。例如下面的配置:

module.exports = {
    // ...
    splitChunks: {
        cacheGroups: {
            defaultVendors: {
                name: 'vendors',
                test: /[\\/]node_modules(?!\/@baidu)[\\/]/,
                // minChunks: 1,
                priority: -10
            },
            common: {
                name: 'common',
                test: /([\/]src\/components(-open)?|[\\/]node_modules\/@baidu\/nano)/,
                priority: -20,
                minChunks: 1,
                chunks: 'initial'
            }
        }
    }
};

代码压缩和优化

在项目中可以对产出的代码进行压缩和优化,San CLI 会对 JS 使用 terserjs 和对 CSS 使用 cssnano。

默认的 terserjs 配置:

{
    format: {
        comments: false
    },
    compress: {
        unused: true,
        // 删掉 debugger
        drop_debugger: true, // eslint-disable-line
        // 移除 console
        drop_console: true, // eslint-disable-line
        // 移除无用的代码
        dead_code: true // eslint-disable-line
    },
    ie8: false,
    safari10: true,
    warnings: false,
    toplevel: true
}

San CLI 的配置文件中使用terserOptions可以对默认的配置进行修改:

module.exports = {
    // ...
    terserOptions: {
        // 自定义的配置
    }
};

默认的 cssnano 配置:

{
    mergeLonghand: false,
    cssDeclarationSorter: false,
    normalizeUrl: false,
    discardUnused: false,
    // 避免 cssnano 重新计算 z-index
    zindex: false,
    reduceIdents: false,
    safe: true,
    // cssnano 集成了 autoprefixer 的功能
    // 会使用到 autoprefixer 进行无关前缀的清理
    // 关闭 autoprefixer 功能
    // 使用 postcss 的 autoprefixer 功能
    autoprefixer: false,
    discardComments: {
        removeAll: true
    }
}

在 San CLI 的配置文件中,所有跟 CSS 的相关的配置是放在了css配置项中,所以对 cssnano 的修改也是在css.cssnanoOptions中进行修改:

module.exports = {
    // ...
    css: {
        cssnanoOptions: {
            // 自定义的配置
        }
    }
};

loaderOptions.esbuild

实验性功能,可能会有坑,但可以有效提升速度体验。

压缩 js 除使用默认开启的 terserjs 外,还可以使用 esbuild 压缩,开启方式:

module.exports = {
    // ...
    loaderOptions: {
        esbuild: true // 或填入 {}
    }
};

开启后将使用以下的默认配置进行压缩:

{
    // ...
    minify: true,        
    target: 'es2015',
};

也可通过 esbuild 配置项直接传入配置,具体配置见esbuild-loader

esbuild 开启后,默认也会在开发环境下使用 esbuild-loader 替换 babel-loader,速度会大幅提升。

esbuild 也可以开启 css 压缩(默认不开启),按照如下方式配置即可:

module.exports = {
    // ...
    loaderOptions: {
        esbuild: {css: true}
    }
};

unsafeCache

webpack5 新增了安全策略,对应 config.module.unsafeCache,开启后(true)表示忽略安全策略,可加快构建速度,默认不开启(false)。

html-minifier 配置

San CLI 中使用的 html-webpack-plugin 的配置项中可以使用 html-minifier,在 San CLI 中默认的配置如下:

{
    removeComments: true,
    collapseWhitespace: false,
    // 引号保留,不然 inline 的 base64 图片 compress 时报错
    removeAttributeQuotes: false,
    quoteCharacter: '"',
    collapseBooleanAttributes: true,
    removeScriptTypeAttributes: false,
    minifyCSS: true,
    // 处理 smarty 和 php 情况
    ignoreCustomFragments: [/{%[\s\S]*?%}/, /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/],
    keepClosingSlash: true
    // more options:
    // https://github.com/kangax/html-minifier#options-quick-reference
}

使用者可以在pages中的html-minifier进行配置,具体配置可以参考这里

编译 NPM 包中的 ES6 语法

在项目中,我们推荐使用 ESM 语法的模块,ESM 语法的模块在使用的同时,可以使用统一的 Webpack 配置,并且基于 Tree-shaking,打出的包体积更加合理。但是在 San CLI 中,默认不会编译 NPM 包中的 ES6 语法的代码,这时候如果需要编译依赖的 NPM 包中的 ES6 语法,需要使用transpileDependencies

transpileDependencies可接受的类型为ArrayString或者RegExp。例如我们项目依赖@baidu/nano这个 UI 基础库,则可以设置配置如下:

module.exports = {
    // ...
    transpileDependencies: ['@baidu/nano']
};

这样nano这个模块就会被 Webpack 编译了。

使用 chainWebpack 和 configWebpack 进行个性化配置

如果要更加自主地进行个性化的配置,那么可以用 San CLI 配置文件中的 chainWebpack 和 configWebpack 来修改配置,chainWebpack 接受的参数是 webpack-chain 语法的配置,configWebpack接受的参数是 Webpack 的配置对象。

例如:

// 静态文件域名
const CDN = 'https://s.bdstatic.com/';
// 生产环境下的静态目录
const STATIC_PRO = 'static/pro';

module.exports = {
    chainWebpack: config => {
        // 这里可以用来扩展 webpack 的配置,使用的是 webpack-chain 语法
        config.module
            .rule('img')
            .test(/\.(png|jpe?g|gif)(\?.*)?$/)
            .use('url-loader')
            .loader(require.resolve('url-loader'))
            .options({
                limit: 1000,
                name: STATIC_PRO + '/img/[name].[hash:7].[ext]',
                publicPath: __isProduction ? CDN : ''
            });

        config.module
            .rule('svg')
            .use('svg-url-loader')
            .loader(require.resolve('svg-url-loader'))
            .options({
                limit: 2500,
                name: STATIC_PRO + '/svg/[name].[hash:7].[ext]',
                publicPath: __isProduction ? CDN : ''
            });
    }
};

在配置文件中添加 Service 插件

plugins 增加自定义插件,例如:

module.exports = {
    plugins: [
        {
            id: 'built-in:plugin-progress',
            apply(api, projectOptions, options = {}) {
                api.chainWebpack(webpackConfig => {
                    options.color = require('san-cli-utils/randomColor').color;
                    webpackConfig.plugin('progress').use(require('webpackbar'), [options]);
                });
            }
        },
        'san-plugin.js'
    ]
};

添加 dev server 中间件

添加 dev server 中间件,需要使用 Service 的插件的addDevServerMiddleware方法,例如:

const plugins = [
    {
        id: 'middleware1',
        apply(api) {
            // 使用 api 配置dev server 中间件
            api.middleware(() =>
                require('hulk-mock-server')({
                    // 配置contentBase
                    contentBase: path.join(__dirname, './' + outputDir + '/'),
                    // 配置 mock 路径
                    rootDir: path.join(__dirname, './mock'),
                    // 配置解析器相关内容
                    processors: [
                        `smarty?router=/template/*&baseDir=${path.join(
                            __dirname,
                            `./${outputDir}/template`
                        )}&dataDir=${path.join(__dirname, './mock/_data_')}`
                    ] // eslint-disable-line
                })
            );
        }
    }
];
module.exports = {
    plugins
};

这里特殊说明下,middleware传入的是一个 function,并且返回一个中间件。

更多

如果想了解更多 Service 插件相关内容,那么请浏览这个文档