关于Webpack前端工程化构建,你必须要掌握这些核心知识点_json


关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_02



引言

在很久之前,模块化管理还没有出现,如果我们开发一个页面想要引入一些依赖的话,最常见的做法就是将依赖文件引入到​​.html​​​文件中。比如,我们要使用​​JS​​​的一些依赖库,就要在​​.html​​​文件中使用​​<script>​​​标签引用;要引用​​CSS​​​的依赖就要使用​​<link>​​标签。如果页面中引入的依赖文件太多,那么向服务发送的请求也随之增多,势必会拖慢网页的加载速度,影响用户体验。另外,网页的内容也会变得很臃肿,增加维护的难度。

在移动互联网时代的网站,正在逐渐演化成Web应用(Web Application,简称​​WebAPP​​​),浏览器也在此之际不断的发展壮大,各种基于​​JavaScript​​​语言的框架横空出世,Web前端发展速度着实之快,让我们也不得不加快学习的脚步。现在的Web前端更倾向于单页面应用(single-page application,简称​​SPA​​),减少页面的刷新次数,这就造成了庞大的页面代码管理问题,如果管理不好会导致很多问题,比如各个模块耦合度变高、难以维护等等。这就催生了模块管理器。

简单来说,​​Webpack​​就是一个“模块打包机”,它的主要工作就是分析项目中的结构找到​​JavaScript​​​模块,根据各个模块之间的依赖关系进行静态分析,然后打包成一个独立的静态模块供浏览器调用,这样就可以大大减少请求次数,提供网页的性能,提高用户的体验。​​Webpack​​​还有一个作用就是把浏览器目前还解释不了的扩展语言(例如​​Scss​​​、​​TypeScript​​​等)进行编译,转换成浏览器可以识别的内容。​​React​​​中使用的是​​ES6​​​的语法,在一些主流的浏览器上还不支持​​ES6​​​,所有需要对​​Webpack​​进行配置后,React才能正常运行。

​Webpack​​​不仅是学习前端框架的前提,也是同学们将来面试必问、笔试必考、工作必用的内容,随着前端工程化的发展,​​Webpack​​​正在变得越来越重要,尤其对于大型的一线互联网公司,会不会​​Webpack​​甚至能直接决定你是否能被录用。我从一个技术小白到全栈工程师,也经历了前端开发从刀耕火种到百家争鸣的各个阶段,在这个过程中沉淀了很多知识,也积累了大量的实践经验,也希望通过我的知识分享,让更多同学受益,避免大家踩坑。

1、Webpack简介与环境搭建

现在的网页开发越来越复杂,我们可以把网页看做是一个功能丰富的应用,为了实现复杂的功能,就需要导入很多的​​JavaScript​​​库和一大堆依赖包。为了简化开发的复杂度,前端社区也涌现出了很多的方法,以便于提高开发效率。但是利用一些方法开发的文件往往需要进行额外的处理才能让浏览器识别,而且手动处理又非常的繁琐,这就为​​Webpack​​这样的工具诞生提供了需求。

1.1、模块化与工程化

随着Web前端的不断发展,前端开发也正在逐渐往Web应用的方式转变,应用场景也变的越来越复杂,需要更新的技术来解决开发中遇到的问题。前端模块化和工程化也呼之欲出,前些年比较流行的构建工具有​​Grunt​​​、​​Gulp​​​、​​FIS​​​等,但是经过近几年​​React​​​、​​Vue​​​框架的发展,不在使用传统的方式来操作​​DOM​​。在这个过程中,前端逐渐发展成了模块化和单页应用为主的形式。

​Webpack​​也就是在这样的发展潮流中,被更多的人视为主流的前端构建工具。这也就引出了我们现在要讲的模块化和工程化。

百度百科是这样解释模块化的:


模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,有多种属性、分别反映其内部特性。


在我们的生活中也经常会见到模块化的应用场景,比如现在的家用电器(电视机、电脑主机等)都是模块化的,如果这些家电坏了,维修的时候一般都是直接拿个新的模块更换一下就可以了,由此可以看出,模块化不仅仅是编程语言的概念。模块化应用到生活中,同样是可以提高办事效率的。

那我们主要说的前端模块化具体指什么呢?

前端模块化一般指的是​​JavaScript​​​的模块,最常见的是​​Node.js​​​中的​​NPM​​​包管理,有了模块化,我们在写代码的时候就避免了很多的重复工作,也不在只是做copy的事情了。有了模块化之后,开发者可以把更多的时间精力放到业务逻辑和代码的维护上。目前,有很多主流的模块化规范,比如​​CommonJS​​​、​​AMD​​​、​​ES6 Module​​​等规范,甚至在​​CSS​​​中也采用了​​@import​​​的方式实现模块化。​​Less​​​和​​Sass​​​作为​​CSS​​​的预处理语言,使用了​​@import​​​来导入一些变量、函数和​​mixin​​的定义。

接下来,我们在聊聊什么是工程化。当开发Web应用的场景越来越复杂时,我们所面临的问题也会随之增加:

  1. 在大型项目中,多模块下如何管理依赖?
  2. 页面复杂度提升之后,多页面、多系统、多状态怎么办?
  3. 团队协作开发中如何统一规范?
    ……

