webpack5入门

开发相关版本:

  • webpack: 5.51.1版本

  • webpack-cli: 4.8.0版本

  • node: 14.10.0版本

1. 初始化项目 

npm init -y

注: -y的含义: yes, 在init的时候省去敲回车的步骤,生成的默认的package.json。 

接着安装webpack、webpack-cli

npm i -D webpack webpack-cli

注: npm i -D是npm install --save-dev的缩写, npm i -S是npm install --save的缩写

新建一个文件夹src,还是使用之前两个js:

greeting.js

export function greeting (name) {
  return "hello " + name;
}

index.js

import { greeting } from './greeting.js';
 
document.write(greeting('world!'))

在package.json里配置打包命令:

"scripts": {
  "build": "webpack ./src/index.js"
},

执行:

npm run build

如果生成了一个dist文件夹,并且内部含有main.js说明已经打包成功了

webpack5入门

2. 新增webpack.config.js 

新建config文件夹,接着新建一个webpack.config.js

const path = require("path");
module.exports = {
  mode: "development", // 开发模式
  entry: "./src/index.js",  // 入口文件
  output: {
    filename: "main.js",    // 打包后的文件名称
    path: path.resolve(__dirname, "../dist") // 打包后的目录
  }
};

更改我们的打包命令

"scripts": {
  "build": "webpack --config ./config/webpack.config.js"
},

执行npm run build进行打包

3.配置html模板

3.1 单入口配置

安装html-webpack-plugin

npm i -D html-webpack-plugin

利用html-webpack-plugin去将webpack打包出来的js文件引入到定义好的html模板中, 在根目录新建public,里面新建一个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>HtmlWebpackPlugin</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
  </body>
</html>

修改webpack.config.js

引入html-webpack-plugin,加上plugins

const HtmlWebpackPlugin = require("html-webpack-plugin");
//...
module.exports = {
  // ....
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html")
    })
  ]
}

执行npm run build就可以看到main.js已经引入到html页面中:

webpack5入门

如果我们需要频繁的修改js,但要实时查看效果的话则需要引入webpack-dev-server来解决这个问题。

webpack-dev-server 为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能

npm i -D webpack-dev-server

 修改配置文件,告知 dev server,从什么位置查找文件:

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "development", // 开发模式
  entry: path.resolve(__dirname,'../src/index.js'),  // 入口文件
  // web server
  devServer: {
    static: {
      directory: path.resolve(__dirname, '../dist'), // 打包后的文件路径
    },
    open: true,    //自动打开浏览器
    compress: true,  //启动gzip压缩
    port: 9000   // 端口号
  },
  output: {
    filename: "main.js", // 打包后的文件名称
    path: path.resolve(__dirname, "../dist"), // 打包后的目录
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html")
    })
  ]
};

接着修改package.json里配置命令:

"scripts": {
    "dev": "webpack-dev-server --config ./config/webpack.config.js",
    "build": "webpack --config ./config/webpack.config.js"
  },

为了在浏览器里看效果,我们可以修改src/index.js

import { greeting } from "./greeting.js";
document.getElementById("app").textContent = greeting("world!");

执行npm run dev就可以启动浏览器看实际效果了:

webpack5入门

3.2 多入口配置

还是利用html-webpack-plugin来解决, 只是生成多个html-webpack-plugin实例来解决这个问题

在src文件夹下新建search.js

search.js

document.getElementById("app").textContent = "我是search页面";

修改webpack.config.js里面的plugins

// ...
module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template:path.resolve(__dirname,'../public/index.html'),
      filename:'index.html',
      chunks:['main'] 
    }),
    new HtmlWebpackPlugin({
      template:path.resolve(__dirname,'../public/index.html'),
      filename:'search.html',
      chunks:['search'] // 与入口文件对应的模块名
    })
  ]
}

先执行npm run build看下打包后生成的文件:

webpack5入门

执行npm run dev看下实际效果:

webpack5入门

webpack5入门

两个页面都正常

3.3 利用glob对页面需要打包文件的路径进行处理

glob 在webpack中对文件的路径处理非常之方便,比如当搭建多页面应用时就可以使用glob对页面需要打包文件的路径进行很好的处理

安装glob

npm i -D glob

我们先调整下src中目录结构

webpack5入门

然后在根目录新建一个getMultiPageConfig.js

getMultiPageConfig.js

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

exports.getMultiPageConfig = function () {
  const entries = {}, htmlPlugins = [];
  // glob同步方法获取
  const entryFiles = glob.sync(path.resolve(__dirname, "./src/js/*/index.js"));
  // ['/xxx/demo05-glob-multpage/src/js/index/index.js', '/xxx/demo05-glob-multpage/src/js/search/index.js']
  entryFiles.forEach((ele) => {
    let match = ele.match(/src\/js\/(.*)\/index\.js/);
    let pageName = match && match[1];
    if (pageName) {
      entries[pageName] = ele;
      htmlPlugins.push(
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, "./public/index.html"),
          filename: `${pageName}.html`,
          chunks: [pageName],
        })
      );
    }
  });
  return {
    entries,
    htmlPlugins,
  };
};

