每日学习计划(Webpack - 性能优化)

1. Webpack性能优化

性能优化主要体现在三方面:

  1. JS运行性能
  2. 开发构建性能
  3. 传输性能

JS运行性能

JS代码在浏览器端运行的速度

该阶段取决于开发者如何书写高性能代码。

开发构建性能

降低代码从打包到代码效果呈现的时间

减少模块解析

模块解析:抽象语法树分析,依赖分析,模块语法替换

如果不对某个模块进行解析,那么该模块经过loader后就是最终代码,可以缩短构建时间

什么模块不需要解析:模块中无其它依赖,并且已经打包过的第三方库。(例:jquery)

如何让某个模块不解析:通过配置module.noParse

模块完整解析过程


module.exports = {
  moudle: {
    noParse: /test.js/ // 忽略test.js文件的解析过程
  }
}

配置noParse过程

优化loader性能

// index.js
const _ = require('lodash');
const a = require('./a.js');

// webpack.config.js
module.exports = {
  module: {
    rules: [
      test: /\.js$/,
      use: 'babel-loader'
    ]
  }
}

以上代码中,lodash库使用的是ES3语法,如果通过babel进行转换反而会浪费构建时间,这时可以通过module.rules.excludemodule.rules.include,可以排除或仅包含需要应用loader的模块

// 配置后lodash将不会应用babel-loader
module.exports = {
  module: {
    rules: [
      test: /\.js$/,
      exclude: /lodash/,
      use: 'babel-loader'
    ]
  }
}
// 甚至可以排除掉`node_modules`目录中的模块,或仅转换`src`目录的模块
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        //或
        // include: /src/,
        use: "babel-loader"
      }
    ]
  }
}

缓存loader结果

在某些情况下,有些文件的内容可能不变,经过相同的loader解析后,解析的结果也不变,如果这时候可以将第一次的解析结果缓存起来,让后续的解析可以直接使用缓存的结果,这时候可以用到cache-loader

// 经过cache-loader解析的文件会被缓存到内存中
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['cache-loader, ...loader']
      }
    ]
  }
}

热替换

热替换:当文件发生变化时,浏览器不会刷新页面重新请求,仅请求改动的资源

热替换并不能降低构建时间(可能还会稍微增加),但可以降低代码改动到效果呈现的时间

// 当配置hot属性后,webpack会向打包结果中注入module.hot属性
module.exports = {
  devServer: {
    hot: true // 开启HMR模块
  },
  //plugins: {
  //  当开启HMR模块后,webpack会自动添加webpack.HotModuleReplacementPlugin()插件;
  //  插件HotModuleReplacementPlugin会根据覆盖原始代码,然后让代码重新执行;
  //  new webpack.HotModuleReplacementPlugin();
  //}
}
// index.js
if(module.hot){ // 判断是否开启热替换
  // 如果没有开启socket管道,不管是否有开启热替换,webpack都会刷新页面;
  module.hot.accept(); // 开启socket管道,允许服务器将更新的内容发送到浏览器
}

传输性能

JS代码打包后传输到浏览器经过的时间

优化传输性能可以从三方面入手:

  1. 减少文件体积
  2. 减少请求数量
  3. 利用浏览器缓存

手动分包

手动分包:将公共模块单独打包减少主文件体积

// 单独使用配置文件webpack.dll.config.js
const webpack = require('webpack');
const path = require('path');

module.exports = {
  mode: 'production',
  entry: {
  lodash: ['lodash']
  },
  output: {
    filename: 'dll/[name].js',
    library: '[name]' // 暴露全局变量名
  },
  plugins: [
    // 利用Dllplugin生成资源清单
    new webpack.DllPlugin({
      // 资源清单中的保存路径
      path: path.resolve(__dirname, "dll", "[name].manifest.json"),
      name: '[name]' // 资源清单中,暴露的变量名
    })
  ]
}
<!-- 公共模块文件生成后,在页面中手动引入公共模块 -->
<script src='./dll/lodash.js'></script>
// 如果配置了clean-webpack-plugin插件
new CleanWebpackPlugin({
  // 排除dll目录本身和它里面的文件,避免它把公共模块清除
  cleanOnceBeforeBuildPatterns: ["**/*", '!dll', '!dll/*']
})
// 最后通过DllReferencePlugin使用资源清单
module.exports = {
  plugins:[
    new webpack.DllReferencePlugin({
      manifest: require("./dll/lodash.manifest.json")
    })
  ]
}

总结手动打包主要分为三个步骤:

  1. 通过output.library暴露公共模块
  2. 使用DllPlugin创建资源清单
  3. 使用DllReferencePlugin使用资源清单

