花时间补下webpack吧 - 1 【🏃🏻‍♀️学习打卡ing】

·  阅读 81

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 1 天,点击查看活动详情

花点时间补一下 webpack 的知识点吧,我想做前端的同学都知道,它是个打包工具,平时的项目中也会看到一些基础的配置,像 entry,output,mode,module,plugin 等等,但通常自己可能不太去深入,为什么要这么配置,为什么要配置这个参数,这个 plugin/loader,或者说这个 plugin 和这个 loader 里面又是如何达到我们想要的打包效果。当然还有使用webpack的优化呀,之类的。

从这篇文章开始理一下有关 webpack 的知识点,我可能是看别的大佬写的文章总结,也可能是看一些有关 webpack 的书籍,又或者是官方文档(这样能避免漏掉新的知识)...学习记录过程和来源可能多种,但是希望最后能学到点东西吧。

第一篇主要是理一下webpack最基础的配置,整理一下

webpack 基本配置

1. entry | output | mode

1.1 entry

entry是配置模块的入口,可抽象成输入,Webpack 执行构建的第一步将从入口开始搜寻及递归解析出所有入口依赖的模块。

entry 配置是必填的,若不填则将导致 Webpack 报错退出。

官网提供entry的类型有二种:entry

类型例子含义
string'./app/entry' (main: './app/entry')入口模块的文件路径,可以是相对路径。
object{ a: './app/entry-a', b: ['./app/entry-b1', './app/entry-b2']}配置多个入口,每个入口生成一个 Chunk

1.2 output

output 配置如何输出最终想要的代码。output 是一个 object,里面包含一系列配置项,下面分别介绍它们。配置 output 选项可以控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。

1.2.1 单入口:

最基本的配置:

  • filename 用于输出文件的文件名。
  • 目标输出目录 path 的绝对路径。

webpack.config.js

const config = {
  output: {
    filename: "bundle.js", // 输出文件名
    path: path.join(__dirname, "dist"), // 输出文件目录
  },
};

module.exports = config;
复制代码

此配置将一个单独的 bundle.js 文件输出到 dist 目录中。

截屏2022-04-01 上午10.28.04.png

1.2.2 多个入口起点

如果配置创建了多个单独的 "chunk"(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用占位符(substitutions)来确保每个文件具有唯一的名称。

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}

// 写入到硬盘:./dist/app.js, ./dist/search.js
复制代码

1.3 mode 提供 mode 配置选项,告知 webpack 使用相应模式的内置优化。

string = 'production': 'none' | 'development' | 'production'

  • development 开发模式,打包更加快速,省了代码优化步骤
  • production 生产模式,打包比较慢,会开启 tree-shaking 和 压缩代码
  • none 不使用任何默认优化选项

先初始化 npm init

再本地先安装一下 webpack 和 webpack-cli

+ webpack-cli@4.9.2
+ webpack@5.70.0
复制代码

直接执行 npx webpack

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
复制代码

我们没有配置 mode(模式),这里提醒我们配置一下

配置:

webpack.config.js:

const path = require("path");

module.exports = {
  mode: "development", // 模式
  entry: "./src/index.js", // 打包入口地址
  output: {
    filename: "bundle.js", // 输出文件名
    path: path.join(__dirname, "dist"), // 输出文件目录
  },
};
复制代码

我们尝试配置打包的入口和出口。

2. loader

loader 用于对模块的源代码进行转换。

  • loader 可以使你在 import 或"加载"模块时预处理文件。
  • loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,
  • loader 可以将内联图像转换为 data URL。
  • loader 可以允许你直接在 JavaScript 模块中 import CSS文件

这边直接举例子:loader转换css

当我们使用.css 文件配置入口出口会怎么样呢?

const path = require("path");

module.exports = {
  mode: "development", // 模式
  entry: "./src/main.css", // 打包入口地址
  output: {
    filename: "bundle.css", // 输出文件名
    path: path.join(__dirname, "dist"), // 输出文件目录
  },
};
复制代码

运行下 npx webpack