以上这些问题都是在软件工程中必须要解决的问题,工程化问题需要运用工程化工具来解决。在早期,前端工程化主要是以​​Grunt​​​、​​Gulp​​等构建工具为主,在这个时期解决的是重复任务的问题,他们将某些功能拆解成固定步骤的任务,然后编写工具来解决,比如:图片压缩、地址添加hash等,都是固定套路的重复工作。

在近几年,前端工程化的发展得益与​​Node.js​​​的发展,​​Webpack​​的插件机制解决了前端资源依赖管理的问题,从而进化成了一整套前端工程化解决方案。

1.2、什么是Webpack


关于Webpack前端工程化构建,你必须要掌握这些核心知识点_json_03


官方解释:


本质上,​​webpack​​​ 是一个现代​​JavaScript​​​ 应用程序的静态模块打包器(module bundler)。当 ​​webpack​​​ 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 ​​bundle​​。


一般像​​Grunt​​​、​​Gulp​​​这类的构建工具,打包思路是:先遍历源文件,然后匹配规则,最后再打包,这个过程很耗费资源。而​​Webpack​​​可以做到按需加载,与其他构建工具不同之处在于:​​Webpack​​是从入口文件开始,经过模块依赖加载、分析和打包三个流程完成项目的构建。在加载、分析和打包的三个过程中,可以针对性的做一些解决方案,达到按需加载的目的,比如拆分公共代码(code split)等。

​Webpack​​​在打包时遵循“一切皆模块”的思想,即​​JS​​​是模块,​​CSS​​​等文件也是模块,还可以将​​ES6​​​转为​​ES5​​​,并且可以对​​Less​​​、​​Sass​​​这些​​CSS​​​预处理器进行编译。在打包过程中通过强大的​​Loader​​​和插件机制来完成解决方案的封装,最后对项目进行压缩和优化等操作。​​Webpack​​解决了传统构建工具实现不了的问题,被更多的前端开发者使用。

1.3、Webpack环境搭建

1.3.1、安装Node.js

​Webpack​​​实际是用​​Node.js​​​写的,所以要先安装​​Node.js​​环境。

首先进入​​Node.js的官网​​,选择对应系统下载安装包:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_04


对于 ​​windows​​​ 用户,直接下载安装包安装即可,如果是 ​​Macos​​​ 用户,推荐使用 ​​brew​​ 进行安装。

​Node.js​​​有很多版本,包括稳定版和开发版,不同的项目需要的​​Node.js​​​版本不同,推荐大家安装 ​​8.x​​ 以上的版本。

1.3.2、NPM简介与常用命令

(1)NPM包管理工具

简单来说,​​NPM​​​(Node Package Manager)是包含在​​Node.js​​​里面的一个包管理工具,​​NPM​​​会随着​​Node.js​​​一起安装,我们可以在命令提示符(以下演示的命令均为windows系统环境)中查看​​NPM​​的版本:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_05


​NPM​​​为开发者提供了一个代码模块共享的大平台,当我们项目中需要使用某个模块(​​JavaScript​​​包)时,可以直接使用​​NPM​​​包管理工具来下载对应的包并安装,我们也可以把自己用​​Node.js​​写的代码发布到平台上供他人使用。

在搭建一个前端项目之前,通常会在项目的根目录下生成一个名为​​package.json​​​的文件作为​​NPM​​​包的描述文件,使用该文件来定义项目信息、配置包依赖关系。​​package.json​​文件可以自己手动创建,也可以使用命令来创建:

npm init

在命令提示符中输入上面命令(先 ​​cd​​​ 到项目根目录下,再执行命令)后,会向用户提问一系列问题,根据对应的提示回答问题,如果不回答直接输入回车,程序会按照默认选项进行配置。期间使用 ​​-f​​​(表示​​force​​​)、​​-y​​​(表示​​yes​​​)会跳过提问阶段,直接生成一个新的​​package.json​​文件。

文件中包含了​​NPM​​包的基本信息(项目名称、版本号、项目描述、作者)和依赖管理,例如:

{
"name": "demo",
"version": "1.0.0",
"dependencies": {
"webpack": "^4.29.6"
}
}

name:表示我们当前的项目名称,如果将来发布到​​npmjs.com​​​平台上,会以这个名字来命名,还有一种命名方式是​​@scope/name​​,表示作用域包。

version:是当前项目的版本,​​NPM​​​是围绕 ​​语义版本控制(semver)​​​思想而设计的,这种规范是以 ​​主版本号.次版本号.修订号​​​(​​MAJOR.MINOR.PATCH​​)的描述形式,其分别表示的意思是:

  • 主版本号:当你做了不兼容的​​API​​修改;
  • 次版本号:当你做了向下兼容的功能性新增;
  • 修订号:当你做了向下兼容的问题修正(例如,修改了一个bug);

