【webpack】常用webpack配置

2023/08/14 16:52:242023/01/06 11:47:46

通用

区分环境与配置文件

区分不同环境的配置文件

将不同环境的配置文件抽离到单独的文件中:

  • webpack.base.config.js 定义公共的配置。
  • webpack.dev.config.js:定义开发环境的配置。
  • webpack.prod.config.js:定义生产环境的配置。

webpack-merge 合并配置:

  • 安装
npm install webpack-merge -D
  • 配置
const merge = require('webpack-merge');
merge({
    devtool: 'cheap-module-eval-source-map',
    module: {
        rules: [
            {a: 1}
        ]
    },
    plugins: [1,2,3]
}, {
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [4,5,6],
});
//合并后的结果为
{
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 1},
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [1,2,3,4,5,6]
}

使用不同配置文件:

  • 安装
npm install cross-env -D
  • 配置启动命令

在启动命令中配置环境变量和要使用的配置文件。

// package.json
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack --config build/webpack.dev.config.js",
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.config.js"
  }
}
  • 在 js 中读取环境变量
const isProdEnv = process.env.NODE_ENV === "production";
const isDevEnv = process.env.NODE_ENV === "development";

处理 JS:babel-loader

js 编译、降级。

  • 安装

    npm install babel-loader -D
    
    npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
    
    npm install @babel/runtime @babel/runtime-corejs3
    
  • webpack 配置

    在 webpack 中配置 babel。

    //webpack.config.js
    module.exports = {
      // mode: 'development',
      module: {
        rules: [
          {
            test: /\.jsx?$/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      corejs: 3,
                    },
                  ],
                ],
              },
            },
            exclude: /node_modules/,
          },
        ],
      },
    };
    
  • babel 配置

    可以创建一个 .babelrc 文件单独放置 babel 的配置信息。

    {
      "presets": ["@babel/preset-env"],
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "corejs": 3
          }
        ]
      ]
    }
    

处理 html:html-webpack-plugin

处理 html 文件,官网地址:html-webpack-pluginopen in new window

  • 安装

    npm install html-webpack-plugin -D
    
  • 配置

    // webpack.config.js
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    module.exports = {
      //...
      plugins: [
        // 数组 放着所有的webpack插件
        new HtmlWebpackPlugin({
          template: "./public/index.html",
          filename: "index.html", //打包后的文件名
          minify: {
            removeAttributeQuotes: false, //是否删除属性的双引号
            collapseWhitespace: false, //是否折叠空白
            config: {
              title: "webpack",
            },
          },
          // hash: true //是否加上hash,默认是 false
        }),
      ],
    };
    

HtmlWebpackPlugin 插件配置可以在 html 中用 htmlWebpackPlugin.options 来访问,需要用 ejs 模板语法来写:

<div><%= (htmlWebpackPlugin.options.config.title) %></div>
<div><%= (htmlWebpackPlugin.options.filename) %></div>

自定义 chunk 引入位置

使用 HtmlWebpackPlugin 插件处理 html 时,会根据 inject 配置来决定要将 chunk 插入到 html 的什么位置。

inject 的可选值为:

  • true:script 标签位于 html 文件的 body 底部。
  • false:不插入生成的 js 文件。
  • 'body':script 标签位于 html 文件的 body 底部。
  • 'head':script 标签位于 html 文件的 head 底部。

如果 'body''head' 不满足实际需求而你想要完全控制你的所有资源的话,可以将 inject 设置为 false,然后手动在 html 文件中引入 chunk。

  • 配置

在 ejs 模板中可以通过 htmlWebpackPlugin.files 直接访问编译时生成的文件:

publicPath: string;
js: string[];
css: string[];
manifest?: string;
favicon?: string;

ejs 模板中添加如下代码插入 css:

<% for (var chunk in htmlWebpackPlugin.files.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[chunk] %>" />
<% } %>

ejs 模板中添加如下代码插入 script:

<% for (var chunk in htmlWebpackPlugin.files.js) { %>
<script defer src="<%=htmlWebpackPlugin.files.js[chunk]%>"></script>
<% } %>

多入口时指定注入的 chunk

webpack 会根据入口配置生成若干个 chunk,使用 HtmlWebpackPlugin 插件处理 html 时,默认情况下会给每一个页面都引入所有的 chunk。

而多页应用中我们希望每个页面只应用自己的 chunk 和公共的 chunk,这个需求可以通过 HtmlWebpackPlugin 的 chunks 配置来实现。

  • 配置

