webpack配置优化

1,038 阅读3分钟

当loader和plugins使用较多后项目也会越来越耗时,因此这里介绍如何优化webpack的配置让项目打包的更快耗时更短。

主要从以下几个方面进行优化:

  • 缩小文件搜索范围
  • 减少打包文件
  • 缓存
  • 多进程

一、缩小文件搜索范围

1. loader

include表示哪些目录中的文件需要进行babel-loader,exclude表示哪些目录中的文件不要进行babel-loader。
这是因为在引入第三方模块的时候,很多模块已经是打包后的,不需要再被处理,比如vue、jQuery等;如果不设置include/exclude就会被loader处理,增加打包时间。

{
    rules: [{
        test: /\.js$/,
        use: {
            loader: 'babel-loader'
        },
        // exclude: /node_modules/,
        include: [path.resolve(__dirname, 'src')]
    }]
}

2. noParse

对于jQuery、lodash、chartjs等一些库,庞大且没有采用模块化标准,因此我们可以选择不解析他们。

如果一些第三方模块没有使用AMD/CommonJs规范,可以使用noParse来标记这个模块,这样Webpack在导入模块时,就不进行解析和转换,提升Webpack的构建速度;noParse可以接受一个正则表达式或者一个函数:

{
    module: {
        // noParse: /jquery|lodash|chartjs/,
        noParse: function(content){
            return /jquery|lodash|chartjs/.test(content)
        }
    }
}

3. resolve.modules

modules用于告诉webpack去哪些目录下查找引用的模块,默认值是["node_modules"],意思是在./node_modules查找模块,找不到再去../node_modules,以此类推。   我们代码中也会有大量的模块被其他模块依赖和引入,由于这些模块位置分布不固定,路径有时候会很长,比如import '../../src/components/button'、import '../../src/utils';这时我们可以利用modules进行优化

{
    resolve: {
        modules: [
            path.resolve(__dirname, "src"),
            path.resolve(__dirname, "node_modules"),
            "node_modules",
        ],
    },
}

这样我们可以简单的通过import 'components/button'、import 'utils'进行导入,webpack会会优先从src目录下进行查找

4. resolve.alias

alias通过创建import或者require的别名,把原来导入模块的路径映射成一个新的导入路径;它和resolve.modules不同的的是,它的作用是用别名代替前面的路径,不是省略;这样的好处就是webpack直接会去对应别名的目录查找模块,减少了搜索时间。

{
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
}

这样我们就能通过import Buttom from '@/Button'来引入组件了;我们不光可以给自己写的模块设置别名,还可以给第三方模块设置别名:

{
  resolve: {
    alias: {
      'vue$': isDev ? 'vue/dist/vue.runtime.js' : 'vue/dist/vue.runtime.min.js',
    },
  },
}

我们在import Vue from 'vue'时,webpack就会帮我们去vue依赖包的dist文件下面引入对应的文件,减少了搜索package.json的时间。

5. resolve.mainFields

mainFields用来告诉webpack使用第三方模块中的哪个字段来导入模块;第三方模块中都会有一个package.json文件用来描述这个模块的一些属性,比如模块名(name)、版本号(version)、作者(auth)等等;其中最重要的就是有多个特殊的字段用来告诉webpack导入文件的位置,有多个字段的原因是因为有些模块可以同时用于多个环境,而每个环境可以使用不同的文件。 mainFields的默认值和当前webpack配置的target属性有关:

如果target为webworker或web(默认),mainFields默认值为["browser", "module", "main"] 如果target为其他(包括node),mainFields默认值为["module", "main"]

这就是说当我们require('vue')的时候,webpack先去vue下面搜索browser字段,没有找到再去搜索module字段,最后搜索main字段。 为了减少搜索的步骤,在明确第三方模块入口文件描述字段时,我们可以将这个字段设置尽量少;一般第三方模块都采用main字段,因此我们可以这样配置:

{
    resolve: {
        mainFields: ["main"],
    }
}

6. resolve.extensions

extensions字段用来在导入模块时,自动带入后缀尝试去匹配对应的文件,它的默认值是:

{
    resolve: {
        extensions: ['.js', '.json']
    }
}

也就是说我们在require('./utils')时,Webpack先匹配utils.js,匹配不到再去匹配utils.json,如果还找不到就报错。
因此extensions数组越长,或者正确后缀的文件越靠后,匹配的次数越多也就越耗时,因此我们可以从以下几点来优化:

extensions数组尽量少,项目中不存在的文件后缀不要列进去 出现频率比较高的文件后缀优先放到最前面 在代码中导入文件的时候,要尽量把后缀名带上,避免查找

二、减少打包文件

1. 定位体积大的文件

使用webpack-bundle-analyzer插件可以可视化的查看webpack打包出来的各个文件体积大小,以便我们定位大文件,进行体积优化。

  • 安装依赖:
npm install webpack-bundle-analyzer --save-dev
  • 配置示例:

webpack.config.js

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports={
  plugins: [
    new BundleAnalyzerPlugin()  // 使用默认配置
    // 默认配置的具体配置项
    // new BundleAnalyzerPlugin({
    //   analyzerMode: 'server',
    //   analyzerHost: '127.0.0.1',
    //   analyzerPort: '8888',
    //   reportFilename: 'report.html',
    //   defaultSizes: 'parsed',
    //   openAnalyzer: true,
    //   generateStatsFile: false,
    //   statsFilename: 'stats.json',
    //   statsOptions: null,
    //   excludeAssets: null,
    //   logLevel: info
    // })
  ]
}

package.json

{
    "scripts": {
        "analyz": "webpack-dev-server --config build/webpack.dev.config.js --progress"
    }
}
  • 查看
npm run analyz

打开浏览器访问 http://localhost:8888 ,即可看到分析页面。

插件详情参考:

2. 提取公共的代码模块、文件

在我们项目中不可避免会引入第三方模块,webpack打包时也会将第三方模块作为依赖打包进bundle中,这样就会增加打包文件尺寸和增加耗时,如果能合理得处理这些模块就能提升不少webpack的性能。

  • 如何分chung?将组件单独打包?

optimization.splitChunks中配置

  • 配置示例:
{
    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    chunks: "all"
                }
            }
        },
        runtimeChunk: {
            name: 'manifest'
        }
    }
}

参考

本文严重参考以下文章: