webpack5基础

120 阅读10分钟

自用记录文章 持续更新

为甚要用打包工具

在开发的时候,我们会用框架、es6模块化语法、less/sass等css预处理器

这样的代码必须要编译成浏览器能识别的css或者js语法,才能运行

所以我们需要打包工具

而且打包工具还能压缩代码、做兼容性处理、提升代码性能(把体积小的图片转化成base54格式减少请求,减轻服务器的压力)

简单介绍

webpack是一个静态资源打包文件 它会以一个或者几个文件作为打包入口,将整个项目所有文件编译组合成一个或者多个文件输出

输出的文件就可以在浏览器中运行了

功能介绍

webpack本身功能是有限的 ++但是它可以使用loader或者plug拓展功能++

  • 开发模式: 仅能编译js中的ES Moudle语法
  • 生产模式:能xxx还能压缩js代码

简单使用

npm init -y 初始化一个package.json文件

如果没有-y那么后端会有很多选项 使用-y那么表示后续所有的选项都为yes

npm i webpack webpack-cli -D 下载webpackweebpack-cli到生产环境

webpack、webpack-cli在实际开发过程中都是生产依赖

启动webpack npx webpack ./src/main.js --mode=development npx webpack ./src/main.js --mode=production

基本配置

五大核心概念

  1. entry(入口)
  2. output(输出)
  3. loader (加载器)
  4. plugins(插件)
  5. mode (模式)

准备webpack配置文件

在项目根目录下创建webpack.config.js

// Node.js的核心模块,专门用来处理文件路径
const path = require("path");

module.exports = {
  // 入口
  // 相对路径和绝对路径都行
  entry: "./src/main.js",
  // 输出
  output: {
    // path: 文件输出目录,必须是绝对路径
    // path.resolve()方法返回一个绝对路径
    // __dirname 当前文件的所在文件夹绝对路径
    path: path.resolve(__dirname, "dist"),
    // filename: 输出文件名
    filename: "main.js",
    clean: true, // 自动将上次打包目录资源清空
  },
  // 加载器
  module: {
    rules: [],
  },
  // 插件
  plugins: [],
  // 模式
  // development和production webpack会默认配置很多模式
  mode: "development", // 开发模式
};

src/math/sum.js

export default function sum(...args) {
  return args.reduce((p,c) => {
    return p + c
  },0)
}

main.js

import sum from './math/sum.js';

console.log(123);
console.log(sum(1,2,3))

public/index.html

<body>
  <div id="ly">liuyang</div>
</body>
<!-- 导入打包后的main.js文件 -->
<script src="../dist/main.js"></script>

npx webpack执行 可以看到dist文件下有一个以及打包好的main.js

处理各种资源

处理样式资源

webpack本身不能处理样式资源()比如css、less、sass或者scss、styl 但是可以使用Loader来帮助webpack解析样式资源

  1. 处理css资源
  • 安装css-loader(将css资源编译成commonjs模块到js中)
  • 安装style-loader(通过动态创建style标签 将js中的css添加到html文件中) ++加载css文件有两种方式 1.生成style标签然后把css样式放进去可以通过css.loader2.生成.css文件然后通过link标签引入到html文件中,后续使用一个插件++
webpack.config.js
module: {
    rules: [
        {
            test: /\.css$/, // 只检测以.css结尾的文件
            use: [ // 执行顺序:下载后面的先执行 
                'style-loader',
                'css-loader'
            ]
        }
    ]
}
 //一定要记得引入dist/main.js 中
    <body>
      <div class="box"></div>
      <script src="../dist/main.js"></script>
    </body>

这里对use说明一下 方便理解后续不同的写法

    // 语法糖
    // loader: [
    //   'style-loader',
    //   'css-loader'
    // ]
    // 如果只有一个loader 那么
    // loader: 'style-loader'

    // 完整版
    // use: [
    //     {
    //       loader: 'style-loader',
    //       option: {
    //       }
    //     },
    //     {
    //       loader: 'css-loader',
    //       option: {
            
    //       }
    //     }
    //  ]

    // 完成版简写
    // use: [
    //   'style-loader',
    //   'css-loader'
    // ]
  1. 处理less文件
  • 安装less-loader

    这个less-loader实际是用lessc(less-componier)把less文件转化成css文件

  • 安装css-loader
  • 安装style-loader
 rules: [
    {
        test: /\.less$/,
        use:[
            "style-loader",
            "css-loader",
            "less-loader"
        ]
    }
 ]

处理图片资源

webpack4中处理图片资源要安装file-loader和url-loader插件,webpack5已经将这两个功能内置了(webpack5中叫做资源模块类型),只用简单配置就好了

资源模块类型

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体 积限制实现

1.配置

webpack.config.js
mudule: {
    rules: [
          {
            test: /\.(png|jpe?g|gif|webp)$/,
            type: "asset",
          }
    ]
}

2.添加图片资源

css样式中引入img e.g. background-image: url("../images/1.jpeg");

3.可以看到dist文件下有一张图片

注意如果图片大小很小(具体多少还不知道)会被webpack5转化成dataUrl dataUrl是在dist文件下看不到的

dist文件下为什么没有样式资源呢? 因为经过 style-loader 的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来

4.优化图片

  1. 将小于某个大小的图片转化成 data URI 形式(Base64 格式)
  2. 设置打包后图片的文件名
    module: {
        rules: [
            {
                test: /\.(png|jpe?g|gif|webp)$/, //以png或jpg或jpeg或gif或webp结尾文件
                type: "asset",
                
                // 对图片进行转base64格式
                parser: {
                    dataUrlCondition: {
                        // 将小于10kb的图片转为base64
                        // 优点:减少请求数量 减轻服务器的压力 缺点:体积会变大点(但是小图片变大一点也不大)
                        maxSize: 10 * 1024
                    }
                },
                generator: {
                    // 指定输出的图片名称
                    // hash: 10代表hash值只取前10位
                    // ext 文件后缀
                    // query 请求url的query
                    filename: 'static/images/[hash:10][ext][query]'
                }
            }
        ]
    }

处理其他资源(字体、音频、视频)

包括 将打包后的文件分类

      {
        test: /\.(ttf|woff2?|map4|map3|avi)$/,
        type: 'asset/resource', //原样输出
        generator: {
          filename: 'static/media/[hash:10][ext][query]'
        }
      }

使打包后的文件分类输出

output:{
// filename 代表入口文件输出的文件名
    filename: 'xxx/xxx/main.js'
}

进一步处理js资源

webpack自己对js的处理是有限的,只能编译js中ES模块化语法,导致js不能在IE浏览器中运行 --可以使用Babel

其次在开发中,要保持团队代码一致性,我们要用专业工具来检测 --可以使用Eslint

Eslint

1.配置文件

eslint的配置文件有很多写法 注意文件要位于根目录

  1. eslintrc
  2. .eslintrc.js
  3. .eslintrc.json

或者 package.json中添加eslintConfig属性

2.具体配置

以.eslintrc.js文件为例

module.exports = {
  // 解析选项
  parserOptions: {},
  // 具体检查规则
  rules: {},
  // 继承其他规则
  extends: [],
  // ...
  // 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
  1. 解析选项说明

解析选项配置不对 那么打包的时候 控制台会对import以及export语法进行提示

"env": { "es6": true } // 启动es6全局变量 不确定啥是全局变量

<!--设置解析器选项能帮助 ESLint 确定什么是解析错误,所有语言选项默认都是 false。-->
parserOptions: {
    ecmaVersion: 6, // 默认情况下,ESLint 支持 ECMAScript 5 语法 这里是启动es6语法 但是不能使用es6全局变量
    sourceType: "module", // ES 模块化
    ecmaFeatures: { // 可以设置额外的语言特性
      jsx: true // 如果是 React 项目,就需要开启 jsx 语法
    }
}
  1. rules规则
  • "off" 或 0 - 关闭规则
  • "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出) e.g. semi: "error", // 禁止使用分号
  1. extends继承
// 例如在React项目中,我们可以这样写配置
module.exports = {
  extends: ["react-app"],
  rules: {
    // 我们的规则会覆盖掉react-app的规则
    // 所以想要修改规则直接改就是了
    eqeqeq: ["warn", "smart"],
  },
};

3.使用

下载npm i eslint-webpack-plugin eslint -D

--save-dev 开发依赖 等同于 -D --save 运行时依赖 等同于 -S

webpack.config.js文件中引入

const ESLintWebpackPlugin = require("eslint-webpack-plugin");

  plugins: [
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, "src"),
      // 打包的时候如果你写的源文件出现格式校验不通过,那么会自动进行fix
      fix: true, 
    }),
  ],

如果有eslint插件 ..... 不用等到编译的时候才报错 会在编写代码的同时去检查你的代码 配合prettier可以自动修复不符合eslint规范的代码

忽略某些文件(这些文件下的出现的错误不报) 默认应该有

创建.eslintignore.js 文件

# 忽略dist目录下所有的文件
dist

Babel

是JavaScript 编译器。编译器包括几个阶段【解析、转换、生成阶段】

主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中

1.配置文件

同eslint相似

2. 具体配置

  1. presets预设 一组babel插件 扩展babel功能

以.babelrc.js 为例 一般是用配置文件 否则就要写在webpake.config.json中 这样很难看

module.exports = {
  // 预设
  presets: [],
};

3.在 Webpack 中使用

npm i babel-loader @babel/core @babel/preset-env -D

.bablerc.js
module.exports = {
  presets: ["@babel/preset-env"],
};
      {
        test: /\.js$/,
        exclude: /node_modules/, // 排除node_modules代码不编译
        loader: "babel-loader",
      },

打开打包后的 dist/static/js/main.js 文件查看,会发现箭头函数等 ES6 语法已经转换了

处理html资源

1.下载包

npm i html-webpack-plugin -D

2.配置

webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin"); 该插件会在dist文件下创建一个index.html文件, 该插件默认有一个模板。如果想要以自己的模板来创建文件 需要传入一个对象 如下:

  plugins: [
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件(在dist下)有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源 自动生成<script defer src="js/main.js"></script>
      template: path.resolve(__dirname, "public/index.html"),
    }),
  ],

title

3. 修改 index.html

<!-- 导入打包后的main.js文件 ( 有了HtmlWebpackPlugin插件 就无需手动进行引入)--> 
<!-- <script src="../dist/main.js"></script> -->

补充

模板文件中可能存在一些变量,如下面(取至vue-cli的public)的<%= BASE_URL %>favicon.ico<%= htmlWebpackPlugin.options.title %> 可以使用插件

public/index.html文件
<!DOCTYPE html>
<html lang="">
  <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">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
    /**
     * DefinePlugin
     * 这里的插件用法 用来定义public/index.html 中<%= BASE_URL %> 的BASE_URL变量的值 
     * 当然也可以定义其他值
     */
    const {DefinePlugin} = require('webpack');
    new DefinePlugin({
      BASE_URL:'"./"'
    }),

//变量 <%= BASE_URL %>favicon.ico"> => ./favicon.ico

//至于 `<%= htmlWebpackPlugin.options.title %>` 表示的是htmlwebpackplugin 的参数 title: 'wb5'

    new HtmlWebpackPlugin({
      template: './public/index.html',
      /**
       * title 用来替代 htmlWebpackPlugin.options.title 这个变量
       */
      title: 'wb5'
    }),

开发服务器&自动化

每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化

两种方式 webpack watch mod 和 webpack-dev-server(常用)

1. webpakck watch mod

不会自动刷新浏览器 在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译 1. 在webpack的配置中,添加 watch: true 2. 在启动webpack的命令中,添加 --watch的标识 webpack --watch

2. webpack-dev-server(常用)

  1. 下载包 npm i webpack-dev-server -D

  2. 配置

    module.exports = {
          // 开发服务器
          devServer: {
            // 启动服务器的域名/ip e.g. 0.0.0.0回环地址 在同一个网段下的电脑都可以访问
            host: "localhost",
            // 启动服务器端口号
            port: "3000", 
            // 是否自动打开浏览器
            open: true, 
            // 是否开启静态资源gzip 默认false
            开启以后会对js文件压缩不会对html文件压缩,开启gzip以后content-coding=gzip
            compress: true  
          },
        }
    

    3.运行

    npx webpack serve

开发服务器没有输出 代码编译打包是在内存中进行的,不会输出到dist目录下 e.g. 实际上是是放在了webpack-dev-serve的一个memfs 文件服务(express写的)

为啥没有输出? 因为如果有输出的到文件的话,devServer服务器会去文件中读取内容到内存这一步非常耗时间,况且开发环境不需要输出 我们可以打包以后让内容直接展示在浏览器中open: true

devServer contentBase属性

用于配置 ++在开发阶段我们想要从额外的文件中获取资源++ 的路径, 比如: index.html中需要引用favorate.icon 这个时候开发环境又没有将资源打包进dist文件中,自然开发环境就不会出现icon,这个时候我们就可以配置contentBase属性 生产阶段我们是怎么做的呢? 生产阶段我们使用插件new CopyWebpackPlugin({from: to:}) 将指定文件复制到dist文件下,打比方 我们将favorate.icon 复制到dist文件下 打包以后 的index.html 依赖的icon就能拿到,即icon能显示 使用contentBase可以在开发环境优化打包速度

生产模式介绍

生产模式是开发完成代码后,我们需要得到代码将来部署上线。

这个模式下我们主要对代码进行优化,让其运行性能更好。

优化主要从两个角度出发:

优化代码运行性能 优化代码打包速度

生产模式准备

1. 文件目录

├── my-webpack (项目根目录)
    ├── config (Webpack配置文件目录)
    │    ├── webpack.dev.js(开发模式配置文件)
    │    └── webpack.prod.js(生产模式配置文件)
    ├── node_modules (下载包存放目录)
    ├── src (项目源码目录,除了html其他都在src里面)
    │    └── 略
    ├── public (项目html文件)
    │    └── index.html
    ├── .eslintrc.js(Eslint配置文件)
    ├── babel.config.js(Babel配置文件)
    └── package.json (包的依赖管理配置文件)

2. 修改 webpack.dev.js 以及 webpack.prod.js

  1. 需要修改绝对路径 这里就不贴代码了

  2. 开发模式不需要输出 也不需要打包之前删除上次打包的文件

 <!--webpack.dev.js-->
  output: {
    path: undefined, // 开发模式没有输出,不需要指定输出目录
    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
    // clean: true, // 开发模式没有输出,不需要清空输出结果
  },
  mode: "development"
  
  <!--webpack.prod.js-->
  output: {
    path: path.resolve(__dirname,'../dist'),
    filename: 'js/main.js',
    clean: true
  },
  mode: "production"
  1. 命令 npx webpack serve --config ./config/webpack.dev.js npx webpack --config ./config/webpack.prod.js

有点复杂 稍后我去package.json中配置

    "start": "npm run dev",
    "dev": "npx webpack serve --config ./config/webpack.dev.js",
    "build": "npx webpack --config ./config/webpack.pro.js",

进一步处理css资源

1. 提取css成单独文件

使用style-loader会使得出现闪屏现象 浏览器首先 解析html代码将结构渲染上去,然后解析js,样式是js解析完以后再加载的 我们应该通过link标签加载

style-loader是把 动态创建一个style标签把js中的css样式添加到html文件中 会出现闪屏现象 原因:浏览器先构建html js 然后加载样式

我们可以使用插件MiniCssExtractPlugin

1. 下载

npm i mini-css-extract-plugin -D

2. 配置

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// 替换
`style-loader` -> `MiniCssExtractPlugin.loader`

    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),

2. 兼容性处理

像flex 动画 等css样式在低版本的浏览器不能使用 用这个插件可以解决大多数样式问题

postcss是一个通过js来转化样式的工具 可以帮助我们进行css的转换和适配 实现这些功能也同样需要postcss对应的插件 比如postcss-preset-env

postcss-preset-env可以帮助我们将一些现代的css特性转化成以前浏览器能认识的css语法和加浏览器前缀

  • 比如 用16进制代表颜色 绝大多数浏览器支持#123456 但是现在css新语法可以支持8位后面两位代表透明度 如果想要所有浏览器适配的话就用可以用这个插件 e.g. 我们写color:#12345678-> color: rgba(18,52,86,0.47059);

1. 下载

npm i postcss-loader postcss postcss-preset-env -D

2. 配置

        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          <!-- 注意放的位置-->
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  "postcss-preset-env", // 能解决大多数样式兼容性问题
                ],
              },
            },
          },
          'sass-loader'
        ]

3. 控制兼容性

我们可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。

{ // 其他省略 "browserslist": ["ie >= 8"] }

实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:

{ // 其他省略 "browserslist": ["last 2 version", "> 1%", "not dead"] }

4. 合并配置

const getStyleLoader = (loader) => {
  return [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            "postcss-preset-env", // 能解决大多数样式兼容性问题
          ],
        },
      },
    },
    loader
  ];
};


  {
    test: /\.s[ac]ss$/,
    use: getStyleLoader('sass-loader')
  },

3. css压缩

1. 压缩包

npm i css-minimizer-webpack-plugin -D

2.配置

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");


    // css压缩
    new CssMinimizerPlugin(),

html压缩

默认生产模式已经开启了:html 压缩和 js 压缩

不需要额外进行配置