Webpack 核心总结

前言

笔者最近从新复习了一遍 Webpack 核心功能,果真是温故而知新!这篇文章结合高级进阶之 Webpack 篇阅读效果更佳!javascript

常见优化手段

构建速度优化

思路:缩小搜索范围,减小没必要要的模块打包,加快构建(缓存,多线程)
缩少搜索范围:css

  • 指定第三方目录 resolve.modules,resolve.alias 缓存目录,extensions 减小后缀的搜索
  • loader 配置指定编译范围 exclude/include

减小没必要要的模块打包:前端

  • 经过cdn引入(vue 全家桶)等,而后用externals 提取经常使用库,不会再打包到 bundle 文件,并能够经过import 引入;
  • 经过 DllPlugin、DllReferencePlugin 插件来预编译模块,减小没必要要的打包

注意:在 Webpack5 中已经不⽤ DllPlugin、DllReferencePlugin 了,⽽是⽤ HardSourceWebpackPlugin 替代vue

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const plugins = [
 new HardSourceWebpackPlugin(
)
]
复制代码

加快构建:java

  • 使用cache-loader进行缓存;
  • 使用 happypack 插件开启多个线程打包资源文件(happypack再也不维护),能够用 thread-loader 替代

性能优化

代码压缩 :
node

使用 ParallelUglifyPlugin 插件:开启多线程对 js 文件压缩和缓存,删除多余的注释和 console.log
注意: 在生产环境下即配置中 mode 设置为 production,webpack 默认开启了 TerserWebpackPlugin 能够实现该功能,若是须要剔除调试代码能够自行配置,具体参考下面的 TerserWebpackPlugin 配置
webpack

  1. 多进程并行压缩
  • webpack-paralle-uglify-plugin
  • uglifyjs-webpack-plugin 开启 parallel 参数 (不支持ES6)
  • terser-webpack-plugin 开启 parallel 参数
  1. 经过 mini-css-extract-plugin 提取 Chunk 中的 CSS 代码到单独文件,经过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS。
  2. 图片压缩 配置 image-webpack-loader


代码分割缓存:
使用 optimization.splitChunks.cacheGroup 进行公共代码分割抽离和缓存 es6

如何作 Tree Shaking

Webpack4.0 以上版本在 mode 为 production 时,会自动开启 Tree Shaking 能力。默认 production mode 的配置以下:web

const config = {
 mode: 'production',
 optimization: {
 usedExports: true,
 minimizer: [
  new TerserPlugin({...})
 ]
 }
};
复制代码

什么是反作用

"side effect(反作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局做用域,而且一般不提供 export。面试

解决反作用:经过 package.json 的 "sideEffects" 属性,来实现这种方式。

{
  "name": "your-project",
  "sideEffects": ["./src/some-side-effectful-file.js"]
}
复制代码

若是你的代码确实有一些反作用,能够改成提供一个数组:

{
  "name": "your-project",
  "sideEffects": ["./src/some-side-effectful-file.js"]
}
复制代码

loader 与 plugin 的区别

  • loader: loader是一个转换器是在 import 或"加载"模块时预处理文件,将A语言转成B语言,如 TypeScript转换为 JavaScript,less 转成 css,单纯的文件转换成浏览器能够识别的文件。
  • plugin:插件是一个扩展器,能够扩展 webpack 的功能,在 webpack 运行的生命周期会广播许多钩子,plugin 会监听这些事件,在合适的时机经过 webapck API (run、compile、emit)等改变输出结果。

webpack 热更新原理

如下内容来自字节前端面试题

  1. 当修改了一个或多个文件;
  2. 文件系统接收更改并通知 webpack;
  3. webpack 从新编译构建一个或多个模块,并通知 HMR 服务器进行更新;
  4. HMR Server 使用 webSocket 通知 HMR runtime 须要更新,HMR 运行时
    经过 HTTP 请求更新 jsonp;
  5. HMR 运行时替换更新中的模块,若是肯定这些模块没法更新,则触发整
    个页面刷新。

注意:启动 HMR 后,css 抽离会不⽣效,还有不⽀持 contenthash,chunkhash

webapck 打包原理

高级进阶之 Webpack 篇