ERROR in ./src/main.css 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> body {
|   margin: 0 auto;
|   padding: 0 20px;

webpack 5.70.0 compiled with 1 error in 69 ms
复制代码

报错了这是因为:webpack 默认支持处理 JS 与 JSON 文件,其他类型都处理不了,这里必须借助 Loader 来对不同类型的文件的进行处理。

首先安装 css-loader 来处理 css npm install css-loader -D

安装成功后添加配置模块

module: {
  rules: [
    // 转换规则
    {
      test: /\.css$/, //匹配所有的 css 文件
      use: "css-loader", // use: 对应的 Loader 名称
    },
  ];
}
复制代码

重新运行打包 npx webpack

asset bundle.css 8.62 KiB [emitted] (name: main)
runtime modules 937 bytes 4 modules
cacheable modules 2.83 KiB
  ./src/main.css 521 bytes [built] [code generated]
  ./node_modules/css-loader/dist/runtime/noSourceMaps.js 64 bytes [built] [code generated]
  ./node_modules/css-loader/dist/runtime/api.js 2.26 KiB [built] [code generated]
webpack 5.70.0 compiled successfully in 281 ms
复制代码

这里我们可以得到一个结论:Loader 就是将 Webpack 不认识的内容转化为认识的内容

当然正常情况下我们不会使用 css 作为入口文件

module.exports = {
  mode: "development", // 模式
  entry: "./src/index.js", // 打包入口地址
  output: {
    filename: "bundle.js", // 输出文件名
    path: path.join(__dirname, "dist"), // 输出文件目录
  },
  module: {
    rules: [
      // 转换规则
      {
        test: /\.css$/, //匹配所有的 css 文件
        use: "css-loader", // use: 对应的 Loader 名称
      },
    ],
  },
};
复制代码

3. plugin 插件

3.1. html-webpack-plugin

与 Loader 用于转换特定类型的文件不同,插件(Plugin)可以贯穿 Webpack 打包的生命周期,执行不同的任务

新建 src/index.html 文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ITEM</title>
  </head>
  <body></body>
</html>
复制代码

如果我想打包后的资源文件,例如:js 或者 css 文件可以自动引入到 Html 中,就需要使用插件 html-webpack-plugin来帮助你完成这个操作

首先安装 npm install html-webpack-plugin -D

在 webpack.config.js 中配置:

const HtmlWebpackPlugin = require("html-webpack-plugin");

...

plugins: [
  // 配置插件
  new HtmlWebpackPlugin({
    template: "./src/index.html",
  }),
];
复制代码

运行一下打包,打开 dist 目录下生成的 index.html 文件,可以看到它自动的引入了打包好的 bundle.js .

3.2. clean-webpack-plugin

每次打包的时候,打包目录都会遗留上次打包的文件,为了保持打包目录的纯净,我们需要在打包前将打包目录清空,可以使用 clean-webpack-plugin 插件

首先还是安装一下:

npm install clean-webpack-plugin -D

再引入并且配置一下

const HtmlWebpackPlugin = require("html-webpack-plugin");
// 引入插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

....

plugins: [
    // 配置插件
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new CleanWebpackPlugin(), // 引入插件
  ],
复制代码

3.3 . cross-env

区分环境, 本地开发和部署线上,肯定是有不同的需求

3.3.1 本地环境:

需要更快的构建速度 需要打印 debug 信息 需要 live reload 或 hot reload 功能 需要 sourcemap 方便定位问题 ...

3.3.2 生产环境:

需要更小的包体积,代码压缩+tree-shaking 需要进行代码分割 需要压缩图片体积 ...

针对不同的需求,首先要做的就是做好环境的区分

  • 首先安装 npm install cross-env -D

  • 配置运行命令:

"scripts": {
    "dev": "cross-env NODE_ENV=dev webpack serve --mode development",
    "test": "cross-env NODE_ENV=test webpack --mode production",
    "build": "cross-env NODE_ENV=prod webpack --mode production"
  },
复制代码
  • 在 webpack 配置文件中获取环境变量
module.exports = (env, argv) => {
  console.log("argv.mode=", argv.mode); // 打印 mode(模式) 值
  // 这里可以通过不同的模式修改 config 配置
  return config;
};
复制代码

执行 npm run build

process.env.NODE_ENV = prod;
argv.mode = production;
复制代码

执行 npm run test

process.env.NODE_ENV = test;
argv.mode = production;
复制代码

执行 npm run dev

process.env.NODE_ENV = dev;
argv.mode = development;
复制代码

7. 启动 devServer

首先安装 npm intall webpack-dev-server@3.11.2 -D

配置:

devServer: {
    contentBase: path.resolve(__dirname, "public"), // 静态文件目录
    compress: true, //是否启动压缩 gzip
    port: 8080, // 端口号
    // open:true  // 是否自动打开浏览器
  },
复制代码

为什么要配置 contentBase ? 因为 webpack 在进行打包的时候,对静态文件的处理,例如图片,都是直接 copy 到 dist 目录下面。但是对于本地开发来说,这个过程太费时,也没有必要,所以在设置 contentBase 之后,就直接到对应的静态目录下面去读取文件,而不需对文件做任何移动,节省了时间和性能开销。

8. 引入 css

上面我们使用了 css-loader 去处理 css 文件,但是单靠 css-loader 是没办法将样式加载到页面上的,我们还需要 style-loader

style-loader 的功能就是将加载好的 css 展示到页面上。

首先安装:npm install style-loader -D

src/index.js

import "./main.css";

const a = "Hello ITEM";
console.log(a);
module.exports = a;
复制代码

src/main.css

body {
  margin: 10px auto;
  background: cyan;
  max-width: 800px;
}
复制代码

重新运行下 npm run dev

9. css 兼容

使用 postcss-loader,自动添加 CSS3 部分属性的浏览器前缀

src/main.css

 margin: 10px auto;
  background: cyan;
  max-width: 800px;
  /* 新增 */
  font-size: 46px;
  font-weight: 600;
  color: white;
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
复制代码

安装 npm install postcss-loader postcss -D

use: ["style-loader", "css-loader", "postcss-loader"];
复制代码

10. 分离样式文件

前面,我们都是依赖 style-loader 将样式通过 style 标签的形式添加到页面上

但是,更多时候,我们都希望可以通过 CSS 文件的形式引入到页面上 mini-css-extract-plugin

安装 npm install mini-css-extract-plugin -D


// 引入插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{
    test: /\.css$/, //匹配所有的 css 文件
    use: [
        "style-loader",
        MiniCssExtractPlugin.loader, // 添加 loader
        "css-loader",
        "postcss-loader",
        //  Loader 的执行顺序是固定从后往前,即按 css-loader --> style-loader 的顺序执行
    ],
},
复制代码