接着修改webpack.config.js 

const path = require("path");
const { getMultiPageConfig } = require("../getMultiPageConfig");
const { entries, htmlPlugins } = getMultiPageConfig();
module.exports = {
  mode: "development", // 开发模式
  // 入口文件
  entry: entries,
  // web server
  devServer: {
    static: {
      directory: path.resolve(__dirname, '../dist'), // 打包后的文件路径
    },
    open: true,    //自动打开浏览器
    compress: true,  //启动gzip压缩
    port: 9000   // 端口号
  },
  output: {
    filename: "[name].js", // 打包后的文件名称
    path: path.resolve(__dirname, "../dist"), // 打包后的目录
  },
  plugins: htmlPlugins
};

 执行npm run dev/build结果都正常

3.4 加入clean-webpack-plugin清理打包目录

使用 clean-webpack-plugin清除dist文件夹

安装 clean-webpack-plugin

npm i -D clean-webpack-plugin

接着修改webpack.config.js , 引入clean-webpack-plugin,及修改plugins

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
//...
module.exports = {
  // ....
  plugins: [new CleanWebpackPlugin()].concat(htmlPlugins)
}

4.引入CSS

4.1 引入css、less、scss等

先修改public/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>webpack learning</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <h1>Hello World!</h1>
    <h2>Hello World!</h2>
    <h3>Hello World!</h3>
    <h4>Hello World!</h4>
    <h5 id="title"></h5>
  </body>
</html>

我们在src里创建assets文件夹, 接着在该文件夹下创建index.css、index.less、index.scss, search.less

index.css

h1 {
    color: red;
}

index.less

@color: blue;
h2 {
    color: @color;
}

index.scss

$color:yellow;
h3 {
    color: $color;
}

search.less 

@color:wheat;
h1, h2, h3 {
    color: @color;
}

修改src/index/index.js

index.js

import { greeting } from "./greeting.js";
import '../../assets/index.css';
import '../../assets/index.less';
import '../../assets/index.scss';
document.getElementById('title').textContent = greeting("world!");

由于webpack开箱即用只支持JS和JSON两种文件类型,此时我们需要利用style-loader、css-loader来解析css文件, 利用less、less-loader来解析.less文件,利用sass、sass-loader来解析.scss文件

我们一次性安装这些loader

npm i -D style-loader css-loader less less-loader sass sass-loader

接着修改webpack.config.js 

//...
module.exports = {
  // ....
  module:{
    rules:[
      {
        test:/\.css$/,
        use:['style-loader','css-loader'] // 从右向左解析原则
      },
      {
        test:/\.less$/,
        use:['style-loader','css-loader','less-loader'] // less的loader
      },
      {
        test:/\.scss$/,
        use:['style-loader','css-loader','sass-loader'] // scss的loader
      }
    ]
  }
}

执行npm run dev,可以看出引入的样式文件都以style的方式引入到html页面中

webpack5入门

4.2 拆分css成独立的文件

webpack 4.0以前,我们通过extract-text-webpack-plugin插件,把css样式从js文件中提取到单独的css文件中。webpack4.0以后,官方推荐使用mini-css-extract-plugin插件来打包css文件

安装mini-css-extract-plugin

npm i -D mini-css-extract-plugin

修改配置如下: 

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//...
module.exports = {
  // ....
  plugins: [
    new CleanWebpackPlugin(),
    ...htmlPlugins,
    new MiniCssExtractPlugin({
      filename: "[name].[hash].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"], // 从右向左解析原则
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      }
    ]
  }
}

执行npm run build,css被打包出单独成一个文件

webpack5入门

 执行npm run dev

webpack5入门

4.3 为css添加浏览器前缀 

利用postcss-loader、autoprefixer来为css添加浏览器前缀

安装postcss-loader、autoprefixer

npm i -D postcss-loader autoprefixer

为了看实际效果,我们在css文件里加animation样式:

src/index/index.less

@color: blue;
h2 {
    color: @color;
}
.test {
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  animation: mymove 5s infinite;
}

@keyframes mymove {
  from {
    left: 0px;
  }
  to {
    left: 200px;
  }
}

修改配置webpack.config.js

//...
module.exports = {
  // ....
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"], // 从右向左解析原则
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [["autoprefixer"]],
              },
            },
          },
          "less-loader",
        ], // less的loader
      },
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [["autoprefixer"]],
              },
            },
          },
          "sass-loader",
        ], // scss的loader
      }
    ],
  }
}

执行npm  run dev 可以看到animation样式已经加了浏览器前缀 

webpack5入门

5.打包图片、媒体、字体等文件

5.1 解决引用文件路径等问题

file-loader就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件url),并将文件移动到输出的目录中

安装file-loader

npm i -D file-loader

在assets里引入test.jpg、test1.jpg、movie.ogg等资源

修改src/index/index.js

