webpack基础

74 阅读3分钟

1.常见概念

entry:指定 webpack 应用程序或类库打包的入口。

output:指定 webpack 将所创建的 bundle输出到哪儿,以及如何命名这些文件。

  •  filename: 指定输出bundle的名称,支持使用变量([name])解决多入口、代码拆分以及插件等创建多个bundle的问题
  • path: 指定输出磁盘目录,必须是绝对目录,可以通过__dirname或者node中path模块创建

loaders:指定 webpack 如何将不支持的文件类型转变成可支持的模块,用于对模块的源代码进行转换(默认支持 js以及json两种文件类型)
在module中的rules里定义规则:

  • test 属性:用于标识那些文件需要被对应的 loader 进行转换。
  • use 属性:表示进行转换时,应该使用哪个 loader

plugins:指定 webpack 如何对 bundle以及chunk的处理(优化、处理)

mode:启用webpack在相应环境下的内置优化。 development | production | none 

resolve :配置模块如何解析。

  • alias: 创建 import 或 require 的别名.可以用来改变解析(引用)文件。

2.配置一个支持react+scss+ts的项目

webpack.config.js如下:

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
 
 
module.exports = {
    entry: "./src/index.ts",
    mode: "development",
    module: {
        rules: [
            {
                test: /\.(ts|tsx)$/,
                exclude: [/node_modules/],
                loader: "babel-loader",
                options: {
                    presets: [
                        "@babel/preset-env",
                        "@babel/preset-react",
                        "@babel/preset-typescript",
                    ],
                },
            },
           /*
           {
                test: /\.(ts|tsx)$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },*/
            {
                test: /\.scss$/,
                use: ["style-loader", "css-loader", "sass-loader"],
            },
            {
                test: /\.html$/,
                loader: "html-loader",
            },
        ],
    },
    resolve: { extensions: [".ts", ".tsx",'.js'] },
    output: {
        path: path.resolve(__dirname, "dist/"),
        publicPath: "/dist/",
        filename: "bundle.js",
    },
    devServer: {
        contentBase: "./dist",
        compress: true,
        port: 8080,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "./src/index.html"),
            filename: "index.html",
        }),
        new CleanWebpackPlugin()
    ],
};

3.常见webpack打包优化方案

项目打包过程,可以分为几个部分:

  • 搜索所有的依赖项
  • 解析所有的依赖模块。
  •  将所有的依赖模块压缩打包到一个文件。

可以从这三个方面分别考虑打包优化。

1.搜索时间优化。

缩小文件搜索范围 ,减小不必要的编译工作
通过优化loader,以及reslove(module、alias、extensions、mainfileds、noparse)配置。

2.解析时间优化。

通过多进程打包优化解析时间。

tthread-loader。(只要把 thread-loader 放置在其他 loader 之前, 那 thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。)

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      // 创建一个 js worker 池
      use: [
        'thread-loader',
        'babel-loader'
      ]
    },
 ]
 }

3.优化压缩时间

webpack4 默认内置使用 terser-webpack-plugin 插件压缩优化代码,该插件使用 terser 来缩小 JavaScript 。

optimization: {
  minimizer: [
    new TerserPlugin({
      parallel: true,
    }),
  ],
 }

4.代码分割

Webpack默认会将所有依赖的文件打包输出到一个bundle.js中(单入口时),可以使用代码分割来将不同代码单独打包成不同chunk输出

  • 通过optimization将公共代码单独打包成chunk。
  • import动态导入(当想要根据业务拆分bundle时可以用这种方式)
  • css资源单独打包。(使用MiniCssExtractPlugin提供的loader)

5. 合理利用缓存。

可以缩短连续构建时间,但会增加初始构建时间.

  • 有些解析文件的loader自身带有缓存功能(如babel-loader,vue-loader)

  • 可以通过cache-loader/HardSourceWebpackPlugin.

  • webpack5提供了cache配置项

4. 实现自定义loader\自定义plugin

自定义loader:

module.exports = function (source) {
  // 默认单行和多行注释都删除
  const defaultOption = {
    oneLine: true,
    multiline: true,
}
// 获取到webpack.config.js中配置的options
const configOptions = this.getOptions();
let result = source;

const  options = Object.assign({}, defaultOption, configOptions);
// 去除单行注释
if (options.oneLine) {
    result = result.replace(/\/\/.*/g, "")
}
    // 去除多行注释
if (options.multiline) {   
    result = result.replace(/\/\*.*?\*\//g, "")
}
return result
}

自定义plugin:

const { sources } = require('webpack');
 
class DelCommentPlugin {
    constructor(options) {
        this.options = options
    }
 
    apply(compiler) {
        // compilation 创建之后执行注册事件
        compiler.hooks.compilation.tap("DelCommentPlugin", (compilation) => {
            // 处理asset
            compilation.hooks.processAssets.tap(
                {
                    name: 'DelCommentPlugin', //自定义插件名称
                    stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,
                },
                (assets) => {
                    for (const name in assets) {
                        // 只对js资产做处理
                        if (name.endsWith(".js")) {
                            if (Object.prototype.hasOwnProperty.call(assets, name)) {
                                const asset = compilation.getAsset(name);
                                const contents = asset.source.source();
                                const result = contents.replace(/\/\/.*/g, "").replace(/\/\*.*?\*\//g, "");
                                // 更新asset的内容
                                compilation.updateAsset(
                                    name,
                                    new sources.RawSource(result)
                                );
                            }
                        }
                    }
                }
            );
        })
    }
}
module.exports = DelCommentPlugin

参考:

juejin.cn/post/697174…
juejin.cn/post/684490…
juejin.cn/post/684490…
juejin.cn/post/697532…