dependencies:是​​demo​​​这个项目中的依赖,还有​​devdependencies​​​也是描述当前项目的依赖,这二者的区别我们会在下面进行详细的说明。上面​​package.json​​​示例中​​dependencies​​​里存放了有关于​​webpack​​​的版本号,版本号前面有个​​^​​​,意思是主版本是​​4​​的最新版本,每次执行安装命令的时候,会更新符合这个规则的最新包。

(2)NPM的常用命令

我们常用的​​NPM​​命令主要是做四种操作:安装、删除、初始化、配置。


安装某个NPM


npm install packageName
## 简写
npm i packageName


安装package.json中的所有依赖


npm install


安装指定版本的包


npm i packageName@x.x.x


全局安装依赖包


npm i packageName --global
## 简写
npm ipackageName -g


安装依赖包,并且将依赖写入package.json文件的​dependencies​部分


npm i packageName --save
## 简写,简写中-S是大写字母
npm i packageName -S


安装依赖包,并且把依赖写入package.json文件的​devdependencies​部分


npm i packageName --save-dev
## 简写
npm i packageName -D


删除某个NPM


npm uninstall packageName


初始化一个NPM项目,自动生成​package.json​文件


npm init


单次设置镜像,使用淘宝NPM镜像替换官方​NPM​镜像


由于​​NPM​​​网站属于境外服务器,所以我们为了保证下载​​NPM​​​包时的网络稳定性,会将下载镜像换成国内的镜像,其中​​淘宝NPM镜像​​​是国内最大的一家​​NPM​​​镜像网站,在下载​​NPM​​​包时,使用 ​​cnpm​​​ 命令代替原来的 ​​npm​​ 命令。

npm [命令] --registry=https://registry.npm.taobao.org


设置默认npm使用淘宝镜像


npm config set registry https://registry.npm.taobao.org


安装cnpm包,安装成功后​npm​命令更换为​cnpm​命令


npm install -g cnpm --registry=https://registry.npm.taobao.org
## 安装成功之后,直接像使用npm一样使用cnpm即可,例如:安装某个包就变成了
cnpm i packageName


设置环境变量


npm set xxx
## 例如:
npm set init-author-name 'Your name'


查看某个包的信息


npm info
## 例如:
npm info lodash


查找npm仓库


npm search
## 后面可以跟字符串或者正则表达式,例如:
npm search webpack


树形的展示当前项目安装的所有模块,以及对应的依赖


npm list
## 例如,查看全局安装的模块:
npm list --global

(3)dependencies 和 devDependencies 区别

在上面关于​​NPM​​​常用命令中,我们有讲到 ​​dependencies​​​ 和 ​​devDependencies​​ 的命令分别是:

# 自动把模块和版本号添加到dependencies部分,将依赖安装到生产环境
npm install module-name --save
# 自动把模块和版本号添加到devdependencies部分,将依赖安装到开发环境
npm install module-name --save-dev

很多同学不太理解什么是开发环境和生产环境,简单来说,就是在项目的开发阶段就是开发环境;项目上线了,开始正式提供对外服务,在生产环境下,一般会关掉错误报告,打开错误日志等操作。

​devdependencies​​​配置的是开发环境,安装项目开发时所依赖的模块。比如像​​webpack​​​工具,只是用来构建项目和打包,这些都是在开发阶段才使用的,等项目上线后就用不到​​webpack​​​工具了,那么我们就可以把​​webpack​​​安装到开发环境中(使用 ​​--save-dev​​​命令安装到​​devdependencies​​下);

​dependencies​​​是生产环境,安装项目运行时所依赖的模块。比如​​jQuery​​​库,,等项目上线以后依然是要继续使用的,我们就要安装在生产环境中(使用 ​​--save​​​ 命令安装到​​dependencies​​下),如果没有把需要的依赖安装到生产环境中,项目上线运行时就有可能会报错。

(4)本地模式和全局模式

我们在安装​​NPM​​包时,有两种模式可选:一是本地模式,二是全局模式。

默认是本地模式安装,本地模式是指在执行​​npm install​​​命令后,会在执行命令的目录下创建​​node_modules​​​目录,然后再把下载的依赖和安装包保存到​​node_modules​​目录下。

全局模式是指将下载的依赖和安装包保存到全局路径下的方式,在​​Node.js​​​中使用​​require​​​依赖时,会优先查找自己当前文件中的​​node_modules​​​目录,如果没有,在循环遍历上层的​​node_modules​​,如果还找不到依赖,就会去全局模式下的安装目录寻找。

在​​package.json​​​中增加​​bin​​​字段,并且指向包内对应的文件映射路径,就可以把该文件放到​​PATH​​​中,使用​​npm​​命令执行了,例如:

"bin": {
"testCmd": "bin/cmd.js"
}

配置好​​bin​​​字段后,在项目根目录输入 ​​npm link xxx​​​ 就可以执行​​testCmd​​​命令了, 使用 ​​npm link​​​ 命令,将 ​​npm​​ 模块链接到对应的运行项目中去,方便地对模块进行调试和测试 。

(5)修改NPM全局模式的默认安装路径