假设项目中有两个页面:home.html 和 order.html,目录结构如下:

- src
  - page
    - home
      - index.js
      - index.html
    - order
      - index.js
      - index.html

webpack 配置如下:

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: {
    home: __dirname + "/src/page/home/index.js",
    order: __dirname + "/src/page/order/index.js",
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "首页",
      filename: "home.html",
      template: __dirname + "/src/page/home/index.html",
      chunks: ["home"],
    }),
    new HtmlWebpackPlugin({
      title: "订单",
      filename: "order.html",
      template: __dirname + "/src/page/order/index.html",
      chunks: ["order"],
    }),
  ],
};

因为有两个入口,所以会生成 home、order 两个 chunk,在 HtmlWebpackPlugin 的配置中加入 chunks 配置,指定要引入的 chunk 名称,就可以避免在每个页面中引入所有 chunk。

处理样式

webpack 不能直接处理 css,需要借助 loader。如果是 .css,我们需要的 loader 通常有: style-loader、css-loader,考虑到兼容性问题,还需要 postcss-loader。

而如果是 less 或者是 sass 的话,还需要 less-loader 和 sass-loader,这里配置一下 less 和 css 文件(sass 的话,使用 sass-loader 即可):

  • 安装
npm install style-loader less-loader css-loader postcss-loader autoprefixer less -D
  • 配置
//webpack.config.js
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.(le|c)ss$/,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              plugins: function () {
                return [
                  require("autoprefixer")({
                    overrideBrowserslist: [">0.25%", "not dead"],
                  }),
                ];
              },
            },
          },
          "less-loader",
        ],
        exclude: /node_modules/,
      },
    ],
  },
};

style-loader 动态创建 style 标签,将 css 插入到 head 中。

css-loader 负责处理 @import 等语句。

postcss-loader 和 autoprefixer,自动生成浏览器兼容性前缀。

less-loader 负责处理编译 .less 文件,将其转为 css。

抽离 css:mini-css-extract-plugin

将 css 抽离到单独文件中,extract-text-webpack-plugin 插件也有同样的功能,相比而言 mini-css-extract-plugin 更好。

抽离 css 可以减少单个模块的大小,但是页面会增加新的 http 请求。

  • 安装
npm install mini-css-extract-plugin -D
  • 配置
//webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  //...
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(c|le)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              reloadAll: true, // 解决开发环境修改css后页面不刷新问题
            },
          },
          "css-loader",
          "less-loader",
        ],
        exclude: /node_modules/,
      },
    ],
  },
};

图片/文件处理:url-loader

  • 安装
# 如果提示需要安装 file-loader 则执行 npm install file-loader -D
npm install url-loader -D
  • 配置
module.exports = {
  modules: {
    rules: [
      {
        test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 10240, // 小于 10K 时将资源编译为 base64,超过 10K 时将文件拷贝到输出目录
              esModule: false, // esModule 设置为 false,否则,<img src={require('XXX.jpg')} /> 会出现 <img src=[Module Object] />
              name: '[name]_[hash:6].[ext]', // 修改编译后的资源文件名
              outpath: 'assets', // 修改输出目录
            },
          },
        ],
      },
    ];
  }
}

注入全局变量

使用 ProvidePlugin 注入的全局变量可以在项目中的所有地方使用。

export default 导出的模块(vue.esm.js)必须要指定 default

module.exports 导出的模块(react)不需要写 default

  • 配置
const webpack = require("webpack");
module.exports = {
  //...
  plugins: [
    new webpack.ProvidePlugin({
      React: "react",
      Component: ["react", "Component"],
      Vue: ["vue/dist/vue.esm.js", "default"],
      $: "jquery",
      _map: ["lodash", "map"],
    }),
  ],
};

如果启动了 eslint 的话需要修改 eslint 的配置:

{
  "globals": {
    "React": true,
    "Vue": true
    //....
  }
}

多页应用配置

开发多页应用时需要配置多个入口和 HtmlWebpackPlugin。

配置多个 HtmlWebpackPlugin 时 filename 字段不可缺省,否则默认生成的都是 index.html,同时需要配置 chunks 字段,指定每个页面需要引入的 chunk。

//webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: {
    index: "./src/index.js",
    login: "./src/login.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[hash:6].js",
  },
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
      filename: "index.html", //打包后的文件名
      chunks: ["index"],
      excludeChunks: ["jquery"], // 指定不想引入的 chunk
    }),
    new HtmlWebpackPlugin({
      template: "./public/login.html",
      filename: "login.html", //打包后的文件名
      chunks: ["login"],
    }),
  ],
};

