Webpack5学习笔记——性能优化

4,364 阅读11分钟

性能优化

该笔记来自coderwhy老师webpack5教程及参考官方文档总结而来。

环境分离

  • 方案一: 编写两个不同的配置文件,开发和生产时分别加载不同的配置文件
  • 方案二:使用相同的一个入口文件配置,通过设置参数区分他们
//package.json
​
  "scripts": {
    "build": "webpack --config ./config/webpack.prod.js",
    "serve": "webpack serve --config ./config/webpack.dev.js",
    "build2": "webpack --config ./config/webpack.common.js --env production",
    "serve2": "webpack serve --config ./config/webpack.common.js --env development"
  },
  • 入口文件解析:

    • 我们运行webpack时module.exports的entry文件的入口,当为相对路径的时候,该路径相对于context所配置的路径,该路径默认为webpack的启动目录( process.cwd() )。
module.exports = {
    context: path.resolve(__dirame,'./')
    entry: '../src/index.js'
}
  • 环境分离代码
//webpack.commo.js
const resolveApp = require("./paths");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
​
const { merge } = require("webpack-merge");
​
const prodConfig = require("./webpack.prod");
const devConfig = require("./webpack.dev");
​
const commonConfig = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: resolveApp("./build"),
  },
  resolve: {
    extensions: [".wasm", ".mjs", ".js", ".json", ".jsx", ".ts", ".vue"],
    alias: {
      "@": resolveApp("./src"),
      pages: resolveApp("./src/pages"),
    },
  },
  module: {
    rules: [
      {
        test: /.jsx?$/i,
        use: "babel-loader",
      },
      {
        test: /.vue$/i,
        use: "vue-loader",
      },
      {
        test: /.css/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
    new VueLoaderPlugin(),
  ]
};
​
module.exports = function(env) {
    //cli传入的参数可以作为函数参数接收
  const isProduction = env.production;
    //当--env production的值时
    //env: { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true }
    //当--evn development时
    //env : { WEBPACK_SERVE: true, development: true }
  process.env.NODE_ENV = isProduction ? "production": "dev elopment";
    //为node设置环境标识,babel可以读取到该环境
  const config = isProduction ? prodConfig : devConfig;
    //通过判断来判断加载哪个配置文件
  const mergeConfig = merge(commonConfig, config);
    //通用配置文件与环境配置文件合并
​
  return mergeConfig;
    //return的结果作为最终的配置文件
};
​
​
//babel.config.js
const presets = [
  ["@babel/preset-env"],
  ["@babel/preset-react"],
];
const plugins = [];
const isProduction = process.env.NODE_ENV === "production";
​
// React HMR -> 模块的热替换 必然是在开发时才有效果
if (!isProduction) {
  plugins.push(["react-refresh/babel"]);
} else {
​
}
module.exports = {
  presets,
  plugins
}

代码分离

  • 代码分离的目的

    • 它主要的目的是将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件;
    • 比如默认情况下,所有的JavaScript代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载, 就会影响首页的加载速度;代码分离可以分出出更小的bundle,以及控制资源加载优先级,提供代码的加载性能;
  • Webpack中常用的代码分离有三种:

    • 入口起点:使用entry配置手动分离代码;
    • 防止重复:使用Entry Dependencies或者SplitChunksPlugin去重和分离代码;
    • 动态导入:通过模块的内联函数调用来分离代码;

多入口起点

  • 多入口起点就是指我们可以配置多个入口文件

    • 此时输出的文件名字不能为固定值,否则就会报错,可以使用占位符

    • Entry Dependencies(入口依赖)

      • 假如我们的index.js和main.js都依赖两个库:lodash、dayjs,如果我们单纯的进行入口分离,那么打包后的两个bunlde都有会有一份lodash和dayjs,事实上我们可以对他们进行共享;
    • 用于描述入口的对象。你可以使用如下属性:

      • dependOn: 默认情况下,每个入口 chunk 保存了全部其用的模块(modules)。使用 dependOn 选项你可以与另一个入口 chunk 共享模块:
      • filename: 指定要输出的文件名称。
      • import: 启动时需加载的模块。
      • library: 指定 library 选项,为当前 entry 构建一个 library。
      • runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
      • publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。请查看 output.publicPath
  entry: {
    main: "./src/main.js",
    index: "./src/index.js"
    // main: { import: "./src/main.js", dependOn: "shared" },
    // index: { import: "./src/index.js", dependOn: "shared" },
    // lodash: "lodash",
    // dayjs: "dayjs"
    // shared: ["lodash", "dayjs"]  //默认从node_modules中寻找
  },
  output: {
    path: resolveApp("./build"),
    filename: "[name].bundle.js",
    chunkFilename: "[name].[hash:6].chunk.js"
  },
  • 在不使用 import 样式文件的应用程序中(预单页应用程序或其他原因),使用一个值数组结构的 entry,并且在其中传入不同类型的文件,可以实现将 CSS 和 JavaScript(和其他)文件分离在不同的 bundle。

    • 举个例子。我们有一个具有两种页面类型的 PHP 应用程序:home(首页) 和 account(帐户)。home 与应用程序其余部分(account 页面)具有不同的布局和不可共享的 JavaScript。我们想要从应用程序文件中输出 home 页面的 home.jshome.css,为 account 页面输出 account.jsaccount.css

    • 由于我们未指定其他输出路径,因此使用以上配置运行 webpack 将输出到 ./dist./dist 目录下现在包含四个文件:

      • home.js
      • home.css
      • account.js
      • account.css
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
​
module.exports = {
  mode: process.env.NODE_ENV,
  entry: {
    home: ['./home.js', './home.scss'],
    account: ['./account.js', './account.scss'],
  },
  output: {
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /.scss$/,
        use: [
          // fallback to style-loader in development
          process.env.NODE_ENV !== 'production'
            ? 'style-loader'
            : MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
};
  • Dynamic entry

    • 如果传入一个函数,那么它将会在每次 make 事件中被调用。

    • 例如,你可以使用动态入口来从外部来源(远程服务器,文件系统内容或者数据库)获取真正的入口:

module.exports = {
  entry() {
    return fetchPathsFromSomeExternalSource(); 
      // 返回一个会被用像 ['src/main-layout.js', 'src/admin-layout.js'] 的东西 resolve 的 promise
  },
};

SplitChunks

  • 另外一种分包的模式是splitChunk,它是使用SplitChunksPlugin来实现的:

  • Webpack提供了SplitChunksPlugin默认的配置,我们也可以手动来修改它的配置:

    • 比如默认配置中,chunks仅仅针对于异步(async)请求,我们可以设置为initial或者all;
  • chunks

    • 默认值是async
    • 另一个值是initial,表示对通过的代码进行处理
    • all表示对同步和异步代码都进行处理
  • minSize

    • 拆分包的大小, 至少为minSize;
    • 如果一个包拆分出来达不到minSize,那么这个包就不会拆分;
  • maxSize

    • 将大于maxSize的包,拆分为不小于minSize的包
  • minChunks

    • 至少被引入的次数,默认是1
    • 如果我们写一个2,但是引入了一次,那么不会被单独拆分
  • name:设置拆包的名称

    • 可以设置一个名称,也可以设置为false;
    • 设置为false后,需要在cacheGroups中设置名称;
  • cacheGroups

    • 用于对拆分的包就行分组,比如一个lodash在拆分之后,并不会立即打包,而是会等到有没有其他符合规则的包一起来打包
    • test属性:匹配符合规则的包
    • name属性:拆分包的name属性
    • filename属性:拆分包的名称,可以自己使用placeholder属性
optimization: [
    // natural: 使用自然数(不推荐),
    // named: 使用包所在目录作为name(在开发环境推荐)
    // deterministic: 生成id, 针对相同文件生成的id是不变
    // chunkIds: "deterministic",  //与生成的chunk的id有关
    splitChunks: {
          // async异步导入
          // initial同步导入
          // all 异步/同步导入
        chunks: 'all',
          // 最小尺寸: 如果拆分出来一个, 那么拆分出来的这个包的大小最小为minSize
        minSize: 20000,
          // 将大于maxSize的包, 拆分成不小于minSize的包
        maxSize: 20000,
          // minChunks表示引入的包, 至少被导入了几次
        minChunks: 1,
        cacheGroups: {
            vendor: {
              test: /[\/]node_modules[\/]/,
              filename: "[id]_vendors.js", //可以使用placehold
              // name: "vendor-chunks.js",  //一般是固定的
              //打包规则冲突谁的优先级高按照哪个规则,同等级优先使用靠前的
              priority: -10
            },
            // bar: {
            //   test: /bar_/,
            //   filename: "[id]_bar.js"
            // }
            default: {
              minChunks: 2,
              filename: "common_[id].js",
              priority: -20
            }
        }
    }
]

动态导入

  • webpack提供了两种实现动态导入的方式

    • 第一种,使用ECMAScript中的 import() 语法来完成,也是目前推荐的方式;
    • 第二种,使用webpack遗留的 require.ensure,目前已经不推荐使用;
  • 比如我们希望在代码运行时加载一个模块,但我们又不确定一定会用到,最后拆分成独立的js文件,这样可以保证用不到该内容时,浏览器无需处理该文件,此时可以使用动态导入。

  • 动态导入的文件命名:

    • 因为动态导入通常是一定会打包成独立的文件的,所以并不会再cacheGroups中进行配置;
    • 那么它的命名我们通常会在output中,通过 chunkFilename 属性来命名;
  output: {
    path: resolveApp("./build"),
    filename: "[name].bundle.js",
    chunkFilename: "[name].[hash:6].chunk.js"
  },
  • 会发现默认情况下我们获取到的 [name] 是和id ( chunkIds) 的名称保持一致的

    • 如果我们希望修改name的值,可以通过magic comments(魔法注释)的方式;
import(/* webpackChunkName: "foo" */"./foo").then(res => {
  console.log(res);
});

其他优化

  • optimization.chunkIds: 用于告知webpack模块的id采用什么算法生成。

    • natural:按照数字的顺序使用id;

    • named:development下的默认值,一个可读的名称的id;

    • deterministic:确定性的,在不同的编译中不变的短数字id

      • 在webpack4中是没有这个值的;
      • 那个时候如果使用natural,那么在一些编译发生变化时,就会有问题;
    • 最佳实践:

      • 开发过程中,我们推荐使用named;
      • 打包过程中,我们推荐使用deterministic;
  • optimization. runtimeChunk: 配置runtime相关的代码是否抽取到一个单独的chunk中

    • runtime相关的代码指的是在运行环境中,对模块进行解析、加载、模块信息相关的代码;

    • 抽离出来后,有利于浏览器缓存的策略

      • 比如我们修改了业务代码(main),那么runtime和component、bar的chunk是不需要重新加载的;
      • 比如我们修改了component、bar的代码,那么main中的代码是不需要重新加载的;
    • 常见值:

      • true/multiple:针对每个入口打包一个runtime文件;
      • single:打包一个runtime文件;
      • 对象:name属性决定runtimeChunk的名称;
    runtimeChunk: {
      name: function(entrypoint) {
        return `xxx-{entrypoint.name}`
      }
    }

Prefetch与Preload

  • Profetch与Preload都可使用magic Comments进行使用

    • prefetch(预获取):将来某些导航下可能需要的资源
    • preload(预加载):当前导航下可能需要资源
button.addEventListener("click", () => {
  // prefetch -> 魔法注释(magic comments)
    /* webpackPrefetch: true */
    /* webpackPreload: true */
  import(
    /* webpackChunkName: 'element' */
    /* webpackPrefetch: true */
    "./element"
  ).then(({default: element}) => {
    document.body.appendChild(element);
  })
});
  • 与 prefetch 指令相比,preload 指令有许多不同之处

    • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载
    • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载
    • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻

Shimming

  • webpack compiler 能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。然而,一些 third party(第三方库) 可能会引用一些全局依赖(例如 jQuery 中的 $)。因此这些 library 也可能会创建一些需要导出的全局变量。这些 "broken modules(不符合规范的模块)" 就是 shimming(预置依赖) 发挥作用的地方。

  • webpack 背后的整个理念是使前端开发更加模块化。也就是说,需要编写具有良好的封闭性(well contained)、不依赖于隐含依赖(例如,全局变量)的彼此隔离的模块。请只在必要的时候才使用这些特性。

  • 我们可以通过使用ProvidePlugin来实现shimming的效果:

    • ProvidePlugin能够帮助我们在每个模块中,通过一个变量来获取一个package;
    • 如果webpack看到这个模块,它将在最终的bundle中引入这个模块;
    • 另外ProvidePlugin是webpack默认的一个插件,所以不需要专门导入;
const path = require('path');
const webpack = require('webpack');
​
 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'main.js',
     path: path.resolve(__dirname, 'dist'),
   },
  plugins: [
    new webpack.ProvidePlugin({
      _: 'lodash',
    }),
  ],
 };

MiniCssExtractPlugin

  • MiniCssExtractPlugin可以帮助我们将css提取到一个独立的css文件中
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
​
module.exports = {
  plugins: [new MiniCssExtractPlugin({
      filename: "css/[name].[hash:8].css"
  })],
  module: {
    rules: [
       {
          test: /.css/i,
          // style-lodader -> development
          use: [
            isProduction ? MiniCssExtractPlugin.loader: "style-loader", 
            "css-loader"
          ],
       },
    ],
  },
};

Hash、ContentHash、ChunkHash

  • Hash:hash值的生成和整个项目有关系

    • 一旦项目中有文件改变了,Hash值就会改变
  • chunkhash可以有效的解决上面的问题,它会根据不同的入口进行借来解析来生成hash值

    • 修改一个入口的文件,和这个入口相关的文件hash值都会改变
  • contenthash表示生成的文件hash名称,只和内容有关系

    • 仅改变当前文件内容会改变hash值

代码压缩与打包效率

DLL

  • DLL全程是动态链接库(Dynamic Link Library),是为软件在Windows中实现共享函数库的一种实现方式;
  • 那么webpack中也有内置DLL的功能,它指的是我们可以将可以共享,并且不经常改变的代码,抽取成一个共享的库
  • 这个库在之后编译的过程中,会被引入到其他项目的代码中
  • 打包DLL库
const path = require('path');
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
​
module.exports = {
  entry: {
    react: ["react", "react-dom"]
  },
  output: {
    path: path.resolve(__dirname, "./dll"),
    filename: "dll_[name].js",
    library: 'dll_[name]'
  },
  optimization: {
    minimizer: [
      new TerserPlugin({
        extractComments: false
      })
    ]
  },
  plugins: [
    new webpack.DllPlugin({
      name: "dll_[name]",
      path: path.resolve(__dirname, "./dll/[name].manifest.json")
    })
  ]
}
  • 使用DLL库

    • 第一步:通过DllReferencePlugin插件告知要使用的DLL库;
    • 第二步:通过AddAssetHtmlPlugin插件,将我们打包的DLL库引入到Html模块中
plugins: [
      new webpack.DllReferencePlugin({
        context: resolveApp("./"),
        manifest: resolveApp("./dll/react.manifest.json")
      }),
      new AddAssetHtmlPlugin({
        filepath: resolveApp('./dll/dll_react.js')
      })
    ],

Terser

  • Terser是从 uglify-es fork 过来的,并且保留它原来的大部分API以及适配 uglify-es和uglify-js@3等;
  • Terser可以帮助我们压缩、丑化我们的代码,让我们的bundle变得更小。
命令行使用Terser
terser [input files] [options]
# 举例说明
terser js/file1.js -o foo.min.js -c -m
  • Compress option:

    • arrows:class或者object中的函数,转换成箭头函数;
    • arguments:将函数中使用 arguments[index]转成对应的形参名称;
    • dead_code:移除不可达的代码(tree shaking);
  • Mangle option

    • toplevel:默认值是false,顶层作用域中的变量名称,进行丑化(转换);
    • keep_classnames:默认值是false,是否保持依赖的类名称;
    • keep_fnames:默认值是false,是否保持原来的函数名称;
npx terser ./src/abc.js -o abc.min.js -c arrows,arguments=true,dead_code -m  toplevel=true,keep_classnames=true,keep_fnames=true 
Terser在Webpack中的使用
  • 在webpack中有一个minimizer属性,在production模式下,默认就是使用TerserPlugin来处理我们的代码的;

  • 也可以自己来创建TerserPlugin的实例,并且覆盖相关的配置;

  • 首先,我们需要打开minimize,让其对我们的代码进行压缩(默认production模式下已经打开了)

  • 其次,我们可以在minimizer创建一个TerserPlugin:

    • extractComments:默认值为true,表示会将注释抽取到一个单独的文件中;

      • 在开发中,我们不希望保留这个注释时,可以设置为false;
    • parallel:使用多进程并发运行提高构建的速度,默认值是true,并发运行的默认数量: os.cpus().length - 1;

      • 我们也可以设置自己的个数,但是使用默认值即可
    • terserOptions:设置我们的terser相关的配置

      • compress:设置压缩相关的选项
      • mangle:设置丑化相关的选项,可以直接设置为true
      • toplevel:底层变量是否进行转换
      • keep_classnames:保留类的名称
      • keep_fnames:保留函数的名称

HTML文件中的代码压缩

  • HtmlWebpackPlugin插件来生成HTML的模板,事实上它还有一些其他的配置

    • inject:设置打包的资源插入的位置(true、 false 、body、head)
    • cache:设置为true,只有当文件改变时,才会生成新的文件(默认值也是true)
    • minify:默认会使用一个插件html-minifier-terser
      new HtmlWebpackPlugin({
        template: "./index.html",
        // inject: "body"
        cache: true, // 当文件没有发生任何改变时, 直接使用之前的缓存
        minify: isProduction ? {
          removeComments: false, // 是否要移除注释
          removeRedundantAttributes: false, // 是否移除多余的属性
          removeEmptyAttributes: true, // 是否移除一些空属性
          useShortDoctype: true, //使用HTML5的文档声明
          collapseWhitespace: false,  //折叠空格
          removeStyleLinkTypeAttributes: true, //移除一些不必要的属性,例如link中的type=“text/css”
          keepClosingSlash: true,  //是否保存单元素尾部
          minifyCSS: true,  //是否压缩CSS
          minifyJS: {
            mangle: {
              toplevel: true
            }
          }
        }: false
      }),
  • InlineChunkHtmlPlugin: 可以辅助将一些chunk出来的模块,内联到html中

    • 比如runtime的代码,代码量不大,但是是必须加载的,那么我们可以直接内联到html中
  • 这个插件是在react-dev-utils中实现的

const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
​
plugins: [
    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*.js/,])
]

CSS压缩

  • CSS压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等;
  • CSS的压缩我们可以使用另外一个插件:css-minimizer-webpack-plugin;
  • css-minimizer-webpack-plugin是使用cssnano工具来优化、压缩CSS(也可以单独使用)
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');module.exports = {
  plugins: [
    new CssMinimizerPlugin()
  ]
}

TreeShaking

  • webpack实现Tree Shaking采用了两种不同的方案

    • usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的
    • sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
  • 如何在项目中对JavaScript的代码进行TreeShaking

    • 在optimization中配置usedExports为true,来帮助Terser进行优化;
    • 在package.json中配置sideEffects,直接对模块进行优化;
usedExports
  • 将mode设置为development模式

    • 为了可以看到 usedExports带来的效果,我们需要设置为 development 模式
    • 因为在 production 模式下,webpack默认的一些优化会带来很大额影响
  • 设置usedExports为true和false对比打包后的代码

    • 在usedExports设置为true时,会有一段注释:unused harmony export mul;
    • 这段注释的意义是什么呢?告知Terser在优化时,可以删除掉这段代码;
  • 这个时候, minimize设置true:

    • usedExports设置为false时,mul函数没有被移除掉;
    • usedExports设置为true时,mul函数有被移除掉;
  • 所以,usedExports实现Tree Shaking是结合Terser来完成的。