一般情况下,我们安装​​Node.js​​​环境,程序会自动把​​NPM​​​全局模块的路径设置在系统盘(通常是​​C​​盘下),我们在项目开发阶段不建议全局路径设置在系统盘,不但会影响电脑的性能,而且还很不安全。可以通过以下命令来设置默认下载的全局路径目录:


输入命令,查看当前配置


npm config ls

运行结果:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_html_06


如果是第一次使用​​NPM​​​安装包的话,在配置中只会看到​​prefix​​​的选项,就是​​NPM​​​默认的全局安装目录。但是如果有多次使用​​NPM​​​安装包的话,就会看到​​cache​​​和​​prefix​​两个路径,如下图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_html_07


第一步:

在欲更改的目录下新建两个文件夹,分别是:​​node_global_modules​​​ 和 ​​node_cache​​,效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_json_08


第二步:

打开命令提示符,执行下面两条命令:

npm  config set prefix "D:\dev\nodejs\node_modules\npm\node_global_modules"
npm config set cache "D:\dev\nodejs\node_modules\npm\node_cache"

执行成功后,可以用 ​​npm config ls​​ 命令查看配置的结果,效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_09


第三步:

验证配置成功后,需要配置环境变量。在环境变量中,新建一个系统变量,变量名:​​NODE_HOME​​​,变量值:​​D:\dev\nodejs​​,效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_前端_10


在​​Path​​变量名中,新建变量值:

​%NODE_HOME%​​​ ​​%NOED_HOME%\node_modules​​​ ​​%NODE_HOME%\node_modules\npm\node_global_modules\​

效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_json_11


保存之后,可以重新执行全局安装命令,查看​​NPM​​依赖包在全局的默认安装目录是否为我们配置好的目录,如果成功下载到了设置后的目录下,就成功修改了默认全局安装路径。

(6)NPM Scripts

​NPM​​​不仅可以用于模块管理,还可以用于执行脚本。可以在​​package.json​​​文件中添加​​scripts​​​字段,用于指定脚本命令,以供​​NPM​​直接调用,示例如下:

// package.json
{
"scripts": {
"build": "webpack",
"start": "node src/scripts/dev.js"
}
}

​scripts​​​里面放的是​​npm​​​要执行的命令,格式是​​key-value​​​形式,为了简化操作,具体命令为​​value​​​,自定义的简化命令为​​key​​​,当​​npm​​​运行​​key​​​命令时,等同于执行后面的​​value​​​命令。例如,在​​package.json​​​添加上面字段之后,可以直接使用​​npm run build​​​和​​npm run start​​​命令了 ,而且​​npm run start​​​命令还可以简写成​​npm start​​。


注意:上面提到的简写,只有​​npm run start​​才可以简写,并不是所有script命令都支持这种简写的方式。

除了 ​​npm​​​ 外,还有一些包管理工具,主要是针对 ​​npm​​​ 的下载速度慢、​​node_modules​​​ 混乱等缺点设计的,例如​​yarn​​​和 ​​pnpm​​。


在后面会讲到使用​​create-react-app​​​脚手架工具搭建​​React​​​开发环境,在自动生成的​​package.json​​​文件中配置的​​scripts​​字段内容是:

"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}

我们在命令窗口中直接执行​​npm start​​​就相当于执行了​​npm react-scripts start​​​命令,通过这个命令可以启动​​React​​​服务,并且在浏览器输入​​IP​​地址就可以访问React项目了。

1.3.3、安装Webpack

(1)安装Webpack

​webpack​​​的安装需要使用​​NPM​​来完成,安装命令如下:

## 全局安装
npm install -g webpack
## 安装到项目目录中
npm install --save-dev webpack

安装好之后,就可以使用​​webpack​​的命令了。

(2)安装Webpack-cli

​Webpack-cli​​​是 ​​Webpack​​​ 的 ​​CLI​​ (Command-line interface)工具,如果在项目中,我们可以使用下面的方式安装:

# 本地安装
npm install webpack-cli --save-dev
# 全局安装
npm install webpack-cli -g

由于​​webpack​​​是用于项目构建和打包的工具,所以我们在安装时使用​​--save-dev​​​命令把​​webpack-cli​​放到开发环境的字段下。

2、Webpack中常规配置项介绍

2.1、webpack.config.js 配置文件

当我们安装好​​webpack​​​之后,就可以使用​​webpack​​​命令了,比如要将一个​​main.js​​​文件打包成一个​​bundle.js​​文件,可以使用如下命令:

webpack main.js bundle.js

一般在实际的项目开发中,要把这些命令写到一个​​webpack.config.js​​的文件中。上面打包操作可以做如下配置:

module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};

在上面的配置文件中,我们配置了项目的入口(​​entry​​​)和出口(​​output​​​),在该项目的关系图中,​​entry​​​字段指定了入口文件,即把​​main.js​​​作为起点。​​output​​​字段是指定了输出位置,即指定​​webpack​​把整理后的资源放在哪里。

2.2、webpack核心概念

​webpack​​主要有4个核心概念:

入口(​​entry​​​):项目入口,也是​​webpack​​所有依赖关系图的起点。

出口(​​output​​​):指定​​webpack​​打包应用程序的目录。

加载器(​​loader​​):加载需要处理的模块,对模块进行转换处理。

