学一点Webpack配置:基本配置
特别声明:小站对部分原创文章已开启付费阅读,并开通年费VIP通道,年费价格为 ¥365.00元。如果您喜欢小站的内容,可以点击开通会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!(^_^)
这两天朋友圈流行这么一张图:
多么形象的展示了前端学习的曲线图。真可谓是一言难尽呀,现在的前端真不好学,乱而杂。如果你要是再看看@Kamran Ahmed整理的2017年、2018年和2019年现代Web开发者要掌握的Roadmap,估计更会泪崩:
现状是如此,未来可能会更混乱,但我们不应该去抱怨,应该更应该保持一颗爱学习的心,继续往前行走。
学点Webpack配置方面的知识
Webpack是构建工具中必不可少的一部分:
作为现代Web开发者就需要对Webpack有所了解,哪怕掌握的不够深入,略知皮毛也对我们自己的工作或学习都是有所帮助的。比如说吧,前段时间折腾React环境下的CSS Modules,就是因为自己对Webpack不了解,有些坑踩了无法立刻解决,就算借助互联网,解的也是知半解(而且现在技术更新太快,网上有些教程根本走不通,不踩不知道,一踩只有泪)。正因为这个原因,促使自己去了解Webpack更多的知识。接下来的内容是一些基础,主要会介绍怎么用Webpack来构建自己的开发环境,感兴趣的请继续往下阅读。
Webpack是什么
一直以来,在我自己的印象和理解中,都认为Webpack是一个构建工具。主要用来构建开发的工程体系。但从其官网来看,告诉我Webpack是一个模块Bundler(捆绑器):
那么,Webpack到底是一个构建工具(或者说一个构建系统)还是一个模块Bundler(捆绑器)呢?答案是:
Webpack既是一个构建系统,也是一个捆绑器!
Webpack不是先构建你的资源(Assets),然后再bundle你的模块,它把你的资源本身就当做是一个模块。这些模块可以被导入、修改和操作等,最后才被打包到你最后的bundle。
简单地说,Webpack其最核心的功能就是 解决模板之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并成一个JS文件(比如bundle.js
)。这个整个过程也常常被称为是模块打包。换句话说,Webpack是一个指令集合的配置文档,然后通过配置好的这些指令去驱动程序做一些指令要求要做的事情。而这些动作都是通过自己写的规则去做编译,而且通过JavaScript的引入(import
)语法让Webpack知道需要它帮忙编译什么东西(比如Pug、Sass等等)。所以我们始终会有一个入口文件(比如index.js
)注入那些Preprocess,让那些Preprocess可以通过这些入口文件的JavaScript让Webpack去根据相关的配置指令编译它,然后打包到一个出口文件中,比如bundles.js
。
为什么要用Webpack
一直以来,在开发Web页面或Web应用程序的时候,都习惯性的将不同资源放置在不同的文件目录之中,比如图片放置在images
(或img
)下,样式文件放置在styles
(或css
)中,脚本文件放在js
和模板文件放置在pages
中。一直以来,发布的时候都会一次性的将所有资源打包发布,不管这些资源用到了还是没用到(事实上很多时候自己都分不清楚哪资源被使用)。用一句话来描述就是:依赖太复杂,太混乱,无法维护和有效跟踪。比如哪个样式文件引用了a.img
,哪个样式文件引用了b.img
;另外页面到底是引用了a.css
呢还是b.css
呢?
而Webpack这样的工具却能很多好的解决它们之间的依赖关系,使其打包后的结果能运行在浏览器上。其目前的工作方式主要被分为两种:
- 将存在依赖关系的模块按照特定规则合并成为单个
.js
文件,一次性全部加载进页面 - 在页面初始时加载一个入口模块,其他模块异步加载
相比于Parcel、Rollup具有同等功能的工具而言,Webpack还具有其他的优势:
- Webpack支持多种模块标准:这对于一些同时使用多种模块标准的工程非常有用,Webpack会帮我们处理好不同类型模块之间的依赖关系
- Webpack有完备的代码分割解决方案:它可以分割打包后的资源,首屏只加载必要的部分,不太重要的功能放到后面动态地加载
- Webpack可以处理各种类型的资源:除了JavaScript之外,Webpack还可以处理样式、模板、图片等资源。开发者要做的只是将之些资源导入,而无需关注其他
另外,Webpack还拥有一个强大的社区。这也是其受开发者青眯的原因之一。接下来,我们还是实际一点,动手来撸码。
从零开始构建你自己的开发环境
为了更好的理解Webpack能帮我们做什么,我打算从零开始构建一个属于自己的开发环境。可能在后面的内容中会涉及到很多关键词,比如Webpack、loaders、Babel、sourcemaps、React、TypeScript,CSS Modules等等。接下来一步一步的学习中会了解到这些单词和相关技术。
后面会一步一步的带大家如何使用Webpack配置适合自己的开发环境,会涉及到一些相关技术,但不会深入到具体技术细节中。
在写这篇文章所具备的环境是:Node是v10.9.0
,NPM是v6.9.2
,Webpack是v4.34.0
,React是v16.8.6
,TypeScript是v3.4.4
,Sass是v.6.9.0
,PostCSS是v.6.9.0
等。接下来的内容会以配置React + TypeScript + CSS Modules + PostCSS为主线,从零开始一个项目。另外,接下来的内容会以不同分支的形式将代码放置在Github上。感兴趣的可以直接将仓库克隆下来,切换到对应步骤的分支,查看代码。
Step01:初始化项目
请将Git分支切换到
step1
分支查看代码。
首先在你的本地创建一个项目,比如我这里创建了一个webpack-sample
项目:
⇒ mkdir webpack-sample && cd webpack-sample
进入到新创建的项目目录下,执行npm init
或者npm init -y
命令来初始化项目,执行完该命令之后,在你的命令终端会看到类似下图这样的命令询问,你可以根据你自己的需要去输入你想要的内容,或者一路Enter
键执行下去:
此时你的项目根目录下会增加一些文件和文件夹:
|--webpack-sample/
|----node_modules/
|----package.json
|----package-lock.json
其中package.json
文件里将包含一些项目信息:
注意,这个文件随着后面的步骤完成,会增加更多的内容。
而package-lock.json
文件是当 node_modules/
或 package.json
发生变化时自动生成的文件,它的主要功能是 确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新。
在Step01中,我们对package.json
文件只做一个修改,删除"main": "index.js"
入口,并添加"private":true
选项,以便确保安装包是私有的,这样可以防止意外发布你的代码。
Step02:安装Webpack和初始配置Webpack
请将分支切换到
step2
查看代码。
在这一步,先来安装Webpack。执行下面的命令安装Webpack配置所需要的包:
⇒ npm i webpack webpack-cli webpack-dev-server -D
此时打开package.json
文件,你会发现在文件中有一个新增项 devDependencies
:
{
// 其他项信息在这省略,详细请查看该文件
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
}
}
注意,在命令终端使用npm i
安装依赖关系时,如果带后缀 -D
(或--save-dev
) 安装的包会记录在"devDependencies"
下;如果使用 --save
后缀(我们后面会用到)安装的包会记录在"dependencies"
下。两者的区别是:
"devDependencies"
:dev
开发时的依赖包dependencies
:程序运行时的依赖包
为了验证Webpack是否能正常工作,这个时候我们需要创建一些新的文件。在webpack-sample
根目录下创建/src
目录,并且在该目录下创建一个index.js
文件:
⇒ mkdir src && cd src && touch index.js
执行完上面的命令,你会发现你的项目目录结构变成下图这样:
我们在新创建的/src/index.js
文件下添加一行最简单的JavaScript代码:
console.log("Hello, Webpack!(^_^)~~")
保存之后回到命令终端,第一次执行有关于Webpack相关的命令:
⇒ npx webpack src/index.js --output dist/bundle.js
执行完上面的命令后,如果看到下图这样的结果,那么要恭喜你,Webpack的安装已经成功,你可以在你的命令终端执行有关于Webpack相关的命令:
回到项目中,会发现项目根目下自动创建了一个/dist
目录,而且该目录下包含了一个bundle.js
文件:
执行完上面的命令之后,可以看到有相关的警告信息。那是因为Webpack4增加了mode
属性,用来表示不同的环境。mode
模式具有development
,production
和 none
三个值,其默认值是production
。也就是说,在执行上面的命令的时候,我们可以带上相应的mode
属性的值,比如说,设置none
来禁用任何默认行为:
⇒ npx webpack src/index.js --output dist/bundle.js --mode none
执行到这里,只知道我们可以运行Webpack相关命令。并不知道/src/index.js
的代码是否打包到/dist/bundle.js
中。为此,我们可以在/dist
目录下创建一个index.html
⇒ cd dist && touch index.html
并且将生成出来的bundle.js
引入到新创建的index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello Webpack (^_^) ~~~</title>
</head>
<body>
<script src="./bundle.js"></script>
</body>
</html>
在浏览器中打开/dist/index.html
,或者在命令行中执行:
⇒ npm i -g http-server
⇒ http-server dist
http-server
是一个启动服务器的npm
包,执行上面的命令之后,就可以在浏览器中访问http://127.0.0.1:8080/
(访问的/dist/index.html
),在浏览器的console.log
控制台中,可以看到src/index.js
的脚本输出的值:
上面的过程足以验证,你的Webpack能正常的工作了。
不过,当你需要构建的东西越复杂,需要的标志就会越多。在某种程度上说,就会变得难以控制。这个时候我们就需要一个文件来管理这些配置。接下来我们需要创建一个webpack.config.js
这样的一个文件,用来配置Webpack要做的事情。注意,这个文件是一个node.js
文件,所以你可以在任何节点文件中执行任何你能够执行的操作。你也可以写成json
文件,但是node文件更强大一些。
首先们先创建Webpack的配置文件,在webpack-sample
根目录下创建一个/build
目录,然后在该目录下添加一个名为webpack.config.js
文件:
⇒ mkdir build && cd build && touch webpack.config.js
执行完上面的命令之后,你会发现你的项目文件目录结构变成下面这样了:
这个时候,新创建的webpack.config.js
文件里面是一片空白,它就是Webpack的配置文件,将会导出一个对象的JavaScript文件。我们需要在这个文件中添加一些配置:
var webpack = require('webpack');
var path = require('path');
var DIST_PATH = path.resolve(__dirname, '../dist'); // 声明/dist的路径
module.exports = {
// 入口JS路径
// 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始
entry: path.resolve(__dirname,'../src/index.js'),
// 编译输出的JS入路径
// 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件
output: {
path: DIST_PATH, // 创建的bundle生成到哪里
filename: 'bundle.js', // 创建的bundle的名称
},
// 模块解析
module: {
},
// 插件
plugins: [
],
// 开发服务器
devServer: {
}
}
Webpack配置是标准的 Node.js CommonJS模块,它通过require
来引入其他模块,通过module.exports
导出模块,由Webpack根据对象定义属性进行解析。
上面很简单,到目前为止只通过entry
设置了入口起点,然后通过output
配置了打包文件输出的目的地和方式。你可能也发现了,在配置文件中还有module
、plugins
和devServer
没有添加任何东西。不需要太急,后面会一步一步带着大家把这里的内容补全的,而且随着配置的东西越来越多,整个webpack.config.js
也会更变越复杂。
完成webpack.config.js
的基础配置之后,回到package.json
文件,并在"scripts"
下添加"build": "webpack --config ./build/webpack.config.js"
:
// package.json
{
// ...
"scripts": {
"build": "webpack --config ./build/webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
}
这样做的,为让我们直接在命令终端执行相关的命令就可以实现相应的功能。比如上面配置的build
,在命令终端执行:
⇒ npm run build
上面的命令执行的效果前面提到的npx webpack src/index.js --output dist/bundle.js --mode none
等同。同样有警告信息,主要是mode
的配置没有添加。在上面的配置中添加:
{
"scripts": {
"build": "webpack --config ./build/webpack.config.js --mode production",
"test": "echo \"Error: no test specified\" && exit 1"
},
}
再次执行npm run build
,不会再有警告信息。你可以试着修改/src/index.js
的代码:
alert(`Hello, Webpack! Let's Go`);
重新编译之后,打开/dist/index.html
你会发现浏览器会弹出alert()
框:
为了开发方便,不可能通过http-server
来启用服务。我们可以把这部分事件放到开发服务器中来做,对应的就是devServer
,所以我们接着在webpack.config.js
中添加devServer
相关的配置:
// webpack.config.js
// 开发服务器
devServer: {
hot: true, // 热更新,无需手动刷新
contentBase: DIST_PATH, //
host: '0.0.0.0', // host地址
port: 8080, // 服务器端口
historyApiFallback: true, // 该选项的作用所用404都连接到index.html
proxy: {
"/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址
}
}
有关于
devServer
更详细的配置参数描述,可以查阅读Webpack官网相关文档。
和build
类似,需要在package.json
的scripts
中添加相关的命令:
// package.json
"scripts": {
"build": "webpack --config ./build/webpack.config.js --mode production",
"dev": "webpack-dev-server --config ./build/webpack.config.js --mode development --open",
"test": "echo \"Error: no test specified\" && exit 1"
},
保存所有文件,在命令行中执行npm run dev
就可以启动服务器:
你可以验证一下,修改/src/index.js
:
document.addEventListener('DOMContentLoaded', () => {
const h1Ele = document.createElement('h1')
document.body.append(h1Ele);
h1Ele.innerText = 'Hello Webpack (^_^)'
h1Ele.style.color = '#f46';
})
保存该文件之后,浏览器会立刻刷新,你将看到修改之后的变化:
Step03: 优化Webpack配置
请将分支切换到
step3
查看代码。
在Step02
中,开发和生产环境相关的配置都集成在webpack.config.js
一个文件中。为了更好的维护代码,在Step03
中做一些优化。把webpack.config.js
拆分成三个部分:
- 公共配置:把开发和生产环境需要的配置都集中到公共配置文件中,即
webpack.common.js
- 开发环境配置:把开发环境需要的相关配置放置到
webpack.dev.js
- 生产环境配置:把生产环境需要的相关配置放置到
webpack.prod.js
先在/build
目录下创建上面提到的三个配置文件。在命令终端执行下面的命令即可:
⇒ cd build && touch webpack.common.js webpack.dev.js webpack.prod.js
这个时候,整个项目目录结构变成下图这样:
从
Step02
中遗留下来的webpack.config.js
文件将会从/build
目录中移除。
为了更好的管理和维护这三个文件,需要安装一个webpack-merge
插件:
⇒ npm i webpack-merge -D
执行完上面的命令之后,package.json
文件中的devDependencies
会增加webpack-merge
相关的配置:
// package.json
{
//... 省略的信息请查看原文件
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2",
"webpack-merge": "^4.2.1"
}
}
接下来分别给webpack.common.js
、webpack.dev.js
和webpack.prod.js
文件添加相关的配置:
Webpack公共配置
在公共配置文件webpack.common.js
文件中添加相应的配置:
const webpack = require('webpack');
const path = require('path');
const DIST_PATH = path.resolve(__dirname, '../dist/'); // 声明/dist的路径
module.exports = {
// 入口JS路径
// 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始
entry: path.resolve(__dirname,'../src/index.js'),
// 编译输出的JS入路径
// 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件
output: {
path: DIST_PATH, // 创建的bundle生成到哪里
filename: 'bundle.js', // 创建的bundle的名称
},
// 模块解析
module: {
},
// 插件
plugins: [
]
}
Webpack开发环境配置
接着给Webpack开发环境配置文件webpack.dev.js
添加下面的相关配置:
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const DIST_PATH = path.resolve(__dirname, '../dist/'); // 声明/dist的路径
module.exports = merge(commonConfig, {
mode: 'development', // 设置webpack mode的模式
// 开发环境下需要的相关插件配置
plugins: [
],
// 开发服务器
devServer: {
hot: true, // 热更新,无需手动刷新
contentBase: DIST_PATH, //
host: '0.0.0.0', // host地址
port: 8080, // 服务器端口
historyApiFallback: true, // 该选项的作用所用404都连接到index.html
proxy: {
"/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址
}
}
})
Webpack生产环境配置
继续给Webpack生产环境配置文件webpack.prod.js
添加相关配置:
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = merge(commonConfig, {
mode: 'production', // 设置Webpack的mode模式
// 生产环境下需要的相关插件配置
plugins: [
],
})
上面的配置信息只是将
Step02
中webpack.config.js
分成三个文件来配置,随着后续添加相应的配置信息,那么这三个文件中的配置信息会越来越多,也会越来越复杂。
修改完Webpack的配置之后,对应的package.json
中的scripts
中的信息也要做相应的调整:
// package.json
{
// ... 其他配置信息请查看原文件
"scripts": {
"build": "webpack --config ./build/webpack.prod.js --mode production",
"dev": "webpack-dev-server --config ./build/webpack.dev.js --mode development --open",
"test": "echo \"Error: no test specified\" && exit 1"
},
}
这个时候重新在命令终端执行
// 执行build命令,重新打包
⇒ npm run build
// 执行dev命令
⇒ npm run dev
这仅仅是最基础部分的优化,因为我们的配置还是最简单的,后续我们添加了别的配置之后,也会在相应的步骤做相应的优化。
Step04: 配置React开发环境
请将分支切换到
step4
查看代码。
经过前面三步,我们完成了Webpack的基本配置,知道文件入口,出口,打包以及开发,生产等环境。接下来,我们来给工程配置React相关的环境。
React的环境需要先安装react
和react-dom
。所以先在命令终端中执行下面的命令:
⇒ npm i react react-dom --save
执行完上面的命令之后,在package.json
文件中的dependencies
增加了ract
和react-dom
相应的信息:
// package.json
{
"dependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
}
为了验证React相关的环境是否能正常工作,将/src/index.js
中的内容做一些修改:
// /src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App'
ReactDOM.render(<App />, document.getElementById('root'))
在这个index.js
中引用了App
组件。所以我们在/src
目录下新增components/
目录,并在该目录下新增App.js
:
// src/components/App.js
import React from 'react';
export default class App extends React.Component {
render() {
return (
<h1>Hello Webpack and React! (^_^)</h1>
)
}
}
另外在/src/
新增一个模板文件index.html
:
<!-- /src/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello Webpack (^_^) ~~~</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
这个时候你在命令终端不管是执行npm run build
还是npm run dev
都无法正常运行,会报错:
首先我要告诉你的是Step04
这一步的操作并没有任何问题,主要是在编译的过程中缺少必要的东西。那就是Babel相关的配置。接下来的Step05
将会添加Babel相关的配置。
Step05:添加Babel相关的配置
请将分支切换到
step5
查看代码。
在Step04
中会失败主要是因为Webpack只识别JavaScript文件,而且只能编译ES5。实际上ES6(甚至后面要说的JSX),Webpack它根本不认识。那么要解决这个问题,就需要借助Babel来处理。先安装需要的插件:
⇒ npm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-transform-modules-commonjs @babel/preset-react -D
⇒ npm i @babel/runtime --save
执行完上面的命令之后,package.json
文件在dependencies
和devDependencies
添加了新的配置信息:
// package.json
{
// ...省略的信息可以查看原文件
"dependencies": {
"@babel/runtime": "^7.4.5",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/plugin-transform-modules-commonjs": "^7.4.4",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.6",
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2",
"webpack-merge": "^4.2.1"
}
}
接着在webpack-sample
根目录下创建.babelrc
文件来配置Babel:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"> 1%",
"last 5 versions",
"ie >= 8"
]
}
}
],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-modules-commonjs"
]
}
有关于Babel更详细的配置可以点击这里阅读,这里不再做过多的阐述。
最后在webpack.common.js
配置文件中的module
中添加rules
来处理.js
和.jsx
文件,这也是我们添加的第一个有关于Webpack的Loader相关的东西:
// webpack.common.js
module.exports = {
// ... 省略的信息查看原文件代码
// 模块解析
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
}
这个时候执行npm run build
可以正常的执行,但是执行npm run dev
还是会报错:
执行npm run dev
不成功是因为我们的模板文件没有自动插入到/dist
目录下。为了让/src/
目录下的模板文件index.html
能自动编译到/dist
目录下,并且所有的.js
引用能自动插入到index.html
中。我们需要使用Webpack的两个插件:
- 生成
index.html
文件,并且自动插入到/d
如需转载,烦请注明出处:https://www.w3cplus.com/javascript/webpack-config-part1.html
如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!