Webpack5 系列(一):基础篇

1,279 阅读7分钟

一、前言

基础篇主要围绕以下几点展开:

  • 为什么会使用 Webpack?
  • Webpack 打包流程
  • Webpack 的本源
  • Webpack 的配置文件
  • 简化 Webpack 打包流程

1. 简单案例

在前端开发中,如果一个页面的逻辑非常多,那么我们就有必要做业务逻辑的划分。

例如,一个网页包含三个部分,分别是:Header、Sidebar、Content,这些全都通过 JavaScript 来编写,并加入到页面中。

文件结构如下:

my-webpack-demo-1/
|- index.html
|- index.js

index.html

<body>
  <div id="root"></div>
  
  <script src="./index.js"></script>
</body>

index.js

const dom = document.getElementById('root');

// header
const header = document.createElement('div');
header.innerText = 'header';
dom.appendChild(header);
// sidebar
const siderBar = document.createElement('div');
siderBar.innerText = 'siderBar';
dom.appendChild(siderBar);
// content
const content = document.createElement('div');
content.innerText = 'content';
dom.appendChild(content);

2. 面向对象

a) 封装

在上面的案例中,实际上 Header、Sidebar、Content 各自的业务逻辑如果很多的话,就可以单独封装到一个构造函数(类)中。

文件结构如下:

my-webpack-demo-1/
|- content.js
|- header.js
|- index.html
|- index.js
|- sidebar.js

header.js:

function Header(dom) {
  const header = document.createElement('div');
  header.innerText = 'header';
  dom.appendChild(header);
}

sidebar.js

function Sidebar(dom) {
  const siderBar = document.createElement('div');
  siderBar.innerText = 'siderBar';
  dom.appendChild(siderBar);
}

content.js

function Content(dom) {
  const content = document.createElement('div');
  content.innerText = 'content';
  dom.appendChild(content);
}

b) 引入

封装完毕后,引入到 index.html 中,并在 <script src="./index.js"></script> 前导入三个 js 文件。

index.html

<body>
  <div id="root"></div>
  
  <script src="./header.js"></script>
  <script src="./sidebar.js"></script>
  <script src="./content.js"></script>
  <script src="./index.js"></script>
</body>

index.js

const dom = document.getElementById('root');

// header
new Header(dom);
// side-bar
new Sidebar(dom);
// content
new Content(dom);

3. 模块化

使用面向对象的方式改进了代码,但是出现了新的问题。什么问题呢?

问题往往发生在 index.html 中引入 js 的先后顺序出现错误。例如,如果先引入 index.js ,之后再引入其他 js 文件,这样顺序不对而导致解析顺序出错,那么必然报错。实际上,我们希望将所有的文件都引入到 index.js 中,然后只引入这个主文件 ( index.js ) 就好。

所以,我们不妨使用 ES6 的模块语法(Module)。

于是,我们需要对代码做一个修改:

  • 通过 export 语法导出模块;
  • 通过 import 语法引入模块。

a) 功能代码编写与导出

header.js

function Header(dom) {
  const header = document.createElement('div');
  header.innerText = 'header';
  dom.appendChild(header);
}

export default Header;

sidebar.js

function Sidebar(dom) {
  const siderBar = document.createElement('div');
  siderBar.innerText = 'siderBar';
  dom.appendChild(siderBar);
}

export default Sidebar;

content.js

function Content(dom) {
  const content = document.createElement('div');
  content.innerText = 'content';
  dom.appendChild(content);
}

export default Content;

b) 功能代码引入到主文件

导出后,将所有的功能代码统一在 index.js 中引入:

// index.js
// ES Module
import Header from './components/header.js';
import Sidebar from './components/sidebar.js';
import Content from './components/content.js';

const dom = document.getElementById('root');

// header
new Header(dom);
// side-bar
new Sidebar(dom);
// content
new Content(dom);

而 index.html 中只引入 index.js:

<body>
  <div id="root"></div>
  
  <script src="./index.js"></script>
</body>

然而,浏览器暂不支持模块语法,会报错。

image-20210724201001354.png

image-20210724201024589.png

于是,Webpack 就应运而生了~

二、初识 Webpack

1. 介绍

a) webpack

Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

www.npmjs.com/package/web…

  • 打包 ES Modules,CommonJS 以及 AMD Modules(甚至是组合)。
  • 可以在运行时对异步加载的单文件或多个块进行打包(减少初始加载时间)。
  • 在编译期间解析依赖项,从而减少运行时大小。
  • 加载器(Loaders) 可以在编译时对文件进行预处理。比如,TypeScript to JavaScript, Handlebars strings to compiled functions, images to Base64, etc.
  • 高度模块化的 插件(Plugin) 系统。(Highly modular plugin system)

b) webpack-cli

webpack 官方的 CLI (Command Line Interface) 工具。

webpack CLI provides a flexible set of commands for developers to increase speed when setting up a custom webpack project. As of webpack v4, webpack is not expecting a configuration file, but often developers want to create a more custom webpack configuration based on their use-cases and needs. webpack CLI addresses these needs by providing a set of tools to improve the setup of custom webpack configuration.

2. 安装

mkdir webpack-demo
cd webpack-demo
npm init -y
npm i webpack webpack-cli --save-dev