插件(​​plugins​​):定义项目要用到的插件。

2.2.1、webpack入口(entry)和出口(output)

在前面的内容中,我们已经了解到了,​​webpack​​​是一个模块打包工具,需要处理具有依赖关系的各个模块,这些模块会构成一个依赖关系图(dependency graph)。​​webpack​​​的入口就是这张关系图的起点,指的是入口文件。​​webpack​​出口指的是需要把这张关系图导出到哪个文件中,即导出文件。

(1)入口(entry)

​entry​​字段指定了入口文件,也可以理解为当前项目启动时运行的第一个文件,语法为:

entry: string | object | Arrary<string>

​entry​​​字段支持多种类型,值可以是字符串、对象、数组。简单来理解,就是​​entry​​​可以指定一个文件入口,也可以指定多文件入口。我们举例来说明​​entry​​的用法:

单文件入口示例

// 使用key-value方式
module.exports = {
entry: 'path/to/my/entry/main.js'
};
// 或者使用对象方式
module.exports = {
entry: {
main: 'path/to/my/entry/main.js'
}
};
// entry也可以使用数组指定多个入口文件路径,输出时会合并输出
module.exports = {
mode: 'development',
entry: ['./src/app.js', './src/home.js'],
output: {
filename: 'array.js'
}
};

多文件入口

// entry配置3个独立的入口文件,会打包成3个对应的bundle
module.exports = {
entry: {
home: 'path/to/my/entry/home.js',
search: 'path/to/my/entry/search.js',
list: 'path/to/my/entry/list.js'
}
};

(2)出口(output)

​output​​​字段是指定​​entry​​​对应文件编译打包后的输出位置,也可以理解为指定​​webpack​​​把整理好的资源放到哪里。​​output​​字段常用的属性有:

  • ​path​​:指定打包完成的文件存放的路径;
  • ​filename​​:指定打包完成的文件的文件名称;
  • ​publicPath​​:指定一个在浏览器中被引用的URL地址;

如果不指定​​path​​​参数,将默认将打包文件输出到​​webpack.config.js​​​同级目录下;如果不指定​​output​​​,打包文件会默认输出到​​dis/main.js​​​,即​​output​​​字段的​​path​​​属性默认是​​dis​​​,​​filename​​​属性默认是​​main​​。

一个​​webpack​​​的配置可以包含多个​​entry​​​,但是只能有一个​​output​​​。对于不同的entry可以通过​​output.filename​​占位符语法来区分,例如:

module.exports = {
entry: {
home: 'path/to/my/entry/home.js',
search: 'path/to/my/entry/search.js',
list: 'path/to/my/entry/list.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};

在上面的配置中,​​output.filename​​​指定的​​[name]​​​就是一个占位符,对应的是​​entry​​​中的​​key​​​(​​home​​​、​​search​​​、​​list​​),最终输出的结果是:

path/to/my/entry/home.js → dist/home.js
path/to/my/entry/search.js → dist/search.js
path/to/my/entry/list.js → dist/list.js

目前​​webpack​​支持的占位符有:

占位符

含义

​[hash]​

模块标识符的​​hash​

​[chunkhash]​

​chunk​​​内容的​​hash​

​[name]​

模块的名称

​[id]​

模块标识符

​[query]​

模块的​​query​​​,例如,文件名 ​​?​​ 后面的字符串

​[function]​

一个 ​​return​​​ 出一个 ​​string​​​ 作为 ​​filename​​ 的函数

2.2.2、webpack 加载器(loader)

​loader​​​是解析处理器,大家都知道,​​webpack​​​的任务就是把具有依赖关系的各个文件进行整合并打包,这些文件的类型有很多,比如​​.html​​​、​​.css​​​、​​.js​​​、​​.scss​​​、​​.jpg​​​等等。但是​​webpack​​​只认识​​JavaScript​​​文件,那如何识别其他文件呢?​​loader​​​就解决了这个问题。比如,很多浏览器不支持​​ES6​​​语法,​​webpack​​​可以通过​​loader​​​配置,将​​ES6​​​语法转化为​​ES5​​​的语法,还可以将图片转化为​​base64​​​的​​dataURL​​​ ,还可以通过​​loader​​​直接在​​JavaScript​​​文件中使用​​import​​​ 引入​​css​​​和​​html​​。

​loader​​​在​​webpack​​​构建文件的过程中起着至关重要的作用,实现可以通过​​loader​​​识别出要对哪些文件进行预处理,然后​​loader​​​转换这些需要预处理的文件,并添加到​​bundle​​​(构建后的模块)中。在​​React​​​框架开发时,经常会用到​​JSX​​​这种扩展语言来编写​​DOM​​​,目前几乎所有的浏览器都不支持​​JSX​​​格式,那么​​loader​​​就可以在使用​​JSX​​​之前做一些预处理操作,将其转化成​​JavaScript​​语言,示例如下:

module.exports = {
entry: {
app: './app.js'
}
output: {
filename: 'bundle.js',
path: './dist'
}
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: 'css-loader'
}
]
}
}