sideEffects
  • sideEffects用于告知webpack compiler哪些模块时有副作用的

  • 在package.json中设置sideEffects的值

    • 如果我们将sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports;
    • 如果有一些我们希望保留,可以设置为数组;
'sideEffects': [
    "./xxxx.js"
]
  • 比如我们有一个format.js、style.css文件

    • 该文件在导入时没有使用任何的变量来接收
    • 那么打包后的文件,不会保留format.js、style.css相关的任何代码;
Css实现TreeShaking
  • CSS的Tree Shaking需要借助于一些其他的插件

    • 在早期的时候,我们会使用PurifyCss插件来完成CSS的tree shaking
    • 目前我们可以使用另外一个库来完成CSS的Tree Shaking:PurgeCSS
  • 配置插件

    • paths:表示要检测哪些目录下的内容需要被分析,这里我们可以使用glob;
    • 默认情况下,Purgecss会将我们的html标签的样式移除掉,如果我们希望保留,可以添加一个safelist的属性;
const PurgeCssPlugin = require('purgecss-webpack-plugin');
​
module.exports = {
  plugins: [
    new PurgeCssPlugin({
      paths: glob.sync(`${resolveApp("./src")}/**/*`, {nodir: true}),
      safelist: function() {
        return {
          standard: ["body", "html"]
        }
      }
    })
  ]
}