重新打包后,dist 目录下会新增一个 main.9b73d94f.css文件

body {
  margin: 10px auto;
  background: rgb(209, 235, 235);
  max-width: 800px;
  /* 新增 */
  font-size: 46px;
  font-weight: 600;
  color: white;
  position: fixed;
  left: 50%;
  -webkit-transform: translateX(-50%);
  transform: translateX(-50%);
}
复制代码

11. 图片和字体文件

虽然上面在配置开发环境的时候,我们可以通过设置 contentBase 去直接读取图片类的静态文件,看一下下面这两种图片使用情况

  • 页面直接引用
<!-- 本地可以访问,生产环境会找不到图片 -->
<img src="/logo.png" alt="">
复制代码
  • 背景图引用
<div id="imgBox"></div>
复制代码
/* ./src/main.css */
#imgBox {
  height: 400px;
  width: 400px;
  background: url("../public/logo.png");
  background-size: contain;
}
复制代码

所以实际上,Webpack 无法识别图片文件,需要在打包的时候处理一下 常用的处理图片文件的 Loader 包含:

  • file-loader 解决图片引入问题,并将图片 copy 到指定目录,默认为 dist
  • url-loader 解依赖 file-loader,当图片小于 limit 值的时候,会将图片转为 base64 编码,大于 limit 值的时候依然是使用 file-loader 进行拷贝
  • img-loader 压缩图片

12. JS 兼容性 babel

在开发中我们想使用最新的 Js 特性,但是有些新特性的浏览器支持并不是很好,所以 Js 也需要做兼容处理,常见的就是将 ES6 语法转化为 ES5。

安装: npm install babel-loader @babel/core @babel/preset-env -D

  • babel-loader 使用 Babel 加载 ES2015+ 代码并将其转换为 ES5
  • @babel/core Babel 编译的核心包
  • @babel/preset-env Babel 编译的预设,可以理解为 Babel 插件的超集
{
    test: /\.js$/i,
    use: [
        {
        loader: 'babel-loader',
        options: {
        presets: [
          '@babel/preset-env'
          ],
        }
      }
    ]
},
复制代码