在上面的例子中,​​test​​​字段表示要对哪些类型的文件进行构建,​​use​​​字段表示要用哪些模块对该类型的文件进行构建。在​​test​​​项中使用​​/\.css$/​​​这种正则表达式来匹配需要处理的模块文件,即匹配以​​.css​​为后缀的文件。

在配置​​loader​​​之前,​​use​​中的模块是需要安装的,命令如下:

npm install --save-dev babel-loader
npm install --save-dev css-loader


提示:

在​​webpack​​​的早期版本中,​​loader​​的写法是:

module.exports = {
......
module: {
loaders: [
{
test: /\.(js|jsx)/,
loader: 'babel-loader'
},
......
]
}
}

​webpack​​​最新版本中已经废弃了​​loaders​​​、​​loader​​​的写法,改成了 ​​rules​​​、​​use​​。


2.2.3、webpack 插件(plugin)

​plugin​​​在​​webpack​​​起着重要的作用,通过​​plugin​​​可以解决​​loader​​​解决不了的问题,以此来丰富​​webpack​​​的功能。​​webpack​​​本身就是有很多插件组成的,所以内置了很多插件。除了内置插件,我们也可以通过​​NPM​​​包的方式来使用插件,比如现在需要安装一个​​html-webpack-plugin​​的插件,先执行安装命令:

npm install --save-dev html-webpack-plugin

然后在​​webpack.config.js​​​文件中配置​​plugins​​:

var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './app.js'
},
output: {
filename: 'bundle.js',
path: './dist'
},
module: {
rules: [
{
test: /\.(js|jsx)/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: 'css-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin()
]
}

上面的示例中,​​plugins​​​参数为一个数组,可以传入多个​​plugin​​​,另外需要注意​​plugin​​​是可以带参数的,所以​​plugins​​​属性传入的必修为​​new​​实例。

3、使用Babel转换JavaScript代码

3.1、什么是Babel

​Babel​​​是一个​​JavaScript​​​的编译器,我们在​​webpack​​​中可以使用​​ES6​​​以上版本的语法写代码,但是目前主流的浏览器仅支持​​ES5​​​语法,通过​​Babel​​​可以将​​ES6+​​​语法自动转换成​​ES5​​​语法,来适配浏览器的​​JavaScript​​解释器。

比如说,我们使用​​ES6​​语法中的箭头函数实现代码:

(val) => (val + 1) ;

使用​​Babel​​​转化成​​ES5​​语法后:

function(val){
return val+1 ;
}

3.2、babel-cli命令行工具的使用

在项目中安装​​babel-cli​​,命令如下:

npm install --save-dev @babel/core @babel/cli
## 简写
npm i -D @babel/core @babel/cli

然后再安装转换规则:

# 安装 preset-env
npm i -D @babel/preset-env
# 执行 CLI 添加--presets
npx babel babel.js --presets=@babel/preset-env

测试​​babel-cli​​​命令行工具,先创建一个​​app.js​​文件,输入下面的代码:

[1, 2, 3].map(n => n ** 2);

然后在命令行中输入下面命令:

npx babel app.js
## 或者 指定输出结果到固定文件
npx babel app.js --out-file appout.js
## 简写
npx babel app.js -o appout.js

转化命令执行成功后,​​app.js​​​的语法就变成了​​ES5​​语法:

[1, 2, 3].map(function(n) {
return Math.pow(n, 2);
});

3.3、Babel配置文件

​Babel​​还支持配置文件的方式进行转化操作,配置文件支持两种形式:

(1)使用​​package.json​​​的​​babel​​属性

示例:

{
"name": "demo",
"version": "1.0.0",
"babel": {
"presets": ["@babel/preset-env"]
}
}

(2)使用​​.babelrc​​文件

在项目的根目录创建​​.babelrc​​​或​​.babelrc.js​​​文件,​​Babel​​​会在正在被转义的文件当前目录中查找一个​​.babelrc​​​文件, 如果不存在,它会向外层目录遍历目录树,直到找到一个 ​​.babelrc​​​ 文件,或一个 ​​package.json​​​ 文件中有 ​​"babel": {}​​ 。 在文件中配置如下:

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

3.4、在webpack中使用babel

第一步:安装依赖包

# 安装开发依赖
npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
# 将 runtime 作为依赖
npm i @babel/runtime -S

第二步:创建​​webpack.config.js​​文件,配置如下:

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

把​​Babel​​​配置到​​webpack.config.js​​​文件的​​options​​​中,或者是配置到​​.babelrc​​​文件, 或者使用​​package.json​​​的 ​​babel​​ 字段。

4、配置webpack-dev-server本地服务器

​webpack-dev-server​​​ 就是一个​​Express​​​的小型服务器,通过​​Express​​​的中间件 ​​webpack-dev-middleware​​​ 和 ​​Webpack​​进行交互的。

4.1、安装webpack-dev-server服务

在项目中安装:

npm i webpack-dev-server

启动服务:

npx webpack-dev-server

服务启动成功后,在浏览器中访问​​webpack-dev-server​​​的默认地址 ​​localhost:8080​​​,就可以默认的​​index.html​​页面,如果没有该页面,就会显示文件目录。

4.2、整合Webpack

将​​webpack-dev-server​​​服务整合到​​webpack​​​的配置文件中,​​webpack.config.js​​配置文件内容如下:

const path = require('path');
module.exports = {
//...
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 9000 //服务器端口
}
};