自动分包

自动分包:要配置一个合理的分包策略让webpack自动处理分包

// webpack提供了`optimization`配置项,用于配置一些优化信息
// 其中`splitChunks`是分包策略的配置
module.exports = {
  optimization: {
    splitChunks: {
      // 分包策略,一般情况下默认配置即可应对大部分分包场景
      chunks: all, // 配置需要应用分包策略的chunk,默认值:async
      maxSize: 66666, // 控制包的最大字节数,超过该子节数会尽可能分离多个包
      minSize: 30000, // 最小字节,达到该子节数才允许分包,默认值:30000
      minChunks: 2, // 一个模块被多少个chunk引用时才进行分包,默认值:1
      automaticNameDelimiter: '~', // 新chunk名称的分隔符,默认值:~
    }
  }
}

// chunk的取值:
// all:所有的chunk都要应用分包策略
// async:默认,仅针对异步chunk应用分包策略
// initial:仅针对普通chunk应用分包策略

缓存组:之前配置的分包策略是全局的,而实际上,分包策略是基于缓存组的,每个缓存组有自己独立的分包策略,webpack会按照优先级,依次处理每个缓存组,被缓存组处理过的分包不会再次被处理

module.exports = {
  optimization:{
    splitChunks: {
      //全局配置
      cacheGroups: {
        // 默认情况下,webpack提供了两个缓存组
        // 属性名是缓存组名称,会影响到分包的chunk名
        // 属性值是缓存组的配置,缓存组继承所有的全局配置,也有自己特殊的配置
        vendors: {
          test: /[\\/]node_modules[\\/]/, // 当匹配到相应模块时,将这些模块进行单独打包
          priority: -10 // 缓存组优先级,优先级越高,该策略越先进行处理,默认值为0
        },
        default: {
          minChunks: 2,  // 覆盖全局配置,将最小chunk引用数改为2
          priority: -20, // 优先级
          reuseExistingChunk: true // 重用已经被分离出去的chunk
        }
      }
    }
  }
}

原理:自动分包主要经过以下步骤:

  1. 检查每个chunk编译的结果
  2. 根据分包策略,找到那些满足策略的模块
  3. 根据分包策略,生成新的chunk打包这些模块(代码有所变化)
  4. 把打包出去的模块从原始包中移除,并修正原始包代码

代码压缩

代码压缩(Terser):移除模块内部的无效代码,减少文件体积,破坏代码可读性

// 默认情况下webapck已自动集成了Terser插件

const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
  optimization: {
    // 是否要启用压缩,默认情况下,生产环境会自动开启
    minimize: true, 
    minimizer: [ // 压缩时使用的插件,可以有多个
      new TerserPlugin(),
      new OptimizeCSSAssetsPlugin()
    ],
  },
};

Tree shaking

tree shaking:移除模块之间的无效代码(移除不会用到的导出依赖),生成环境下,自动开启

原理:webpack会从入口模块寻找依赖关系,当解析一个模块时,根据ES6的模块导入语句来判断,该模块依赖了另一个模块的哪个导出

保持代码可运行性

如果导入的是一个对象,webapck为了避免错误,不会移除任何信息

因此,在编写代码时,尽量使用以下模块导入导出:

  • 使用export xxx导出,而不使用export default {xxx}导出
  • 使用import {xxx} from "xxx"导入,而不使用import xxx from "xxx"导入
作用域分析

Tree shaking没有完善的作用域分析,因此对于一些dead code函数中的依赖仍然不会移除,
插件webpack-deep-scope-plugin提供了作用域分析,可解决这些问题

副作用处理

Tree shaking当一个模块可能有副作用时,往往会将该模块视为有副作用,webapck为了避免错误,不会移除任何信息

// 配置package.json 属性sideEffects
{
  "sideEffects": false
  // - false:当前工程中,所有模块都没有副作用。注意,这种写法会影响到某些css文件的导入
  // - 数组:设置哪些文件拥有副作用,例如:`["!src/common.js"]`,表示只要不是`src/common.js`的文件,都有副作用
}

CSS Tree shaking

webpack无法对CSS完成Tree shaking,因为CSS跟ES6没有关系,因此对CSS的Tree shaking需要其他插件完成
例如:purgecss-webpack-plugin

GZIP

GZIP压缩文件算法,减少文件体积,传输效率可能大幅提升

缺点:服务器压缩文件需要时间,客户端解压文件需要时间

webpack可以通过compression-webpack-plugin插件对文件进行预压缩,移除服务器的压缩时间

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值