为了避免 webpack.config.js 太臃肿,建议将 Babel 配置文件提取出来

根目录下新增 .babelrc.js

13. SourceMap 配置选择

13.1 介绍

由于在开发过程中经常会使用新语言去开发项目,最后会把源码转换成能在浏览器中直接运行的 JavaScript 代码。 这样做虽能提升开发效率,在调试代码的过程中你会发现生成的代码可读性非常差,这给代码调试带来了不便。

Webpack 支持为转换生成的代码输出对应的 Source Map 文件,以方便在浏览器中能通过源码调试。 控制 Source Map 输出的 Webpack 配置项是 devtool

此选项控制是否生成,以及如何生成 source map。

使用 SourceMapDevToolPlugin 进行更细粒度的配置。查看 source-map-loader 来处理已有的 source map。

13.2 该如何选择哪种sourceMap

Devtool 配置项提供的这么多选项看似简单,但很多人搞不清楚它们之间的差别和应用场景。

如果你不关心细节和性能,只是想在不出任何差错的情况下调试源码,可以直接设置成 source-map,但这样会造成两个问题:

  • source-map 模式下会输出质量最高最详细的 Source Map,这会造成构建速度缓慢,特别是在开发过程需要频繁修改的时候会增加等待时间;
  • source-map 模式下会把 Source Map 暴露出去,如果构建发布到线上的代码的 Source Map 暴露出去就等于源码被泄露;

为了解决以上两个问题,可以这样做:

  • 在开发环境下把 devtool 设置成 cheap-module-eval-source-map,因为生成这种 Source Map 的速度最快,能加速构建。由于在开发环境下不会做代码压缩,Source Map 中即使没有列信息也不会影响断点调试;
  • 在生产环境下把 devtool 设置成 hidden-source-map,意思是生成最详细的 Source Map,但不会把 Source Map 暴露出去。由于在生产环境下会做代码压缩,一个 JavaScript 文件只有一行,所以需要列信息。

在生产环境下通常不会把 Source Map 上传到 HTTP 服务器让用户获取,而是上传到 JavaScript 错误收集系统,在错误收集系统上根据 Source Map 和收集到的 JavaScript 运行错误堆栈计算出错误所在源码的位置。

不要在生产环境下使用 inline 模式的 Source Map, 因为这会使 JavaScript 文件变得很大,而且会泄露源码。

SourceMap 是一种映射关系,当项目运行后,如果出现错误,我们可以利用 SourceMap 反向定位到源码位置

devtool: 'source-map',
复制代码

重新打包,dist 目录下有一个 bundle.js.map 的文件

截屏2022-04-01 下午2.36.34.png

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const config = {
  mode: "development", // 模式
  entry: "./src/index.js", // 打包入口地址
  output: {
    filename: "bundle.js", // 输出文件名
    path: path.join(__dirname, "dist"), // 输出文件目录
  },
  devServer: {
    contentBase: path.resolve(__dirname, "public"), // 静态文件目录
    compress: true, //是否启动压缩 gzip
    port: 8080, // 端口号
    // open:true  // 是否自动打开浏览器
  },
  devtool: "source-map",
  module: {
    rules: [
      // 转换规则
      {
        test: /\.css$/, //匹配所有的 css 文件
        use: [
          MiniCssExtractPlugin.loader, // 添加 loader
          "style-loader",
          "css-loader",
          "postcss-loader",
        ],
      },
    ],
  },
  resolve: {
    extensions: [".js", ".json"],
  },
  plugins: [
    // 配置插件
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new CleanWebpackPlugin(), // 引入插件
    new MiniCssExtractPlugin({
      // 添加插件
      filename: "[name].[hash:8].css",
    }),
  ],
};

module.exports = (env, argv) => {
  return config;
};

复制代码

我是婧大,一名前端小学崽,希望和你一起学习一起进步。🙆🙆🙆

加油!wx:lj18379991972 欢迎👏🏻一起交流学习

文章肯定有写的不好的地方,可以评论区指正❤。

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改