基于 webpack 构建项目

初始化项目

新建一个项目文件夹,在终端打开,执行

1
npm init

之后,会在项目根目录下出现一个 package.json 文件。
然后在项目根目录下新建 src 文件夹,在此文件夹下新建 main.js 入口文件和 index.html 模板文件。

1
2
3
4
5
- project
- src
- main.js
- index.html
- package.json

安装 webpack

1
npm i webpack-cli webpack webpack-dev-server
  • webpack:webpack 的核心模块;
  • webpack-cli:使得我们可以通过命令行参数运行 webpack;
  • webpack-dev-server:可以启动一个本地 HTTP 服务,支持代码热更新,其配置在 webpack.config.jsdevServer 中。
配置 webpack

在项目根目录下新建 webpack.config.js 文件,添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const path = require("node:path");


module.exports = {
mode: process.env.NODE_ENV,
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[hash:8].js",
clean: true
},
devServer: {
port: 8080,
host: "0.0.0.0",
hot: true,
open: true,
proxy: {
"/api": {
target: "http://localhost:5000",
pathRewrite: { "^/api": "" }
}
}
}
}

配置 package.json
1
2
3
4
5
6
{
"scripts": {
"dev": "webpack-dev-server --mode development",
"build": "webpack --config webpack.config.js --mode production"
}
}

我们可以通过 --mode 指定运行环境。
现在可以通过 npm run dev 来启动一个本地服务,通过 npm run build 来打包构建。

我们还可以为不同的环境创建单独的配置文件:
在根目录下新建 scripts 文件夹,在该文件夹下新建三个配置文件:

1
2
3
4
- scripts
- webpack.base.js // 通用配置
- webpack.dev.js // 开发环境配置
- webpack.prod.js // 生产环境配置

webpack.dev.jswebpack.prod.js 怎么继承 webpack.base.js 中的配置呢?可以使用 webpack-merge 合并配置文件:

1
npm i webpack-merge
1
2
3
4
5
6
7
8
9
10
11
// webpack.base.js

const path = require('path')

module.exports = {
entry: path.resolve(__dirname, '../src/main.js'),
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].[hash:8].js'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// webpack.dev.js

const { merge } = require('webpack-merge')
const base = require('./webpack.base.js')

module.exports = merge(base, {
mode: 'development',
devServer: {
open: true,
port: 8080,
host: "0.0.0.0",
hot: true
},
})
1
2
3
4
5
6
7
8
// webpack.prod.js

const { merge } = require('webpack-merge')
const base = require('./webpack.base.js')

module.exports = merge(base, {
mode: 'production',
})

现在 package.json 中的配置如下:

1
2
3
4
5
6
{
"scripts": {
"dev": "cross-env NODE_ENV=development webpack server -c scripts/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack -c scripts/webpack.prod.js"
}
}

使用 NODE_ENV= 来设置环境变量,为了在不同的平台上都能使用,我们使用 cross-env 来兼容,这样在不同环境下也能正确获取环境变量。

1
npm i cross-env

配置 html 插件

当我们启动服务后会发现,之前创建的模板文件并没有被引用。这时就需要一个插件让 webpack 引用我们自定义的模板文件,并将打包后的出口文件自动插入到模板文件中。

1
npm i html-webpack-plugin

配置 webpack:

1
2
3
4
5
6
7
8
9
10
11
12
13
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
// 打包后的文件名
filename: "index.html",
// 插入到 html 文件的哪个部位
inject: "body"
})
]
}

安装 Babel

babel 的作用就是将 es6/esnext 转为 es5,使得代码能够在各个浏览器中运行。

1
npm i @babel/core @babel/preset-env babel-loader
  • @babel/core:babel 的核心包;
  • @babel/preset-env:将 es6+ 语法转为 es5 的预设;
  • babel-loader:将 babel 与 webpack 关联起来。
配置 babel

在项目根目录下新建 .babelrc 文件,添加如下内容:

1
2
3
{
"presets": ["@babel/preset-env"]
}

配置 webpack:
1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
include: /src/
}
]
}
}

加载 css

webpack 默认是不处理 css 的,需要安装两个 loader:

1
npm i style-loader css-loader
  • style-loader:将 css 添加到 style 标签中,并插入到 html;
  • css-loader:解析 css 文件的 import 导入方式,以及 url() 引入方式。

配置 webpack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
include: /src/
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
}
}

注意: loader 的加载顺序是从右到左(从后往前)的,也就是先支持 import 导入再去插入到 style 标签,如果反了,逻辑也就反了。

加载图片或文件

如果我们直接将图片的路径或 import 导入的变量赋值给 img 标签的 src 属性,会报错无法加载文件。
这时需要安装对应的 loader 来解决:file-loader 或 url-loader。
url-loader 包含了 file-loader,它通过限定一个图片的大小(limit),来判断是否采用编码的方式。小于 limit 的时候使用 base64 进行压缩,大于则使用 file-loader 变成路径。
这里选择 file-loader:

1
npm i file-loader

配置 webpack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
include: /src/
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
},
{
test: /\.(png|jpe?g|gif)$/i,
loader: "file-loader",
options: {
// 打包后文件名字
name: "[name].[ext]",
// 打包后输出路径
outputPath: "./img"
}
}
]
}
}

配置 ts

1
npm i typescript ts-loader @babel/preset-typescript
  • typescript:TS 的主要引擎;
  • ts-loader:转译 ts -> js,并打包;
  • @babel/preset-typescript:与 ts-loader 作用相同,但是能够加快打包速度。

既然 @babel/preset-typescript 与 ts-loader 作用相同,且打包速度更快,那为什么还要用 ts-loader 呢?
因为 ts-loader 在内部是调用了 TypeScript 的官方编译器 tsc,会提供完整的报错信息, 而 @babel/preset-typescript 有的时候会无法提供完整的报错信息和类型提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.config.js

module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: "ts-loader"
}
]
}
}
1
2
3
4
5
// .babelrc

{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}