目的

打包大小
打包速度
模块拆分

1.css优化

删除无用的css样式

使用purge-css + mini-css

  1. const path = require("path");
  2. const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //提取css为link标签引入
  3. const HtmlWebpackPlugin = require("html-webpack-plugin"); //html提取插件 给模板
  4. const { CleanWebpackPlugin } = require("clean-webpack-plugin");
  5. const vueLoaderPlugin = require("vue-loader/lib/plugin");
  6. const glob = require("glob");
  7. //主要的作用时删除无意义的css 只能配合 mini-css-extract-plugin使用
  8. const PurgeCssWebpackPlugin = require("purgecss-webpack-plugin");
  9. {
  10. test: /\.css$/,
  11. use: [
  12. //style生产环境下转为link标签引入
  13. mode == "production" ? MiniCssExtractPlugin.loader : "style-loader",
  14. "css-loader",
  15. ],
  16. },

2.图片优化

图片压缩插件

image-webpack-loader

        {
          //图片 降低分辨率 image-webpack-loader 从右向左 先优化再拷贝
          //在使用file-loader之前对图片进行压缩
          test: /\.(png|jpe?g|gif)$/,
          use: [
            "file-loader",
            mode == "production1" && {
              loader: "image-webpack-loader",
              options: {
                mozjpeg: {
                  progressive: true,
                  quality: 65,
                },
                // optipng.enabled: false will disable optipng
                // optipng: {
                //   enabled: false,
                // },
                // pngquant: {
                //   quality: [0.65, 0.9],
                //   speed: 4,
                // },
                // gifsicle: {
                //   interlaced: false,
                // },
                // // the webp option will enable WEBP
                // webp: {
                //   quality: 75,
                // },
              },
            },
          ].filter(Boolean),
        },

3.CDN加载js

图片.png

externals(用得很多)

外部扩展(Externals) | webpack 中文文档https://webpack.docschina.org/configuration/externals/

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)
例如,从 CDN 引入 jQuery,而不是把它打包:

外部扩展(Externals) | webpack 中文文档https://webpack.docschina.org/configuration/externals/

比如jquery 不打包而是html中直接引入
script标签里面引入的是全局的

    externals:{
      "jquery":"$" //不去打包代码中的jquery $是变量名 代表是从外部引入的
    },

请查看上面的例子。属性名称是 jquery,表示应该排除 import $ from ‘jquery’ 中的 jquery 模块。为了替换这个模块,jQuery 的值将被用来检索一个全局的 jQuery 变量。换句话说,当设置为一个字符串时,它将被视为全局的(定义在上面和下面)。

4.Tree-shaking树摇 && Scope-Hoisting作用域提升

tree-shaking默认值支持es6静态导入语法 export import
只在生产环境下使用

optimization

开发环境下标出未使用的js

在development下

    optimization: {
      usedExports: true, //使用了哪个模块和我说一下
    },


     unused harmony export
/***/ "./src/calc.js":
/*!*********************!*\
  !*** ./src/calc.js ***!
  \*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"minus\": () => (/* binding */ minus)\n/* harmony export */ });\n/* unused harmony export add */\nvar add = function add(a, b) {\n  return a + b + \"sum\";\n};\nvar minus = function minus(a, b) {\n  return a - b + \"minus\";\n};\n\n//# sourceURL=webpack://learn-webpack-optimize/./src/calc.js?");

生产环境中剔除副作用的代码

如果引入的变量没有被使用 就应该被删除
图片.png

sideEffects:true

结论
我们学到为了利用 tree shaking 的优势, 你必须…

  • 使用 ES2015 模块语法(即 import 和 export)。
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
  • 在项目的 package.json 文件中,添加 “sideEffects” 属性。
  • 使用 mode 为 “production” 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码) 和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。
如果你对优化输出很感兴趣,请进入到下个指南,来了解 生产环境 构建的详细细节。

Scope-Hoisting作用域提升

每个模块都是一个函数

会减少作用域 会去做计算
会内部优化
图片.png
图片.png
计算好一个值 直接替换

5.DllPlugin动态链接库 (一般用在开发阶段)

DllPlugin

webpack.dll.js


const path = require("path")

const DllPlugin = require("webpack").DllPlugin

module.exports = {
    mode:"development",
    entry:["vue"],
    output:{
        library:"vue",//打包后接收自执行函数的名字叫做calc
        // libraryTarget:"commonjs2",//umd this var commonjs
        filename:"vue.dll.js",
        path:path.resolve(__dirname,"dll")
    },
    // 当前这个dll.js并没有在页面中引入
    plugins:[
        new DllPlugin({
            name:"react",
            path:path.resolve(__dirname,"dll/manifest.json")
        })
    ]
}

// 目前是为了将calc 打包成 node 可以使用的模块 commonjs




/******
 * // 我本地使用了import React语法 
//  需要先去manifest.json查找 找到后会加载 
 * 
 * 
 * 
 */

DllReferencePlugin

add-asset-html-webpack-plugin - npmhttps://www.npmjs.com/package/add-asset-html-webpack-plugin