路径别名

  • 配置
//webpack.config.js
module.exports = {
  //....
  resolve: {
    alias: {
      "@": "src",
    },
  },
};

配置模块默认后缀

  • 配置
//webpack.config.js
module.exports = {
  //....
  resolve: {
    extensions: [".json", ".js"],
  },
};

寻找模块时从左到右遍历后缀列表,因此最好将频率最高的后缀放在第一位,并且控制列表的长度,以减少尝试次数。

经过上面配置后,对于 import ('../dialog') 导入语句来说,会先寻找 ../dialog.json 然后再寻找 ../dialog.js

生产环境

打包前清空 dist 目录:clean-webpack-plugin

  • 安装
npm install clean-webpack-plugin -D
  • 配置
//webpack.config.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ["**/*", "!dll", "!dll/**"], // 不删除dll目录下的文件
    }),
  ],
};

静态资源拷贝:copy-webpack-plugin

  • 安装
npm install copy-webpack-plugin -D
  • 配置
//webpack.config.js
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
module.exports = {
  //...
  plugins: [
    new CopyWebpackPlugin(
      [
        {
          from: "public/js",
          to: path.resolve(__dirname, "dist", "js"),
        },
        //还可以继续配置其它要拷贝的文件
      ],
      {
        ignore: ["other.js"], // 可设置要忽略的文件
      }
    ),
  ],
};

压缩 css:optimize-css-assets-webpack-plugin

  • 安装
npm install optimize-css-assets-webpack-plugin -D
  • 配置
//webpack.config.js
const OptimizeCssPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  //....
  plugins: [new OptimizeCssPlugin()],
};

开发环境

搭建开发环境:webpack-dev-server

  • 安装
npm install webpack-dev-server -D

npm install cross-env -D
  • 配置
// package.json
{
  "scripts": {
    "serve": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
  }
}

热更新

  1. 首先配置 devServer 的 hot 为 true。
  2. 在 plugins 中增加 new webpack.HotModuleReplacementPlugin()。
//webpack.config.js
const webpack = require("webpack");
module.exports = {
  //....
  devServer: {
    hot: true,
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(), //热更新插件
  ],
};

我们配置了 HotModuleReplacementPlugin 之后,会发现,此时我们修改代码,仍然是整个页面都会刷新。不希望整个页面都刷新,还需要修改入口文件:

if (module && module.hot) {
  module.hot.accept();
}

配置代理实现跨域

假设前端在 3000 端口,服务端在 4000 端口,可以通过配置代理来实现跨域。

  • 配置
//webpack.config.js
module.exports = {
  //...
  devServer: {
    proxy: {
      "/api": "http://localhost:4000",
    },
  },
};

配置 mock 数据:mocker-api

开发时如果有 mock 接口可以提高对接效率,如果后端没有提供,可以使用 mocker-api 自己配置。

  • 安装
npm install mocker-api -D
  • webpack 配置
// webpack.config.dev.js
const apiMocker = require("mocker-api");
module.export = {
  //...
  devServer: {
    before(app) {
      apiMocker(app, path.resolve("./mock/mocker.js"));
    },
  },
};
  • /mock/mocker.js 配置
module.exports = {
  "GET /user": { name: "刘小夕" },
  "POST /login/account": (req, res) => {
    const { password, username } = req.body;
    if (password === "888888" && username === "admin") {
      return res.send({
        status: "ok",
        code: 0,
        token: "sdfsdfsdfdsf",
        data: { id: 1, name: "刘小夕" },
      });
    } else {
      return res.send({ status: "error", code: 403 });
    }
  },
};

在调用对应接口时会返回 mock 数据:

fetch("/login/account", {
  method: "POST",
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    username: "admin",
    password: "888888",
  }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((err) => console.log(err));

映射源代码

经过 webpack 打包之后在浏览器中查看源代码看到的是压缩编译后的代码,不方便调试,可以通过配置 devtool 来将编译后的代码映射会原始代码。

//webpack.config.js
module.exports = {
  devtool: "cheap-module-eval-source-map", //开发环境下使用
};

参考

带你深度解锁 Webpack 系列(基础篇)open in new window

带你深度解锁 Webpack 系列(进阶篇)open in new window

带你深度解锁 Webpack 系列(优化篇)open in new window

HTML Webpack Pluginopen in new window