Scope Hoisting

  • Scope Hoisting 功能是对作用域进行提升,并且让webpack打包后的代码更小、运行更快

  • 无论是从最开始的代码运行,还是加载一个模块,都需要执行一系列的函数, Scope Hoisting可以将函数合并到一个模块中来运行

  • 使用Scope Hoisting非常的简单,webpack已经内置了对应的模块

    • 在production模式下,默认这个模块就会启用
    • 在development模式下,我们需要自己来打开该模块
plugins: [
     new webpack.optimize.ModuleConcatenationPlugin(),
]

HTTP压缩

  • HTTP压缩是一种内置在 服务器 和 客户端 之间的,以改进传输速度和带宽利用率的方式

  • HTTP压缩的流程什么呢

    • 第一步:HTTP数据在服务器发送前就已经被压缩了
    • 第二步:兼容的浏览器在向服务器发送请求时,会告知服务器自己支持哪些压缩格式(例如:Accept-Encoding: gzip)
    • 第三步:服务器在浏览器支持的压缩格式下,直接返回对应的压缩后的文件,并且在响应头中告知浏览器(例如:Content-Encoding:gzip)
  • Webpack中对文件的压缩

    • webpack中相当于是实现了HTTP压缩的第一步操作,我们可以使用CompressionPlugin
    new CompressionPlugin({
      test: /.(css|js)$/i, //匹配需要压缩的文件
      threshold: 0, //设置文件多大开始压缩
      minRatio: 0.8,  //至少压缩的比例
      algorithm: "gzip",  //采用的压缩算法
      // exclude
      // include
    }),

封装Library

  • webpack可以帮助我们打包自己的库文件
  • 配置webpack.config.js文件
const path = require('path');
​
module.exports = {
  mode: "production",
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "./build"),
    filename: "library_name.js",
    // AMD/CommonJS/浏览器
    // CommnJoS: 社区规范的CommonJS, 这个里面是没有module对象
    // CommonJS2: Node实现的CommonJS, 这个里面是有module对象, module.exports
    libraryTarget: "umd",
    library: "library",
    globalObject: "self"
  }
}

\