import { greeting } from "./greeting.js";
import '../../assets/index.css';
import '../../assets/index.less';
import '../../assets/index.scss';
// 在js中使用图片
import test from "../../assets/test.jpg";
import test1 from "../../assets/test1.jpg";

import movie from "../../assets/movie.ogg";
document.getElementById('title').textContent = greeting("world!");

let img = new Image();
img.src = test;
document.getElementById("testImg").appendChild(img);

let img1 = new Image();
img1.src = test1;
document.getElementById("testImg").appendChild(img1);

let videoDom = document.createElement("video");
videoDom.src = movie;
videoDom.controls = "controls"
document.getElementById("testMedia").appendChild(videoDom);

修改配置文件,加上该loader

//...
module.exports = {
  // ....
  module: {
    rules: [
      {
        test: /\.(gif|png|jpe?g)$/i,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[path][name].[ext]",
            },
          },
        ],
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[path][name].[ext]",
            },
          },
        ],
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[path][name].[ext]",
            },
          },
        ],
      },
    ],
  }
}

执行npm run build 效果如下:

webpack5入门

5.2 优化图片等问题 

url-loader 一般与file-loader搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中

安装url-loader

npm i -D url-loader

修改配置文件,

//...
module.exports = {
  // ....
  module: {
    rules: [
      {
        test: /\.(gif|png|jpe?g)$/i,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 1024 * 8, // // 将小于8KB的图片转换成base64的格式
              fallback: {
                loader: "file-loader",
                options: {
                  name: "img/[name].[hash:8].[ext]", // 文件名.hash.文件扩展名 默认格式为[hash].[ext]
                },
              },
            },
          },
        ],
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
        use: [{
          loader: 'file-loader',
          options: {
            name: 'media/[name].[ext]'
          }
        }]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[path][name].[ext]",
            },
          },
        ],
      },
    ],
  }
}

执行npm run dev可以看出小于8kb的图片base64编码了

webpack5入门

6. babel转ES5

在webpack中 默认只能处理部分 ES6的新语法,一些更高级的ES6或ES7的语法,webpack是处理不了的这个时候就需要借助第三方的loader 来帮助webpack 处理这些高级的语法。

Balel 可以帮我我们将高级的语法转为低级的语法

利用babel-loader可以使我们的js代码兼容更多的环境

安装babel-loader @babel/preset-env @babel/core

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

修改src/index/index.js,加上一些ES6方法

//...省略部分代码

// 箭头函数
let arrowFn = () => console.log("hello babel");
arrowFn();
// 解构赋值
let arr = [1, 2, 3];
let [a, b, c] = arr;
// 拓展运算符
console.log(...arr);
//
const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach((x) => s.add(x));

for (let i of s) {
  console.log(i);
}

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "done");
  });
}

timeout(100).then((value) => {
  console.log(value);
});

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint("hello world", 50);

修改配置文件,加上babel-loader

//...
module.exports = {
  // ....
  module: {
    rules: [
      {
        test:/\.js$/,
        use:{
          loader:'babel-loader',
          options:{
            presets:['@babel/preset-env']
          },
        },
        exclude: /node_modules/
      },
    ],
  }
}

在npm run dev执行完后发现一个错误

webpack5入门

这个报错表面上是由于 async function 语法被 babel 转译之后的代码使用了 regeneratorRuntime 这个变量,但是这个变量在最终的代码里未定义造成的报错。

babel 在转译的时候,会将源代码分成 syntax 和 api 两部分来处理:

  • syntax:类似于展开对象、optional chain、let、const 等语法
  • api:类似于 [1,2,3].includes 等函数、方法

上面的babel-loader只会将 ES6/7/8语法转换为ES5语法,但是对新api并不会转换 例如(promise、Generator、Set、Maps、Proxy等)
此时我们需要借助babel-polyfill来帮助我们转换

安装babel-polyfill

npm i @babel/polyfill

接着在src/index/index.js引入该文件

import "@babel/polyfill";
// ...省略以下代码

执行打包,一切正常,但在这种模式下,babel 会将所有的 polyfill 全部引入导致打包体积过大,而且需要在头部引入该文件,正确的做法是使用按需加载,@babel/preset-env 中有一个配置选项 useBuiltIns,用来告诉 babel 如何处理 api。将 useBuiltIns 改为 "usage",babel 就可以按需加载 polyfill,并且不需要手动引入 @babel/polyfill 

修改配置文件:

//...
module.exports = {
  // ....
  module: {
    rules: [
      {
        test:/\.js$/,
        use:{
          loader:'babel-loader',
          options:{
            presets:['@babel/preset-env']
          },
        },
        exclude: /node_modules/
      },
    ],
  }
}

 同时去掉src/index/index.js中import "@babel/polyfill";

 执行npm run dev看下打包完的index.js

webpack5入门

可以看出是按需加载的 

参考资料:

webpack官网

https://juejin.cn/post/6844904031240863758#heading-10

上一篇:webpack5


下一篇:webpack5 + Node.js+ Nacos 搭建微前端应用网络