Webpack优化指南

举报
flyfishzy 发表于 2020/06/23 19:23:44 2020/06/23
【摘要】 Webpack的优化主要包括以下的目的:优化开发体验:优化构建速度。 在项目庞大时构建耗时可能会变的很长,每次等待构建的耗时加起来也会是个大数目。优化使用体验。 通过自动化手段完成一些重复的工作,让我们专注于解决问题本身。优化输出质量:优化输出质量的目的是为了给用户呈现体验更好的网页,例如减少首屏加载时间、提升性能流畅度等。 这至关重要,因为在互联网行业竞争日益激烈的今天,这可能关系到你的产...

Webpack的优化主要包括以下的目的:

优化开发体验:

  1. 优化构建速度。 在项目庞大时构建耗时可能会变的很长,每次等待构建的耗时加起来也会是个大数目。

  2. 优化使用体验。 通过自动化手段完成一些重复的工作,让我们专注于解决问题本身。

优化输出质量:

优化输出质量的目的是为了给用户呈现体验更好的网页,例如减少首屏加载时间、提升性能流畅度等。 这至关重要,因为在互联网行业竞争日益激烈的今天,这可能关系到你的产品的生死。
优化输出质量本质是优化构建输出的要发布到线上的代码,分为以下几点:

  1. 减少用户能感知到的加载时间,也就是首屏加载时间。

  2. 提升流畅度,也就是提升代码性能。

缩小文件搜索范围

优化loader配置

由于 Loader 对文件的转换操作很耗时,需要让尽可能少的文件被 Loader 处理。可以通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。

 

module.exports = {
  module: {
    rules: [
      {
        // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
        test: /\.js$/,
        // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
        use: ['babel-loader?cacheDirectory'],
        // 只对项目根目录下的 src 目录中的文件采用 babel-loader
        include: path.resolve(__dirname, 'src'),
      },
    ]
  },
};

优化 resolve

resolve.modules 的默认值是 ['node_modules'],含义是先去当前目录下的 ./node_modules 目录下去找想找的模块,如果没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules 中找,以此类推。
当安装的第三方模块都放在项目根目录下的 ./node_modules 目录下时,没有必要按照默认的方式去一层层的寻找,可以指明存放第三方模块的绝对路径,以减少寻找,配置如下:

 

module.exports = {
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    // 其中 __dirname 表示当前工作目录,也就是项目根目录
    modules: [path.resolve(__dirname, 'node_modules')]
  },
};

在实战项目中经常会依赖一些庞大的第三方模块,以React为例,通过配置 resolve.alias 可以让 Webpack 在处理 React 库时,直接使用单独完整的 react.min.js 文件,从而跳过耗时的递归解析操作。

 

module.exports = {
  resolve: {
    // 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件,
    // 减少耗时的递归解析操作
    alias: {
      'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js')
    }
  },
};

在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试询问文件是否存在。如果这个列表越长,或者正确的后缀在越后面,就会造成尝试的次数越多,所以 resolve.extensions 的配置也会影响到构建的性能。 在配置 resolve.extensions 时你需要遵守以下几点,以做到尽可能的优化构建性能:

  • 后缀尝试列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中。

  • 频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。

  • 在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。例如在你确定的情况下把 require('./data') 写成 require('./data.json')。

优化 module.noParse 配置

module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。
在上面的 优化 resolve.alias 配置 中讲到单独完整的 react.min.js 文件就没有采用模块化,让我们来通过配置 module.noParse 忽略对 react.min.js 文件的递归解析处理, 相关 Webpack 配置如下:

 

const path = require('path');


module.exports = {
  module: {
    // 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
    noParse: [/react\.min\.js$/],
  },
};

使用Dlls

使用DllPlugin插件将不常变更的代码进行单独编译。尽管这会增加构建的复杂度,但会提高应用的编译速度。

  1. 使用DllPlugin配置一个webpack_dll.config.js来构建dll文件:

 

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
 entry:{
     react:['react','react-dom'],
     polyfill:['core-js/fn/promise','whatwg-fetch']
 },
 output:{
     filename:'[name].dll.js',
     path:path.resolve(__dirname, 'dist'),
     library:'_dll_[name]',  //dll的全局变量名
 },
 plugins:[
     new DllPlugin({
         name:'_dll_[name]',  //dll的全局变量名
         path:path.join(__dirname,'dist','[name].manifest.json'),//描述生成的manifest文件
     })
 ]
}
  1. 在主config文件里使用DllReferencePlugin插件引入xx.manifest.json文件:

 

const path = require('path');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = {
    entry:{ main:'./main.js' },
    //... 省略output、loader等的配置
    plugins:[
        new DllReferencePlugin({
            manifest:require('./dist/react.manifest.json')
        }),
        new DllReferenctPlugin({
            manifest:require('./dist/polyfill.manifest.json')
        })
    ]
}

多线程构建

核心原理:使用 thread-loader 将 loader 放置在一个 worker 池里面运行,以达到多线程构建,减少构建时间。

 

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          "thread-loader",
          // 你的高开销的loader放置在此 (e.g babel-loader)
        ]
      }
    ]
  }

每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。请在高开销的loader中使用,否则效果不佳

压缩代码

在 Webpack3 中,我们一般使用 UglifyJS 来压缩代码,但是这个是单线程运行的,为了加快效率,我们可以使用 webpack-parallel-uglify-plugin 来并行运行 UglifyJS,从而提高效率。在 Webpack4 中,我们就不需要以上这些操作了,只需要将 mode 设置为 production 就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩 JS 代码,还可以压缩 HTML、CSS 代码,并且在压缩 JS 代码的过程中,我们还可以通过配置实现比如删除 console.log 这类代码的功能。

启用Tree Shaking

  1. 修改.babelrc以保留ES6模块化语句

  2. 启动webpack时带上 --display-used-exports可以在shell打印出关于代码剔除的提示

  3. 使用UglifyJSPlugin,或者启动时使用--optimize-minimize

  4. 在使用第三方库时,需要配置 resolve.mainFields: ['jsnext:main', 'main'] 以指明解析第三方库代码时,采用ES6模块化的代码入口


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。