const DLLReferencePlugin = require("webpack").DllReferencePlugin;
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");
      new DLLReferencePlugin({
        //打包时候会配置clean-webpack-plugin 所以不放在dist里面, 而应该放到dll下
        manifest: path.resolve(__dirname, "dll/manifest.json"),
      }),
      new AddAssetHtmlPlugin({
        filepath: path.resolve(__dirname, "dll/*.dll.js"),
      }),

大大提升构建速度
图片.png

先将react + react-plugin打包好

manifest.json 载货单文件

怎么打包第三方库

就先抽离好 打好包 以后就不用打包了

图片.png

6.动态加载 import语法

原理就是jsonp 动态导入

import语法可以实现代码分割

默认会打包成单文件
使用jsonp去加载

图片.png
图片.png


let button = document.createElement("button");

button.innerHTNL = "点我";

button.addEventListener("click", () => {
  // 动态导入 import语法 类比路由的懒加载
  //webpack默认会把这样的语法单独打包成一个文件
  //点击的时候会使用jsonp动态加载这个文件
  import(/* webpackChunkName:'calc' */ "./calc").then((data) => {
    console.log(data);
  });
  console.log(add(1, 2));
});

document.body.appendChild(button);

7.打包分析工具

webpack-bundle-analyzer - npmhttps://www.npmjs.com/package/webpack-bundle-analyzer

图片.png

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    devMode && new BundleAnalyzerPlugin()
  ].filter(Boolean)
}

88端口
当多入口都加载了某个模块时,
需要抽离第三方模块
1.不要和业务逻辑放在一起
2.增加缓存 304

多入口 多entry多output

图片.png
图片.png

8.splitChunks 避免重复依赖

SplitChunksPlugin | webpack 中文文档

https://webpack.docschina.org/plugins/split-chunks-plugin/

下面这个配置对象代表 SplitChunksPlugin 的默认行为。
webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async', //异步
      minSize: 20000, //20k
      minRemainingSize: 0,
      minChunks: 1,//最少一次
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
Wa

splitChunks.chunks

string = ‘async’ function (chunk)
这表明将选择哪些 chunk 进行优化。当提供一个字符串,有效值为 all,async 和 initial。设置为 all 可能特别强大,因为这意味着 chunk 可以在异步和非异步 chunk 之间共享。
webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // include all types of chunks
      chunks: 'all',
    },
  },
};

9.费时更新

speed-measure-webpack-plugin - npmhttps://www.npmjs.com/package/speed-measure-webpack-plugin

图片.png

yarn add -D speed-measure-webpack-plugin

包裹

const webpackConfig = {
  plugins: [new MyPlugin(), new MyOtherPlugin()],
};
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

const smp = new SpeedMeasurePlugin();

const webpackConfig = smp.wrap({
  plugins: [new MyPlugin(), new MyOtherPlugin()],
});

10.noParse

图片.png

11.resolve

图片.png

12.include/exclude

图片.png

resolve

object
配置模块如何解析。例如,当在 ES2015 中调用 import ‘lodash’,resolve 选项能够对 webpack 查找 ‘lodash’ 的方式去做修改(查看模块)。
webpack.config.js
module.exports = { //… resolve: { // configuration options }, };

resolve.alias

object
创建 import 或 require 的别名,来确保模块引入变得更简单。例如,一些位于 src/ 文件夹下的常用模块:
webpack.config.js

const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/'),
    },
  },
};

}, };
现在,替换“在导入时使用相对路径”这种方式,就像这样:

import Utility from '../../utilities/utility';

你可以这样使用别名:

import Utility from 'Utilities/utility';

也可以在给定对象的键后的末尾添加 $,以表示精准匹配:
webpack.config.js

const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      xyz$: path.resolve(__dirname, 'path/to/file.js'),
    },
  },
};

这将产生以下结果:

import Test1 from 'xyz'; // 精确匹配,所以 path/to/file.js 被解析和导入
import Test2 from 'xyz/file.js'; // 非精确匹配,触发普通解析
alias: import ‘xyz’ import ‘xyz/file.js’
{} /abc/node_modules/xyz/index.js /abc/node_modules/xyz/file.js
{ xyz: ‘/abc/path/to/file.js’ } /abc/path/to/file.js error
{ xyz$: ‘/abc/path/to/file.js’ } /abc/path/to/file.js /abc/node_modules/xyz/file.js
{ xyz: ‘./dir/file.js’ } /abc/dir/file.js error
{ xyz$: ‘./dir/file.js’ } /abc/dir/file.js /abc/node_modules/xyz/file.js
{ xyz: ‘/some/dir’ } /some/dir/index.js /some/dir/file.js
{ xyz$: ‘/some/dir’ } /some/dir/index.js /abc/node_modules/xyz/file.js
{ xyz: ‘./dir’ } /abc/dir/index.js /abc/dir/file.js
{ xyz: ‘modu’ } /abc/node_modules/modu/index.js /abc/node_modules/modu/file.js
{ xyz$: ‘modu’ } /abc/node_modules/modu/index.js /abc/node_modules/xyz/file.js
{ xyz: ‘modu/some/file.js’ } /abc/node_modules/modu/some/file.js error
{ xyz: ‘modu/dir’ } /abc/node_modules/modu/dir/index.js /abc/node_modules/modu/dir/file.js
{ xyz$: ‘modu/dir’ } /abc/node_modules/modu/dir/index.js /abc/node_modules/xyz/file.js