还可以将​​webpack-dev-server​​​放到​​package.json​​​的​​scripts​​里面,来简化服务启动命令:

{
"scripts": {
"dev": "webpack-dev-server --mode development --config webpack.config.dev.js --hot --inline --port 3000"
}
}

配置完成后,可以使用下面命令启动服务:

npm run dev

执行​​npm run dev​​​实际就是执行的对应​​webpack-dev-server​​命令。

4.3、配置热加载

热加载也叫模块热替换( Hot Module Replacement ,简称​​HMR​​​),配置热加载后,在应用运行期间修改代码,不需要重新刷新页面就可以在浏览器中加载更新后的内容。 通过配置 ​​webpack.HotModuleReplacementPlugin​​​ 插件来开启全局的 ​​HMR​​ 功能。

修改​​webpack.config.js​​文件,开启热加载:

const path = require('path');
module.exports = {
entry: './src/index.js',
devServer: {
open: true, //自动打开浏览器
contentBase: path.join(__dirname, 'dist'),//指定服务器将从哪个目录去查找内容文件
port: 9000, //服务器端口
hot: true // 开启 hmr 支持
},
plugins: [
// 添加 hmr plugin
new webpack.HotModuleReplacementPlugin()
]
};

完成上面的配置后,重启服务,在浏览器访问默认地址 ​​http://localhost:9000​​​ ,然后再修改​​JavaScript​​代码,就可以实时看到效果了。

如果使用​​webpack-dev-server​​​的​​cli​​功能只需要通过执行下面命令,就可以开启自动刷新功能:

webpack-dev-server --hot --inline

执行上面命令会自动将​​webpack.HotModuleReplacementPlugin​​​这个插件添加到 ​​Webpack​​ 的配置中去。这种是最简单的配置方式了。

4.4、本地服务器的常见配置项

常见配置如下:

# 修改端口号和 host
webpack-dev-server --port 3000 --host 127.0.0.1
# 启动inline 模式的自动刷新
webpack-dev-server --hot --inline
# 手动指定 webpack config 文件
webpack-dev-server --config webpack.xxx.js
# 指定 webpack 的 mode
webpack-dev-server --mode development
# watch 功能,文件发生变化则触发重新编译
webpack-dev-server --watch
# dev-server默认会将工作目录(当前目录)最为基本目录,可以手动修改它
webpack-dev-server --content-base ./build
# 查看帮助
webpack-dev-server -h
# 开启inline模式,自动刷新页面
webpack-dev-server --hot --inline

5、配置React开发环境

5.1、使用webpack配置React环境

5.1.1、搭建React项目

创建​​React​​​项目的文件夹,在项目根目录下打开命令提示符,执行初始化命令,生成​​package.json​​文件:

npm init -y

依次执行下列命令,如果已经安装了​​cnpm​​​的话,可以使用​​cnpm​​​替代​​npm​​命令,执行命令完成基础创建:

# 安装 react react-dom依赖
npm i react react-dom
# 安装 webpack 和 webpack-cli 开发依赖
npm i webpack webpack-cli -D
# 安装 babel
npm i babel-loader @babel/core @babel/preset-env -D
# 安装 babel preset-react
npm i @babel/preset-react -D

创建​​webpack.config.js​​​配置文件,并在配置文件中添加对​​JSX​​​语法的​​Babel​​编译支持:

module.exports = {
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/, // jsx/js文件的正则
exclude: /node_modules/, // 排除 node_modules 文件夹
use: {
// loader 是 babel
loader: 'babel-loader',
options: {
// babel 转义的配置选项
babelrc: false,
presets: [
// 添加 preset-react
require.resolve('@babel/preset-react'),
[require.resolve('@babel/preset-env'), {modules: false}]
],
cacheDirectory: true
}
}
}
]
}
};

在项目根目录创建 ​​src/App.jsx​​​ 文件,​​src​​​是源文件目录,​​App.jsx​​文件内容如下:

import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return (
<div>
<h1>Hello React and Webpack</h1>
</div>
);
};
export default App;
ReactDOM.render(<App />, document.getElementById('app'));

在​​src​​​目录下创建​​index.jsx​​文件,内容如下:

import App from './App'; // 这里可以省略.jsx

将文件添加到​​webpack.config.js​​​中的​​entry​​:

module.exports = {
entry: './src/index.jsx',
// ...
};

接下来在​​src​​​目录下创建​​index.html​​文件,作为项目的模板,内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Hello React Webpack</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

需要使用 ​​html-webpack-plugin​​​ 插件来复制 ​​index.html​​​ 到 ​​dist​​ 文件夹下。

首先是安装 ​​html-webpack-plugin​​:

npm i html-webpack-plugin -D

然后修改​​webpack.config.js​​配置:

const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebPackPlugin({
template: 'src/index.html',
filename: 'index.html',
inject: true
})
]
};