重要配置代码展现

webpack-paralle-uglify-plugin

// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
    new ParallelUglifyPlugin({
      // 传递给 UglifyJS 的参数
      uglifyJS: {
        output: {
          // 最紧凑的输出
          beautify: false,
          // 删除全部的注释
          comments: false,
        },
        compress: {
          // 在UglifyJs删除没有用到的代码时不输出警告
          warnings: false,
          // 删除全部的 `console` 语句,能够兼容ie浏览器
          drop_console: true,
          // 内嵌定义了可是只用到一次的变量
          collapse_vars: true,
          // 提取出出现屡次可是没有定义成变量去引用的静态值
          reduce_vars: true,
        }
      },
    }),
复制代码

uglifyjs-webpack-plugin

const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
	optimization: {
		minimizer: [
			new UglifyJsPlugin({
				test: /\.js(\?.*)?$/i, //测试匹配文件,
				// include: /\/includes/, //包含哪些文件
				// exclude: /\/node_modules/, //不包含哪些文件
				//容许过滤哪些块应该被uglified(默认状况下,全部块都是uglified)。
				//返回true以uglify块,不然返回false。
				chunkFilter: chunk => {
				  // `vendor` 模块不压缩
				  if (chunk.name === "vendor") {
					return false;
				  }
				  return true;
				},
				cache: false, //是否启用文件缓存,默认缓存在node_modules/.cache/uglifyjs-webpack-plugin.目录
				parallel: true //使用多进程并行运行来提升构建速度
			})
		]
	},
}

// 等价于

{
    plugins: [    
            // 压缩JS文件
            new UglifyJSPlugin({
                            test: /\.js(\?.*)?$/i, //测试匹配文件,
                            // include: /\/includes/, //包含哪些文件
                            // exclude: /\/node_modules/, //不包含哪些文件
                            //容许过滤哪些块应该被uglified(默认状况下,全部块都是uglified)。
                            //返回true以uglify块,不然返回false。
                            chunkFilter: chunk => {
                              // `vendor` 模块不压缩
                              if (chunk.name === "vendor") {
                                    return false;
                              }
                              return true;
                            },
                            cache: false, //是否启用文件缓存,默认缓存在node_modules/.cache/uglifyjs-webpack-plugin.目录
                            parallel: true //使用多进程并行运行来提升构建速度,不支持 es6
                    }),
    ],
}

复制代码

terser-webpack-plugin

// webpack.config.js

// 导入terser-webpack-plugin-->减小js体积(其中删除js的console.log和注释)
const TerserWebpackPlugin = require('terser-webpack-plugin');
// 实例化TerserWebpackPlugin对象
const terserPlugin = new TerserWebpackPlugin({
  parallel: 4,
  extractComments: true,
  terserOptions: {
    compress: {
      warnings: false,
      drop_console: true,
      drop_debugger: true,
      pure_funcs: ['console.log'] //移除console
    }
  }
});

module.exports = {
	optimization: {
    	minimizer: [
      	// 只有打包环境为production时才能生效
      	terserPlugin
    ],
  },
}

复制代码

mini-css-extract-plugin

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      // chunkFilename: "[name].css",
      disable: isDebug
    }),],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};
复制代码

optimization.splitChunks

splitChunks:{
        cacheGroups: {
            common:{
                chunks: 'initial',
                name:'Common', // 打包后的文件名
                minSize: 0, 
                minChunks: 2 // 重复2次才能打包到此模块
            },
            vendor: {
                priority: 1, // 优先级配置,优先匹配优先级更高的规则,不设置的规则优先级默认为0
                test: /node_modules/, // 匹配对应文件
                chunks: 'initial',
                name:'Vendor',
                minSize: 0,
                minChunks: 1
            }
        }
    }
复制代码

热更新实现

const path = require('path')
const webpack = require('webpack')
module.exports = {
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, '/')
    },
    devServer: {
        hot: true
    },
    plugins:[
    new webpack.HotModuleReplacementPlugin()
    ]
}
复制代码

若是不使用 HotModuleReplacementPlugin 能够直接 package.json 配置

"dev": "webpack-dev-server --hot --open"
复制代码