webpack 和 webpack-cli 是两个不同的包,--save-dev 可以简写为 -D,表示保存并写进开发依赖中。

注意:webpack-cli 必须安装,否则运行不了 webpack!

查看版本:

npx webpack -v

npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装!

3. 文件结构

将文件按照如下位置安放:

webpack/
|- /src
  |- /components
    |- content.js
    |- header.js
    |- sidebar.js
  |- index.js
|- index.html
|- package.json

4. 打包

执行打包命令:

npx webpack-cli

运行结果如下:

image-20210724205928412.png

打包完成后,就会出现一个 dist 目录,里面有一个 main.js 文件,这就是打包完成的 js 文件。

或者通过:npx webpack 也可以。

5. 效果

在打包结束后,需要修改 index.html 中脚本的引用位置:

<body>
  <div id="root"></div>

  <script src="./dist/main.js"></script>
</body>

页面效果如下:

image-20210724210539876.png

网页正常显示。

那么, webpack 做了什么事情?它将原来的文件翻译打包成了浏览器看得懂的 js 文件 ( main.js )。

6. 本源

在某种程度上,我们可以将 webpack 理解为 js 代码翻译器吗?其实不然!

它只是可以识别 js 的模块语法而已,例如,上面的 import、export 语法 ( Header、Sidebar、Content 就是模块。),然而对于其他的 js 语法它是不认识的!

正如它的官方文档所说,Webpack is a module bundler. webpack 是一个模块打包工具!它可以将多个模块打包到一起。另外,对于 CommonJS(Node.js 用)、CMD、AMD 这些模块规范,webpack 也可以识别并打包。

除了 js 文件以外,webpack 还可以打包其他模块文件,例如, ts 文件,css/sass/less/stylus 文件,图片文件等等。

阅读参考:

三、Webpack 配置文件

在第二部分中,我们通过 npx webpack 的形式进行打包,实际上我们用的是 webpack 默认的配置来打包的。

那么如何自定义打包配置呢?

  1. 在根目录下创建配置文件 ( 默认是:webpack.coonfig.js )
  2. 编写打包配置项

注意,此时的文件结构如下:

webpack/
|- /src
  |- /components
    |- content.js
    |- header.js
    |- sidebar.js
  |- index.js
|- index.html
|- package.json

1. 默认的配置文件

在文件根目录下创建:

touch webpack.config.js

webpack.config.js

// webpack 配置文件
const path = require('path'); // node.js 的路径模块

module.exports = {
  // entry: './src/index.js', // 入口文件(简写形式)
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'), // 打包后的路径
    filename: 'bundle.js', // 打包后的文件名
  }
}

以上是简单的打包配置,配置项包括入口文件、打包路径、打包文件名。

其中,入口文件是指一个项目的主文件,一般来说,所有的模块都会被加入到这个文件中,类似于 vue-cli 中的 main.js 文件。

之后,运行 npx webpack,即可按照此配置文件进行打包。

2. 自定义的配置文件

现在,如果我们并没有将配置文件设置为默认的 webpack.config.js,

而是使用了其他的名字,例如:my-webpack-config.js,

在这种情况下,我们该如何以这个自定义的配置文件作为配置的标准来打包呢?

通过以下命令即可:

npx webpack --config my-webpack-config.js

运行结果如下:

image-20210725102954540.png

3. 简化打包流程

以上,我们都是通过手动的 npx webpack 来打包的!

实际上,还可以利用 package.json 中的 scripts 字段来编写运行脚本,通过脚本进行打包。

// ...
"scripts": {
  "bundle": "webpack"
},
// ...

之后,通过 npm run bundle 就可以打包了。

image-20210725105318883.png

疑惑:为什么 "bundle" 后面不写成:"npx webpack" 呢?

原因在于,当你运行 npm run bundle 时,它会先去 node_modules 文件夹中去找是否安装了 webpack 这个指令,如果有就会执行了,相当于被翻译成了 webpack 这个命令。这个与 npx webpack 是类似的,但并不是相等的,本文前面有叙述过,请自行查找~

(也就是看 node_modules 中的 webpack 下的 bin 中是否有对应的可执行指令文件,这块需要了解 Node.js 的内容,此篇不论。)

参考:webpack.js.org/guides/gett…

4. 打包模式

每次打包完,打包内容中总有一段 WARNING 警告,提醒我们设置打包模式(mode)。

默认情况下,它被设置为 'production',我们也可以自行配置:

// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development', // 'development' | 'production'
  // entry: './src/index.js',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
}

可以配置为开发模式或者生产模式:

  • 开发模式:本地环境
  • 生产模式:线上环境(代码是压缩的)

具体有何不同,在打包后,打开 dist 文件夹下的 main.js,便可一目了然,此处不再赘述。

小结

以上,是本篇的所有内容。 最后回答一下前言部分的问题:

  • 为什么会使用 Webpack?模块化、预处理。
  • Webpack 打包流程:确保存在 package.json 的情况下去打包,注意配置项的设置。
  • Webpack 的本源:模块打包器,记住是模块。
  • Webpack 的配置文件:webpack.config.js,配置项:mode/entry/output
  • 简化 Webpack 打包流程:设置 package.json 中的 scripts 字段。

添加我的微信:with_his_x,共同成长,卷卷群里等你 🤪。

以上,感谢您的阅读~