上面的操作都完成之后,可以执行打包命令:

npx webpack --mode development

运行结果:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_json_12


打包成功后,会在项目的根目录下自动生成​​dist​​​文件夹,存放的是​​webpack​​​编译打包后的文件。这时,我们就可以把打包的命令放到​​package.json​​​文件的​​scripts​​中,配置如下:

//package.json文件
{
"scripts": {
"build": "webpack --mode production"
}
}

配置好了以后,我们就可以使用简化的命令来操作打包了:

npm run build

5.1.2、配置React项目的本地服务

(1)配置服务

第一步:安装​​webpack-dev-server​​依赖:

npm i webpack-dev-server -D

第二步:在​​webpack.config.js​​配置文件中添加服务相关配置,完整配置如下:

//webpackage.config.js
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
devServer: {
contentBase: path.join(__dirname, './src/'),
publicPath: '/',
host: '127.0.0.1',
port: 3000,
stats: {
colors: true
}
},
entry: './src/index.jsx',
// 将 jsx 添加到默认扩展名中,省略 jsx
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/, // jsx文件的正则
exclude: /node_modules/, // 排除 node_modules 文件夹
use: {
// loader 是 babel
loader: 'babel-loader',
options: {
// babel 转义的配置选项
babelrc: false,
presets: [
// 添加 preset-react
require.resolve('@babel/preset-react'),
[require.resolve('@babel/preset-env'), {modules: false}]
],
cacheDirectory: true
}
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: 'src/index.html',
filename: 'index.html',
inject: true
})
]
};

第三步:修改​​package.json​​​文件的​​scripts​​​配置,添加​​start​​字段:

{
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --mode development --open"
}
}

第四步:执行启动服务命令:

npm start

服务启动成功后,会自动打开浏览器,并访问 ​​http://127.0.0.1:3000/​​ ,效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_13


(2)配置热加载

第一步:在​​src​​​目录中新建​​dev.js​​文件,添加以下代码:

if (module.hot) {
module.hot.accept(err => {
if (err) {
console.error('Cannot apply HMR update.', err);
}
});
}

上面代码用于触发​​HMR​​,这部分代码不属于业务代码。

第二步:在​​webpack.config.js​​配置文件中添加热加载配置:

// webpack.config.dev.js
const webpack = require('webpack'); //增加导入webpack
module.exports = {
devServer: {
...
hot: true, //在devServer中增加hot字段
...
},
...
entry: ['./src/index.jsx', './src/dev.js'], //在entry字段中添加触发文件配置
...
plugins: [
// plugins中增加下面内容,实例化热加载插件
new webpack.HotModuleReplacementPlugin(),
...
]
...
}

第三步:启动服务,测试热加载

执行启动服务命令:

npm start

服务启动后,会自动打开浏览器,我们在​​App.js​​中修改内容,保存后会看到浏览器内自动更新,效果如下图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_webpack_14

5.2、使用create-react-app快速搭建环境

​Create React App​​​是​​FaceBook​​​的​​React​​​团队官方出的一个构建​​React​​​单页面应用的脚手架工具。它本身集成了​​Webpack​​​,并配置了一系列内置的​​loader​​​和默认的​​npm​​​的脚本,可以很轻松的实现零配置就可以快速开发​​React​​的应用。

​create-react-app​​​是​​React​​中最简单的创建单页面程序的方式,安装命令如下:

npm install -g create-react-app

在需要创建项目的文件夹下启动命令提示符,使用​​create-react-app​​创建项目,命令如下:

create-react-app my-app

上面命令中,​​my-app​​​是创建的项目名称。执行 ​​cd my-app​​ 进入到项目根目录下,执行启动服务命令:

npm start

服务启动成功后,会自动打开浏览器,效果如图:

关于Webpack前端工程化构建,你必须要掌握这些核心知识点_html_15


到此,​​React​​项目就搭建成功了。

6、总结

我们这个章节先通过前端的发展历程引出了为什么要使用​​Webpack​​​构建项目,然后通过对​​Webpack​​​的常规配置、Babel转换、搭建本地服务进行了详细的讲解,最后使用​​Webpack​​​搭建​​React​​开发环境。

作为一套​​React​​​框架教程,对​​Webpack​​​的使用有了解就可以了,由于​​Webpack​​​主要是用于项目的构建和打包,甚至有的同学在工作中根本就接触不到​​Webpack​​​,或者是项目中只配置一次​​Webpack​​,后面就不再用了。对于这些同学来说,希望这篇教程能够给你带来一些新的知识储备,在以后用到的时候,能够有操作的思路和方法。

关于​​Webpack​​的知识远远比这篇教程中的内容要多,不过对于项目开发来说,掌握这些基础知识就够用了。大家一定要培养独立思考和解决问题的能力,在项目构建过程中,如果遇到了问题,根据所学的知识寻找解决问题的方法,通过实践来提升自己的编程能力,大家加油!

我是江太公,山水有相逢

关注我,我们下期见!

前段时间搭了一个个人博客网站,有兴趣的可以了解下哈:​​前端江太公​