Ajax
一. Ajax入门
1. AJAX 概念和 axios 使用
什么是 AJAX ? mdn
- AJAX 是异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)
- 使用浏览器的 XMLHttpRequest 对象 与服务器通信
- 它可以使用 JSON、XML、HTML 和text 文本等格式发送和接收数据。AJAX最吸引人的就是它的"异步"特性,也就是是它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
简单来说,AJAX是浏览器与服务器进行数据通信的技术。
什么是服务器?
- 可以暂时理解为提供数据的一台电脑
为何学 AJAX ?
- 以前我们的数据都是写在代码里固定的, 无法随时变化
- 现在我们的数据可以从服务器上进行获取,让数据变活
怎么学 AJAX ?
-
先使用一个第三方库叫 axios,与服务器通信
- 基于 XMLHttpRequest 封装、代码简单、月下载量在 14 亿次,让我们有更多精力关注在与服务器通信上
- 后续 Vue,React项目中都会用到 axios
-
再学习 XMLHttpRequest 对象的使用,了解 AJAX 底层原理
axios 使用
-
引入 axios.js
axios.js文件链接: cdn.jsdelivr.net/npm/axios/d...
-
使用 axios 函数
- 传入配置对象
- 再用 .then 回调函数接收结果,并做后续处理
jsaxios({ url: '目标资源地址' }).then((result) => { // 对服务器返回的数据做后续处理 })
注意:请求的 url 地址, 就是标记资源的网址
2. 认识 URL
了解 URL 的组成和作用
1. 为什么要认识 URL ? mdn
知道作用和组成,方便与后端人员沟通
2. 什么是 URL ?
统一资源定位符
(Uniform Resource Locator,简写:URL),或称定位地址、URL地址,俗称网页地址,简称网址
,用于定位访问网络上的资源
(资源指的是:网页,图片,数据,视频,音频等等)
3. URL 的组成?
- 协议,域名,资源路径(URL 组成有很多部分,我们先掌握这3个重要的部分即可)

协议
http 协议:超文本传输协议,规定了浏览器和服务器之间传输数据的格式
(而格式具体有哪些稍后我们就会学到)
协议范围:http、https...
域名
域名:标记服务器在互联网中的方位
网络中有很多服务器,你想访问哪一台,就需要知道它的域名才可以

资源路径
资源路径:一个服务器内有多个资源,用于标记资源在服务器下的具体位置

3. URL 查询参数
通过URL传递查询参数,获取匹配的数据
查询参数定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

axios 如何携带查询参数?
-
使用 params 选项即可
注意:axios 在运行时把参数名和值,会拼接到 url?参数名=值
jsaxios({ url: '目标资源地址', params: { 参数名: 值 } }).then(result => { // 对服务器返回的数据做后续处理 })
案例-查询-地区列表 代码如下:
js
/*
获取地区列表: http://hmajax.itheima.net/api/area
查询参数:
pname: 省份或直辖市名字
cname: 城市名字
*/
// 目标: 根据省份和城市名字, 查询地区列表
// 1. 查询按钮-点击事件
document.querySelector('.sel-btn').addEventListener('click', () => {
// 2. 获取省份和城市名字
let pname = document.querySelector('.province').value
let cname = document.querySelector('.city').value
// 3. 基于axios请求地区列表数据
axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname,
cname
}
}).then(result => {
// console.log(result)
// 4. 把数据转li标签插入到页面上
let list = result.data.list
console.log(list)
let theLi = list.map(areaName => `<li class="list-group-item">${areaName}</li>`).join('')
console.log(theLi)
document.querySelector('.list-group').innerHTML = theLi
})
})
4. 常用请求方法和数据提交
掌握如何向服务器提交数据,而不单单是获取数据
想要提交数据,先来了解什么是请求方法
请求方法
请求方法:对服务器资源,要执行的操作。
请求方法是一些固定单词的英文,例如:GET,POST,PUT,DELETE,PATCH(这些都是http协议规定的),每个单词对应一种对服务器资源要执行的操作。

前面我们获取数据其实用的就是GET请求方法,但是axios内部设置了默认请求方法就是GET,我们就没有写
提交数据需要使用POST请求方法
-
什么时候进行数据提交呢?
场景:当数据需要在服务器上保存
例如:多端要查看同一份订单数据,或者使用同一个账号进行登录,那订单/用户名+密码,就需要保存在服务器上,随时随地进行访问
-
axios 如何提交数据到服务器呢?
需要学习,method 和 data 这2个新的选项了
axios 请求配置
-
url:请求的 URL 网址
-
method
:请求的方法,GET可以省略(不区分大小写) -
data
:提交数据jsaxios({ url: '目标资源地址', method: '请求方法', data: { 参数名: 值 } }).then(result => { // 对服务器返回的数据做后续处理 })
-
案例:注册账号,提交用户名和密码到服务器保存
注册用户 URL 网址:hmajax.itheima.net/api/registe...
请求方法:POST
参数名:
username:用户名(要求中英文和数字组成,最少8位)
password:密码(最少6位)
代码如下:
js
/*
注册用户:http://hmajax.itheima.net/api/register
请求方法:POST
参数名:
username:用户名(中英文和数字组成,最少8位)
password:密码 (最少6位)
目标:点击按钮,通过axios提交用户和密码,完成注册
*/
document.querySelector('.btn').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima007',
password: '7654321'
}
})
})
5. axios 错误处理
掌握接收 axios 响应错误信息的处理语法
-
场景:再次注册相同的账号,会遇到报错信息
-
处理:用更直观的方式,给普通用户展示错误信息
因为,普通用户不会去控制台里看错误信息,我们要编写代码拿到错误并展示给用户在页面上
-
语法:在 then 方法的后面,通过点语法调用 catch 方法,传入回调函数并定义形参
jsaxios({ // ...请求选项 }).then(result => { // 处理成功数据 }).catch(error => { // 处理失败错误 })
案例:
js
//注册案例,重复注册时通过弹框提示用户错误原因
document.querySelector('.btn').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
data: {
username: 'itheima007',
password: '7654321'
}
}).then(result => {
// 成功
console.log(result)
}).catch(error => {
// 失败
// 处理错误信息
console.log(error)
console.log(error.response.data.message)
alert(error.response.data.message)
})
})
6. HTTP 协议-报文
HTTP 协议:规定了浏览器发送及服务器返回内容的格式
请求报文
了解 HTTP 协议中,请求报文的组成和作用
请求报文:浏览器按照 HTTP 协议要求的格式,发送给服务器的内容
请求报文的组成部分有:
- 请求行:请求方法,URL,协议版本
- 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
- 空行:分割请求头,空行之后的是发送给服务器的资源
- 请求体:发送的资源

通过 Chrome 的网络面板如何查看请求体?

错误排查
需求:通过请求报文排查错误原因,并修复
学习了查看请求报文有什么用呢?
- 可以用来确认我们代码发送的请求数据是否真的正确
响应报文
响应报文:服务器按照http协议要求的格式,返回给浏览器的内容
响应报文的组成:
- 响应行(状态行):协议,HTTP响应状态码,状态信息
- 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
- 空行:分割响应头,控制之后的是服务器返回的资源
- 响应体:返回的资源

HTTP 响应状态码
HTTP 响应状态码:用来表明请求是否成功完成
例如:404(客户端要找的资源,在服务器上不存在)

7. 接口文档
接口文档:描述接口的文章(一般是后端工程师,编写和提供)
接口:指使用 AJAX 和 服务器通讯时,使用的 URL,请求方法,以及参数,例如:AJAX阶段接口文档
8. form-serialize 插件
-
作用:快速收集表单元素的值
-
我们前面收集表单元素的值,是一个个标签获取的,如果一套表单里有很多很多表单元素,如何一次性快速收集出来呢?
使用 form-serialize 插件提供的 serialize 函数就可以办到
-
form-serialize 插件语法:
-
引入 form-serialize 插件到自己网页中
-
使用 serialize 函数
serialize(参数1, 参数2)
-
参数1:要获取的 form 表单标签对象(要求表单元素需要有 name 属性-用来作为收集的数据中属性名)
-
参数2:配置对象
- hash 设置获取数据结构:
- true - 收集出来的是一个 JS 对象结构
- false - 收集出来的是一个查询字符串格式
- empty 设置是否获取空值:
- true - 收集空值
- false - 不收集空值
- hash 设置获取数据结构:
-
-
需求:收集登录表单里用户名和密码
对应代码:
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>form-serialize插件使用</title>
</head>
<body>
<form action="javascript:;" class="example-form">
<input type="text" name="username">
<br>
<input type="text" name="password">
<br>
<input type="button" class="btn" value="提交">
</form>
<!--
目标:在点击提交时,使用form-serialize插件,快速收集表单元素值
1. 把插件引入到自己网页中
-->
<script src="./lib/form-serialize.js"></script>
<script>
document.querySelector('.btn').addEventListener('click', () => {
/**
* 2. 使用serialize函数,快速收集表单元素的值
* 参数1:要获取哪个表单的数据
* 表单元素设置name属性,值会作为对象的属性名
* 建议name属性的值,最好和接口文档参数名一致
* 参数2:配置对象
* hash 设置获取数据结构
* - true:JS对象(推荐)一般请求体里提交给服务器
* - false: 查询字符串
* empty 设置是否获取空值
* - true: 获取空值(推荐)数据结构和标签结构一致
* - false:不获取空值
*/
const form = document.querySelector('.example-form')
const data = serialize(form, { hash: true, empty: true })
// const data = serialize(form, { hash: false, empty: true })
// const data = serialize(form, { hash: true, empty: false })
console.log(data)
})
</script>
</body>
</html>
案例-用户登录-form-serialize
-
基于模板代码,使用 form-serialize 插件来收集用户名和密码
-
在原来的代码基础上修改即可
-
先引入插件
html<!-- 3.1 引入插件 --> <script src="./lib/form-serialize.js"></script>
-
然后修改代码
js// 3.2 使用serialize函数,收集登录表单里用户名和密码 const form = document.querySelector('.login-form') const data = serialize(form, { hash: true, empty: true }) console.log(data) // {username: 'itheima007', password: '7654321'} const { username, password } = data
-
9. 小结
-
AJAX 有什么用?
答案
- 浏览器和服务器之间通信,动态数据交互
-
AJAX 如何学:
答案
- 先掌握 axios 库使用,再了解 XMLHttpRequest 原理
-
这一节 axios 体验步骤(语法)?
答案
- 引入 axios 库,使用 axios 相关语法
-
URL 是什么?
答案
- 统一资源定位符,网址,用于访问服务器上资源
-
请解释这个 URL,每个部分作用?
hmajax.itheima.net/api/news
答案- 协议://域名/资源路径
-
URL 查询参数有什么用?
答案
- 浏览器提供给服务器额外信息,获取对应的数据
-
axios 要如何携带查询参数?
答案
- 使用 params 选项,携带参数名和值在对象结构中
-
请求方法最常用的是哪2个,分别有什么作用?
答案
- POST 提交数据,GET 查询数据
-
axios 的核心配置项有哪几个,作用分别是什么?
答案
- url:目标资源地址(请求 URL 网址);
- method:请求方法,GET 可以省略(不区分大小写);
- params:查询参数;
- data:提交的数据
-
axios 如何拿到请求响应失败的信息?
答案
- 通过 axios 函数调用后,在后面接着调用 .catch 方法捕获
-
浏览器发送给服务器的内容叫做,请求报文
-
请求报文的组成是什么?
答案
- 请求行,请求头,空行,请求体
-
学会了查看请求报文,对实际开发有什么帮助呢?
答案
- 可以快速确认我们发送的内容是否正确
-
响应报文的组成:
答案
- 响应行(状态行):协议,HTTP响应状态码,状态信息
- 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
- 空行:分割响应头,控制之后的是服务器返回的资源
- 响应体:返回的资源
-
接口文档是什么?
答案
- 由后端提供的描述接口的文章
-
接口文档都包含哪些信息?
答案
- 请求的 URL 网址,请求方法,请求参数和说明
-
在浏览器中如何查看查询参数/请求体,以及响应体数据?
-
请求报文和响应报文由几个部分组成,每个部分的作用?
-
我们什么时候使用 form-serialize 插件?
答案
- 快速收集表单元素的值
-
如何使用 form-serialize 插件?
答案
-
- 先引入插件到自己的网页中,2. 准备form和表单元素的name属性,3.使用serialize函数,传入form表单和配置对象
-
-
配置对象中 hash 和 empty 有什么用?
答案
- hash 决定是收集为 JS 对象还是查询参数字符串,empty 决定是否收集空值
-
如何把一个第三方插件使用在已完成的案例中?
答案
- 引入后,只需要使用在要修改的地方,修改一点就要确认测试一下
二. AJAX原理
1. AJAX原理-XMLHttpRequest
概念
定义:XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLHttpRequest可以再不刷新页面的情况下请求特定URL,获取数据。这运行网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用。
关系:axios 内部采用 XMLHttpRequest 与服务器交互。

好处:掌握使用 XHR 与服务器进行数据交互,了解 axios 内部原理
AJAX 是浏览器与服务器通信的技术,采用 XMLHttpRequest 对象相关代码
axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数
学习 XHR 也是了解 axios 内部与服务器交互过程的真正原理
使用XMLHttpRequest
步骤:
- 创建 XMLHttpRequest 对象
- 配置请求方法和请求 url 地址
- 监听 loadend 事件,接收响应结果
- 发起请求
js
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
// 响应结果 接收- 响应
console.log(xhr.response)
})
xhr.send() // 发送 - 请求
XMLHttpRequest - 查询参数
什么是查询参数:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
所以,原生 XHR 需要自己在 url 后面携带查询参数字符串,没有 axios 帮助我们把 params 参数拼接到 url 字符串后面了
js
/**
* 目标:使用XHR携带查询参数,展示某个省下属的城市列表
*/
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=辽宁省')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
const data = JSON.parse(xhr.response)
console.log(data)
document.querySelector('.city-p').innerHTML = data.list.join('<br>')
})
xhr.send()
URLSearchParams
多个查询参数,如果自己拼接很麻烦,这里用 URLSearchParams 把参数对象转成"参数名=值&参数名=值"格式的字符串,语法如下:
js
// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
参数名1: 值1,
参数名2: 值2
})
// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2
XMLHttpRequest - 数据提交
需求:通过 XHR 提交用户名和密码,完成注册用户功能
核心:
-
请求头设置 Content-Type:application/json
-
请求体携带 JSON 字符串
步骤和语法:
-
注意1:但是这次没有 axios 帮我们了,我们需要自己设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用
-
注意2:没有 axios 了,我们前端要传递的请求体数据,也没人帮我把 JS 对象转成 JSON 字符串了,需要我们自己转换
-
注意3:原生 XHR 需要在 send 方法调用时,传入请求体携带
js
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
// 1. 告诉服务器,我传递的内容类型,是 JSON 字符串
xhr.setRequestHeader('Content-Type', 'application/json')
// 2. 准备数据并转成 JSON 字符串
const user = { username: 'itheima007', password: '7654321' }
const userStr = JSON.stringify(user)
// 3. 发送请求体数据
xhr.send(userStr)
小结
-
AJAX 原理是什么?
答案
- window 提供的 XMLHttpRequest对象
-
为什么学习 XHR ?
答案
- 有更多与服务器数据通信方式
- 了解 axios 内部原理
-
XHR 使用步骤?
答案
-
- 创建 XHR 对象
-
- 调用 open 方法,设置 url 和请求方法
-
- 监听 loadend 事件,接收结果
-
- 调用 send 方法,发起请求
-
-
XHR 如何携带查询参数?
答案
- 在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
-
JS 对象如何转成查询参数格式字符串?
答案
- 在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
-
XHR 如何提交请求体数据?
答案
- 在 send 中携带请求体数据,要按照后端要求的内容类型携带
2. Promise
Promise 定义
-
什么是 Promise ?
- Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。
-
Promise 的好处是什么?
-
逻辑更清晰(成功或失败会关联后续的处理函数)
-
了解 axios 函数内部运作的机制
-
能解决回调函数地狱问题
-

-
Promise 管理异步任务,语法怎么用?
js// 1. 创建 Promise 对象 const p = new Promise((resolve, reject) => { // 2. 执行异步任务-并传递结果 // 成功调用: resolve(值) 触发 then() 执行 // 失败调用: reject(值) 触发 catch() 执行 }) // 3. 接收结果 p.then(result => { // 成功 }).catch(error => { // 失败 })
-
示例代码:
js/** * 目标:使用Promise管理异步任务 */ // 1. 创建Promise对象 const p = new Promise((resolve, reject) => { // 2. 执行异步代码 setTimeout(() => { // resolve('模拟AJAX请求-成功结果') reject(new Error('模拟AJAX请求-失败结果')) }, 2000) }) // 3. 获取结果 p.then(result => { console.log(result) }).catch(error => { console.log(error) })
Promise 三种状态
-
为什么要了解 Promise 的三种状态(作用) ?
- 知道 Promise 对象如何关联的处理函数,以及代码的执行顺序
-
Promise 有哪三种状态?
每个 Promise 对象必定处于以下三种状态之一
-
待定(pending):初始状态,既没有被兑现,也没有被拒绝
-
已兑现(fulfilled):操作成功完成
-
已拒绝(rejected):操作失败
-
-
Promise 的状态改变有什么用:调用对应函数,改变 Promise 对象状态后,内部触发对应回调函数传参并执行。
注意: Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变
封装_简易axios-获取省份列表
模拟 axios 函数封装,更深入了解 axios 内部运作原理
-
需求:基于 Promise 和 XHR 封装 myAxios 函数,获取省份列表展示到页面
-
核心语法:
jsfunction myAxios(config) { return new Promise((resolve, reject) => { // XHR 请求 // 调用成功/失败的处理程序 }) } myAxios({ url: '目标资源地址' }).then(result => { }).catch(error => { })
代码如下:
scss
```js
/**
* 目标:封装_简易axios函数_获取省份列表
* 1. 定义myAxios函数,接收配置对象,返回Promise对象
* 2. 发起XHR请求,默认请求方法为GET
* 3. 调用成功/失败的处理程序
* 4. 使用myAxios函数,获取省份列表展示
*/
// 1. 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
return new Promise((resolve, reject) => {
// 2. 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
// 3. 调用成功/失败的处理程序
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
// 4. 使用myAxios函数,获取省份列表展示
myAxios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
console.log(result)
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
console.log(error)
document.querySelector('.my-p').innerHTML = error.message
})
```
封装_简易axios-获取地区列表
修改 myAxios 函数支持传递查询参数,获取辽宁省,大连市的地区列表
-
需求:在上个封装的建议 axios 函数基础上,修改代码支持传递查询参数功能
-
修改步骤:
- myAxios 函数调用后,判断 params 选项
- 基于 URLSearchParams 转换查询参数字符串
- 使用自己封装的 myAxios 函数显示地区列表
-
核心代码:
jsfunction myAxios(config) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() // 1. 判断有params选项,携带查询参数 if (config.params) { // 2. 使用URLSearchParams转换,并携带到url上 const paramsObj = new URLSearchParams(config.params) const queryString = paramsObj.toString() // 把查询参数字符串,拼接在url?后面 config.url += `?${queryString}` } xhr.open(config.method || 'GET', config.url) xhr.addEventListener('loadend', () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.response)) } else { reject(new Error(xhr.response)) } }) xhr.send() }) } // 3. 使用myAxios函数,获取地区列表 myAxios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: '辽宁省', cname: '大连市' } }).then(result => { console.log(result) document.querySelector('.my-p').innerHTML = result.list.join('<br>') })
封装_简易axios-注册用户
js
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
// 1. 判断有data选项,携带请求体
if (config.data) {
// 2. 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
// 如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}
document.querySelector('.reg-btn').addEventListener('click', () => {
// 3. 使用myAxios函数,完成注册用户
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima999',
password: '666666'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})
3. 小结
-
什么是 Promise ?
答案
- 表示(管理)一个异步操作最终状态和结果值的对象
-
为什么学习 Promise ?
答案
- 成功和失败状态,可以关联对应处理函数
- 了解 axios 内部运作的原理
-
Promise 使用步骤?
答案
- new Promise 创建Promise对象。
- 执行异步任务-并传递结果1. 用 resolve 关联 then 的回调函数传递成功结果。2.用 reject 关联 catch 的回调函数传递失败结果。
-
Promise 对象有哪 3 种状态?
答案
- 待定 pending,已兑现 fulfilled,已拒绝 rejected
-
Promise 状态有什么用?
答案
- 状态改变后,调用关联的处理函数
-
AJAX 如何判断是否请求响应成功了?
答案
- 响应状态码在大于等于 200 并且小于 300 的范围是成功的
-
自己封装的 myAxios 如何设置默认请求方法 GET?
答案
- config.method 判断有值就用,无值用'GET'方法
-
外面传入查询参数对象,myAxios 函数内如何转查询参数字符串?
答案
- 使用 URLSearchParams 对象转换
-
外面传入 data 选项,myAxios 函数内如何携带请求体参数?
答案
- 判断外面传入了这个属性,自己转成 JSON 字符串并设置请求头并在 send 方法中携带
-
做完这个项目会带来什么收获?
答案
- 可以做一个真正有意义的业务,查看城市的天气预报,测试自己封装的 myAxios 函数是否好用
-
监听输入框实时改变的事件是什么?
答案
- input事件
4. 重点(必须会)
- 了解 AJAX 原理之 XMLHttpRequest(XHR)相关语法
- 了解 Promise 的作用和三种状态
- 了解 axios 内部运作的过程
三. AJAX进阶
1. 同步代码和异步代码
同步代码:浏览器是按照我们书写代码的顺序一行一行地执行程序的。浏览器会等待代码的解析和工作,在上一行完成才会执行下一行。(逐行执行,需原地等待结果后,才继续向下执行)
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果
代码打印顺序:
js
const result = 0 + 1
console.log(result)
setTimeout(() => {
console.log(2)
}, 2000)
document.querySelector('.btn').addEventListener('click', () => {
console.log(3)
})
document.body.style.backgroundColor = 'pink'
console.log(4)
//结果:1, 4, 2
//按钮点击一次打印一次 3
异步代码接收结果,使用的都是回调函数
2. 回调函数地狱和 Promise 链式调用
回调函数地狱
了解回调函数地狱的概念和缺点
需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中
概念:
在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:
可读性差,异常无法捕获,耦合性严重,牵一发动全身
Promise-链式调用
了解 Promise 链式调用特点和语法
概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题

按照图解,编写核心代码:
js
/**
* 目标:掌握Promise的链式调用
* 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
// 1. 创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
// 2. 获取省份名字
const p2 = p.then(result => {
console.log(result)
// 3. 创建Promise对象-模拟请求城市名字
// return Promise对象最终状态和结果,影响到新的Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '--- 北京')
}, 2000)
})
})
// 4. 获取城市名字
p2.then(result => {
console.log(result)
})
// then()原地的结果是一个新的Promise对象
console.log(p2 === p)
Promise-链式调用_解决回调地狱
使用 Promise 链式调用,解决回调函数地狱问题
做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

按照图解思路,编写核心代码:
js
/**
* 目标:把回调函数嵌套代码,改成Promise链式调用结构
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
let pname = ''
// 1. 得到-获取省份Promise对象
axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2. 得到-获取城市Promise对象
return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3. 得到-获取地区Promise对象
return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
}).then(result => {
console.log(result)
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
3. async 函数和 await
概念
- async 和await关键字让我们可以用一种更简洁的方式写出基于 Promise的异步行为,而无需刻意地链式调用promise。
- 在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
做法:使用 async 和 await 解决回调地狱问题
注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
核心代码:
js
// 1. 定义async修饰函数
async function getData() {
// 2. await等待Promise对象成功的结果
const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
const pname = pObj.data.list[0]
const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
const cname = cObj.data.list[0]
const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
const areaName = aObj.data.list[0]
document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
document.querySelector('.area').innerHTML = areaName
}
getData()
async 函数和 await 捕获错误
使用:try...catch
try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
语法:
js
try {
// 要执行的代码
} catch (error) {
// error 接收的是,错误消息
// try 里代码,如果有错误,直接进入这里执行
}
注意:try 里有报错的代码,会立刻跳转到 catch 中
4. 事件循环 EventLoop
掌握事件循环模型是如何执行异步代码的
事件循环(EventLoop)好处:掌握后知道 JS 是如何安排和运行代码的
作用:JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码,收集和处理事件以及执行队列中的子任务
原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
事件循环-练习
需求:请根据掌握的事件循环的模型概念,分析代码执行过程
js
/**
* 目标:阅读并回答执行的顺序结果
*/
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
function myFn() {
console.log(3)
}
function ajaxFn() {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
console.log(4)
})
xhr.send()
}
for (let i = 0; i < 1; i++) {
console.log(5)
}
ajaxFn()
document.addEventListener('click', () => {
console.log(6)
})
myFn()
结果:1 5 3 2 4 点击一次document就会执行一次打印6
宏任务与微任务
掌握微任务和宏任务的概念和区分
ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务
异步任务划分为了
- 宏任务:由浏览器环境执行的异步代码
- 微任务:由 JS 引擎环境执行的异步代码


注意:Promise 本身是同步的,而 then 和 catch 回调函数是异步的。
微任务队列清空后,才会执行下一个宏任务
注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!
下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队
总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系

经典面试题 宏任务和微任务-执行顺序
示例1

执行步骤说明:
- script脚本交给【宿主环境(浏览器)】,将整个js代码推入宏任务队列,此时调用栈是空闲,就开始执行第一个宏任务
- 【console.log(1)】放入调用栈执行,在控制台打印1,执行完之后出栈
- js引擎接着读取代码,【setTimeout(() => {console.log(2)}, 0)】是宏任务,交给宿主浏览器,浏览器进行倒计时,发现是0秒,会立刻将回调函数【() => {console.log(2)}】放到宏任务队列当中排队
- js引擎继续读取代码,【new Promise((resolve) => { console.log(3) resolve(4) })】放到执行栈调用,Promise 本身是同步的,所以控制台立刻打印3,【resolve(4)】标记promise成功状态,【new Promise】执行完毕出栈,此时得到一个promise对象,它是成功状态的
- 接着,js引擎执行同步代码【p.then(result => console.log(result))】放入执行栈,发现回调函数是异步微任务,将【result => console.log(result)】放入微任务队列进行排队,【p.then(result => console.log(result))】执行完毕出栈
- js引擎接着读取代码,【console.log(5)】推入调用栈执行,在控制台打印5,执行完毕之后出栈
- 此时,script中的同步代码都执行完毕,调用栈空闲了,开始去任务队列中反复调度有没有要执行的异步回调函数
- 优先调度微任务队列,因为微任务更接近JS 引擎
- 发现微任务队列中有待执行的回调函数,推入到调用栈执行【result => console.log(result),已确认成功并且传入4】,所以在控制台打印4,微任务的回调就执行完了,出栈
- 调用栈又空闲了,又反复调度任务队列中的回调,发现微任务队列中的任务已经被清空了,所以调度宏任务队列中任务,【() => {console.log(2)}】推入调用栈执行,在控制台打印2,宏任务的回调执行完毕,出栈
- 执行完毕
示例2
js
// 目标:回答代码执行顺序
console.log(1)
setTimeout(() => {
console.log(2)
const p = new Promise(resolve => resolve(3))
p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
setTimeout(() => {
console.log(4)
}, 0)
resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7)
//打印顺序为:1 7 5 6 2 3 4

5. Promise.all 静态方法
了解 Promise.all 作用和使用场景
概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

语法:
js
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})
js
<body>
<ul class="my-ul"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
// 1. 请求城市天气,得到Promise对象
const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })
// 2. 使用Promise.all,合并多个Promise对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
// 注意:结果数组顺序和合并时顺序是一致
console.log(result)
const htmlStr = result.map(item => {
return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
}).join('')
document.querySelector('.my-ul').innerHTML = htmlStr
}).catch(error => {
console.dir(error)
})
</script>
</body>
6. 小结
-
什么是同步代码?
答案
- 逐行执行,原地等待结果后,才继续向下执行
-
什么是异步代码?
答案
- 调用后耗时,不阻塞代码执行,将来完成后触发回调函数
-
JS 中有哪些异步代码?
答案
- setTimeout / setInterval,事件,AJAX
-
异步代码如何接收结果?
答案
- 依靠回调函数来接收
-
什么是回调函数地狱?
答案
- 在回调函数一直向下嵌套回调函数,形成回调函数地狱
-
回调函数地狱问题?
答案
- 可读性差,异常捕获困难,耦合性严重
-
什么是 Promise 的链式调用?
答案
- 使用 then 方法返回新 Promise 对象特性,一直串联下去
-
then 回调函数中,return 的值会传给哪里?
答案
- 传给 then 方法生成的新 Promise 对象
-
Promise 链式调用有什么用?
答案
- 解决回调函数嵌套问题
-
Promise 链式调用如何解决回调函数地狱?
答案
- then 的回调函数中 return Promise对象,影响当前新 Promise 对象的值
-
await 的作用是什么?
答案
- 替代 then 方法来提取 Promise 对象成功状态的结果
-
try 和 catch 有什么作用?
答案
- 捕获同步流程的代码报错信息
-
什么是事件循环?
答案
- 执行代码和收集异步任务,在调用栈空闲时,反复调用任务队列里回调函数执行机制
-
为什么有事件循环?
答案
- JavaScript 是单线程的,为了不阻塞 JS 引擎,设计执行代码的模型
-
JavaScript 内代码如何执行?
答案
- 执行同步代码,遇到异步代码交给宿主浏览器环境执行
- 异步有了结果后,把回调函数放入任务队列排队
- 当调用栈空闲后,反复调用任务队列里的回调函数
-
什么是宏任务?
答案
- 浏览器执行的异步代码
- 例如:JS 执行脚本事件(script),setTimeout/setInterval,AJAX请求完成事件,用户交互事件等
-
什么是微任务?
答案
- JS 引擎执行的异步代码
- 例如:Promise对象.then()的回调(注:Promise对象本身是同步的)
-
JavaScript 内代码如何执行?
答案
- 执行第一个 script 脚本事件宏任务,执行里面同步代码
- 遇到 宏任务/微任务 交给宿主环境(浏览器/js引擎),有结果回调函数进入对应队列
- 当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来
-
Promise.all 什么时候使用?
答案
- 合并多个 Promise 对象并等待所有同时成功的结果,如果有一个报错就会最终为失败状态,当需要同时渲染多个接口数据同时到网页上时使用
7. 今日重点(必须会)
-
区分异步代码,回调函数地狱问题和所有解决防范(Promise 链式调用)
-
掌握 async 和 await 的使用
-
理解 EventLoop 和宏任务微任务执行顺序
-
了解 Promise.all 的作用和使用场景
扩展阅读
ES6
-
ES6 对象属性和值简写的前提是什么?
答案
- 当属性名和value位置变量名同名即可简写
函数
-
我们什么时候需要封装函数?
答案
- 遇到相同逻辑,重复代码要复用的时候
-
如何封装一个函数呢?
答案
- 先明确要完成的需求,以及需要的参数,再来实现其中的细节,然后在需要的地方调用
-
哪个事件能实时检测到输入框值的变化?
A:input 事件
B:change 事件
答案- 选A
项目实战注意点
token
概念:访问权限的令牌,本质上是一串字符串
创建:正确登录后,由后端签发并返回
作用:判断是否有登录状态等,控制访问权限
注意:前端只能判断 token 有无,而后端通过解密可以提取 token 字符串的原始信息,判断有效性

token 的使用:只有登录状态,才可以访问内容页面
axios 请求拦截器和响应拦截器
axios 请求拦截器
axios 请求拦截器:发起请求之前,触发的配置函数,对请求参数进行额外配置
axios 请求拦截器,什么时候使用?
- 有公共配置和设置时,统一设置在请求拦截器中
场景:
axios 响应拦截器
axios 响应拦截器:响应回到 then/catch 之前,触发的拦截函数,对响应结果统一处理
例如:身份验证失败,统一判断并做处理
axios 响应拦截器,什么时候触发成功/失败的回调函数?
- 状态为 2xx 触发成功回调,其他则触发失败的回调函数

富文本编辑器
Node.js
一. Node.js入门
1. Node.js 简介
什么是 Node.js
什么是 Node.js,有什么用,为何能独立执行 JS 代码
定义:Node.js 是一个跨平台的 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序。
作用:编写服务器端程序(也可以对前端代码进行压缩,转译,整合等等,提高前端开发和运行效率)
- 编写数据接口,提供网页资源浏览功能等等
- 前端工程化:为后续学习 Vue 和 React 等框架做铺垫
什么是前端工程化?
- 前端工程化:开发项目直到上线,过程中集成的所有工具和技术(前端代码压缩,整合,转译,测试,自动部署等等工具的集成统称,为了提高前端开发项目的效率)(对前端代码进行优化,集成各种提高前端开发效率的工具等)
- Node.js 是前端工程化的基础(因为 Node.js 可以主动读取前端代码内容)

Node.js 为何能执行 JS?
首先:浏览器能执行 JS 代码,依靠的是内核中的 V8 引擎(C++ 程序)
其次:Node.js 是基于 Chrome V8 引擎进行封装(运行环境)
区别:都支持 ECMAScript 标准语法,Node.js 有独立的 API,但是语法和浏览器环境的 V8 有所不同,没有 DOM 和 BOM 等(document 和 window)

注意:Node.js 环境没有 DOM 和 BOM 等
Node.js 安装
要求:下载 node-v16.19.0.msi 安装程序(指定版本:兼容 vue-admin-template 模板)
安装过程:默认下一步即可
注释事项:
-
安装在非中文路径下
-
无需勾选自动安装其他配套软件
成功验证:
-
打开 cmd 终端,输入 node -v 命令查看版本号
-
如果有显示,则代表安装成功
Node.js 没有图形化界面,需要使用 cmd 终端命令行(利用一些命令来操控电脑执行某些程序软件)输入
使用 Node.js
需求:新建 JS 文件,并编写代码后,在 node 环境下执行
命令:在 VSCode 集成终端中,输入 node xxx.js,回车即可执行

小结
-
Node.js 是什么?
答案
- 基于 Chrome 的 V8 引擎封装,独立执行 JavaScript 代码的环境
-
Node.js 有什么用?
答案
- 编写后端程序:提供数据和网页资源等
- 前端工程化:集成各种开发中使用的工具和技术(如翻译压缩整合代码等,提高开发效率)
-
Node.js 为何能执行 JS 代码?
答案
- 基于 Chrome 的 V8 引擎封装
-
Node.js 与浏览器环境的 JS 最大区别?
答案
- Node.js 环境中没有 BOM 和 DOM,但是也用 JS 语法
-
Node.js 如何执行代码?
答案
- 在 VSCode 终端中输入:node xxx.js 回车即可执行(注意路径)
2. fs模块-读写文件
了解模块概念,使用 fs 模块封装的方法读写文件内容
模块:类似插件,封装了方法/属性
fs 模块:封装了与本机文件系统进行交互的,方法和属性
fs 模块使用语法如下:
-
加载 fs 模块,得到 fs 对象
jsconst fs = require('fs')
-
写入文件内容语法:
js//注意:文件可以没有,但是写入文件的文件夹要有 fs.writeFile('文件路径', '写入内容', err => { // 写入后的回调函数 })
-
读取文件内容的语法:
jsfs.readFile('文件路径', (err, data) => { // 读取后的回调函数 // data 是文件内容的 Buffer 数据流 })
向 test.txt 文件写入内容并读取打印
js
/**
* 目标:使用 fs 模块,读写文件内容
* 语法:
* 1. 引入 fs 模块
* 2. 调用 writeFile 写入内容
* 3. 调用 readFile 读取内容
*/
// 1. 引入 fs 模块
const fs = require('fs')
// 2. 调用 writeFile 写入内容
// 注意:建议写入字符串内容,会覆盖目标文件所有内容
fs.writeFile('./text.txt', '欢迎使用 fs 模块读写文件内容', err => {
if (err) console.log(err)
else console.log('写入成功')
})
// 3. 调用 readFile 读取内容
fs.readFile('./text.txt', (err, data) => {
if (err) console.log(err)
else console.log(data.toString()) // 把 Buffer 数据流转成字符串类型
})
3. path模块-路径处理
使用 path 模块来得到绝对路径
问题:Node.js 代码中,相对路径是根据终端所在路径来查找的,可能无法找到你想要的文件
举例
如果终端是要执行文件所在文件夹,node执行当前js文件路径没问题;但是,如果终端不是要执行文件所在文件夹,js文件里面使用相对路径,就会导致找不到文件
建议:在 Node.js 代码中,使用绝对路径
-
为什么在 Node.js 待执行的 JS 代码中要用绝对路径:
Node.js 执行 JS 代码时,代码中的路径都是以终端所在文件夹出发查找相对路径,而不是以我们认为的从代码本身出发,会遇到问题,所以在 Node.js 要执行的代码中,访问其他文件,建议使用绝对路径
补充:__dirname 内置变量(动态获取当前文件所在文件夹的绝对路径)
- windows:D:\备课代码\3-B站课程\03_Node.js与Webpack\03-code\03
- mac:/Users/xxx/Desktop/备课代码/3-B站课程/03_Node.js与Webpack/03-code/03
- 在 windows 和 mac 地址中,分隔符不一样
注意:path.join() 会按照所在本机系统的分隔符作为定界符来链接你传入的路径
总结:使用模块内置变量 __dirname
配合 path.join() 来得到绝对路径使用
语法:
-
加载 path 模块
const path = require('path)
-
使用 path.join 方法,拼接路径
path.join('路径1', '路径2',...)
jsconst fs = require('fs') console.log(__dirname) // D:\备课代码\2_node_3天\Node_代码\Day01_Node.js入门\代码\03 // 1. 加载 path 模块 const path = require('path') // 2. 使用 path.join() 来拼接路径 // const pathStr = path.join(__dirname, '../text.txt') const pathStr = path.join(__dirname, '..', 'text.txt') console.log(pathStr) fs.readFile(pathStr, (err, data) => { if (err) console.log(err) else console.log(data.toString()) })
4. 案例-压缩前端html
需求:把准备好的 html 文件里的回车符(\r)和换行符(\n)去掉进行压缩,写入到新 html 中
步骤:
- 读取源 html 文件内容
- 正则替换字符串
- 写入到新的 html 文件中,并运行查看是否能正常打开网页
代码实现如下:
js
/**
* 目标一:压缩 html 里代码
* 需求:把 public/index.html 里的,回车/换行符去掉,写入到 dist/index.html 中
* 1.1 读取 public/index.html 内容
* 1.2 使用正则替换内容字符串里的,回车符\r 换行符\n
* 1.3 确认后,写入到 dist/index.html 内
*/
const fs = require('fs')
const path = require('path')
// 1.1 读取 public/index.html 内容
fs.readFile(path.join(__dirname, 'public', 'index.html'), (err, data) => {
const htmlStr = data.toString()
// 1.2 使用正则替换内容字符串里的,回车符\r 换行符\n
const resultStr = htmlStr.replace(/[\r\n]/g, '')
// 1.3 确认后,写入到 dist/index.html 内
fs.writeFile(path.join(__dirname, 'dist', 'index.html'), resultStr, err => {
if (err) console.log(err)
else console.log('压缩成功')
})
})
5. 认识URL中的端口号
认识 URL 中端口号的作用,以及 Web 服务的作用
URL 统一资源定位符,简称网址,用于访问网络上的资源
端口号的作用:标记服务器里不同的服务程序(端口号范围:0-65535 之间的任意整数)

常见的服务程序
- Web服务程序:用于提供网上信息浏览的程序代码
- 注意:0-1023 和一些特定的端口号被占用,我们自己编写服务程序请避开使用

6. http模块-创建Web服务
基于 Node.js 环境,使用内置 http 模块,创建 Web 服务程序
需求:引入 http 模块,使用相关语法,创建 Web 服务程序,响应返回给请求方一句提示 'hello,world'
步骤:
- 引载 http 模块,创建 Web 服务对象
- 监听 request 请求事件,设置响应头和响应体
- 启动 Web 服务监听对应端口号(配置端口号并启动 Web 服务)
- 运行本服务在终端进程中,用浏览器发起请求(浏览器请求 http://localhost:3000 测试)
注意:本机的域名叫做 localhost
代码如下:
js
/**
* 目标:基于 http 模块创建 Web 服务程序
* 1.1 加载 http 模块,创建 Web 服务对象
* 1.2 监听 request 请求事件,设置响应头和响应体
* 1.3 配置端口号并启动 Web 服务
* 1.4 浏览器请求(http://localhost:3000)测试
*/
// 1.1 加载 http 模块,创建 Web 服务对象
const http = require('http')
const server = http.createServer()
// 1.2 监听 request 请求事件,设置响应头和响应体
server.on('request', (req, res) => {
// 设置响应头-内容类型-普通文本以及中文编码格式
res.setHeader('Content-Type', 'text/plain;charset=utf-8')
// 设置响应体内容,结束本次请求与响应
res.end('欢迎使用 Node.js 和 http 模块创建的 Web 服务')
})
// 1.3 配置端口号并启动 Web 服务
server.listen(3000, () => {
console.log('Web 服务启动成功了')
})
小结
-
如何访问本机里运行的 Web 服务?
答案
- http://localhost:Web服务的端口号/资源路径
7. 案例-浏览时钟
体验 Web 服务除了接口数据以外,还能返回网页资源等
需求:基于 Web 服务,开发提供网页资源的功能,了解下后端的代码工作过程
步骤:
- 基于 http 模块,创建 Web 服务
- 使用 req.url 获取请求资源路径为 /index.html 的时候,读取 index.html 文件内容字符串返回给请求方
- 其他路径,暂时返回不存在的提示
- 运行 Web 服务,用浏览器发起请求
代码如下:
js
/**
* 目标:编写 web 服务,监听请求的是 /index.html 路径的时候,返回 dist/index.html 时钟案例页面内容
* 步骤:
* 1. 基于 http 模块,创建 Web 服务
* 2. 使用 req.url 获取请求资源路径,并读取 index.html 里字符串内容返回给请求方
* 3. 其他路径,暂时返回不存在提示
* 4. 运行 Web 服务,用浏览器发起请求
*/
const fs = require('fs')
const path = require('path')
// 1. 基于 http 模块,创建 Web 服务
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
// 2. 使用 req.url 获取请求资源路径,并读取 index.html 里字符串内容返回给请求方
if (req.url === '/index.html') {
fs.readFile(path.join(__dirname, 'dist/index.html'), (err, data) => {
res.setHeader('Content-Type', 'text/html;charset=utf-8')
res.end(data.toString())
})
} else {
// 3. 其他路径,暂时返回不存在提示
res.setHeader('Content-Type', 'text/html;charset=utf-8')
res.end('你要访问的资源路径不存在')
}
})
server.listen(8080, () => {
console.log('Web 服务启动了')
})
小结
-
Web 服务程序都有什么功能?
答案
- 提供数据和网页资源等等功能,其他它的功能远不止于此
二. Node.js模块化
1. 模块化简介
了解模块化概念和好处,以及 CommonJS 标准语法导出和导入
定义:在 Node.js 中,每个文件都视为一个单独的模块。
概念:项目是由多个模块文件组成的
好处:提高代码复用性,按需加载,独立作用域
使用:需要标准语法导出和导入才进行使用
- 因为模块内的属性和函数都是私有的,如果对外使用,需要使用标准语法导出和导入才可以,而这个标准叫 CommonJS 标准

CommonJS 标准
需求:定义 utils.js 模块,封装基地址和求数组总和的函数,导入到 index.js
使用:
-
导出语法:
jsmodule.exports = { 对外属性名: 模块内私有变量 }
-
导入语法:
jsconst 变量名 = require('模块名或路径') // Node.js 环境内置模块直接写模块名(例如:fs,path,http) // 自定义模块:写模块文件路径(例如:./utils.js)
变量名的值接收的就是目标模块导出的对象
模块名或路径:
- 内置模块:直接写名字(例如:fs,path,http)
- 自定义模块:写模块文件路径(例如:./utils.js)
代码实现
-
utils.js:导出
js/** * 目标:基于 CommonJS 标准语法,封装属性和方法并导出 */ const baseURL = 'http://hmajax.itheima.net' const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0) // 导出 module.exports = { url: baseURL, arraySum: getArraySum }
-
index.js:导入使用
js/** * 目标:基于 CommonJS 标准语法,导入工具属性和方法使用 */ // 导入 const obj = require('./utils.js') console.log(obj) const result = obj.arraySum([5, 1, 2, 3]) console.log(result)
小结
-
Node.js 中什么是模块化?
答案
- 每个文件都是独立的模块
-
模块之间如何联系呢?
答案
- 使用特定语法,导出和导入使用
-
CommonJS 标准规定如何导出和导入模块?
答案
- 导出:module.exports = {},
- 导入:require('模块名或路径')
-
模块名/路径如何选择?
答案
- 内置模块:直接写名字。例如:fs,path,http等。
- 自定义模块:写模块文件路径,例如:./utils.js
2. ECMAScript标准-默认导出和导入
掌握 ECMAScript 标准语法中,默认导出和导入的使用
CommonJS 规范是 Node.js 环境中默认的,后来官方推出 ECMAScript 标准语法,我们接下来在一个需求中,体验下这个标准中默认导出和导入的语法要如何使用
需求:封装并导出基地址和求数组元素和的函数
默认标准使用:
-
导出语法:
jsexport default { 对外属性名: 模块内私有变量 }
-
导入语法:
jsimport 变量名 from '模块名或路径'
注意:Node.js 默认只支持 CommonJS 标准语法,如果想要在当前项目环境下使用 ECMAScript 标准语法,请新建 package.json 文件并设置 type: 'module'
package.json
json
{ "type": "module" }
代码实现:
-
utils.js:导出
js/** * 目标:基于 ECMAScript 标准语法,封装属性和方法并"默认"导出 */ const baseURL = 'http://hmajax.itheima.net' const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0) // 默认导出 export default { url: baseURL, arraySum: getArraySum }
-
index.js:导入
js/** * 目标:基于 ECMAScript 标准语法,"默认"导入,工具属性和方法使用 */ // 默认导入 import obj from './utils.js' console.log(obj) const result = obj.arraySum([10, 20, 30]) console.log(result)
小结
-
ECMAScript 标准规定如何默认导出和导入模块?
答案
- 导出:export default {}
- 导入:import 变量名 from '模块名或路径'
-
如何让 Node.js 切换模块标准为 ECMAScript?
答案
- 运行模块所在文件夹,新建 package.json 并设置 {"type":"module"}
3. ECMAScript标准-命名导出和导入
掌握 ECMAScript 标准语法中,命名导出和导入的使用
ECMAScript 标准的语法有很多,常用的就是默认和命名导出和导入,这节课我们来学习下命名导出和导入的使用
需求:封装并导出基地址和数组求和函数
命名标准使用:
-
命名导出语法:
jsexport 修饰定义语句
-
命名导入语法:
jsimport { 同名变量 } from '模块名或路径'
代码示例:
-
utils.js 导出
js/** * 目标:基于 ECMAScript 标准语法,封装属性和方法并"命名"导出 */ export const baseURL = 'http://hmajax.itheima.net' export const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
-
index.js 导入
js/** * 目标:基于 ECMAScript 标准语法,"命名"导入,工具属性和方法使用 */ // 命名导入 import {baseURL, getArraySum} from './utils.js' console.log(obj) console.log(baseURL) console.log(getArraySum) const result = getArraySum([10, 21, 33]) console.log(result)
与默认导出如何选择:
markdown
* 按需加载,使用命名导出和导入
* 全部加载,使用默认导出和导入
小结
-
Node.js 支持哪 2 种模块化标准?
答案
- CommonJS 标准语法(默认)
- ECMAScript 标准语法
-
ECMAScript 标准,命名导出和导入的语法?
答案
- 导出:export 修饰定义的语句
- 导入:import { 同名变量 } from '模块名或路径'
-
ECMAScript 标准,默认导出和导入的语法?
答案
- 导出:export default {}
- 导入:import 变量名 from '模块名或路径'
-
ECMAScript 标准,命名导出和与默认导出如何选择?
答案
- 按需加载,使用命名导出和导入
- 全部加载,使用默认导出和导入
4.包的概念
包:将模块,代码,其他资料整合成一个文件夹,这个文件夹就叫包
包分类:
- 项目包:主要用于编写项目和业务逻辑
- 软件包:封装工具和方法进行使用
包要求:根目录中,必须有 package.json 文件(记录包的清单信息)
包使用:导入软件包时,引入的默认是 index.js 模块文件 / main 属性指定的模块文件(在引入一个包文件夹到代码中,默认引入的是包文件节下的 index.js 模块文件里导出的对象,如果没有 index.js 文件,则会引入 package.json 里 main 属性指定的文件模块导出的对象)
需求:封装数组求和函数的模块,封装判断用户名和密码长度函数的模块,形成一个软件包
代码示例:
js
/**
* 本文件是,utils 工具包的唯一出口
* 作用:把所有工具模块方法集中起来,统一向外暴露
*/
const { getArraySum } = require('./lib/arr.js')
const { checkUser, checkPwd } = require('./lib/str.js')
// 统一导出所有函数
module.exports = {
getArraySum,
checkUser,
checkPwd
}
- index.js 导入软件包文件夹使用(注意:这次导入的是包文件夹,不是模块文件)
js
/**
* 目标:导入 utils 软件包,使用里面封装的工具函数
*/
const obj = require('./utils')
console.log(obj)
const result = obj.getArraySum([10, 20, 30])
console.log(result)
小结
-
什么是包?
答案
- 将模块,代码,其他资料聚合成的文件夹
-
包分为哪 2 类呢?
答案
- 项目包:编写项目代码的文件夹,
- 软件包:封装工具和方法供开发者使用
-
package.json 文件的作用?
答案
- 记录软件包的名字,作者,入口文件等信息
-
导入一个包文件夹的时候,导入的是哪个文件?
答案
- 默认 index.js 文件,或者 package.json里面 main 属性指定的文件
5. npm软件包管理器
掌握使用 npm 管理软件包
npm 简介链接: 是 Node.js 标准的软件包管理器,用于下载和管理 Node.js 环境中的软件包
npm 使用步骤:
-
初始化清单文件: npm init -y (得到 package.json 文件,有则跳过此命令)
注意 -y 就是所有选项用默认值,所在文件夹不要有中文/特殊符号,建议英文和数字组成,因为 npm 包名限制建议用英文和数字或者下划线中划线
-
下载软件包:npm i 软件包名称
-
使用软件包

小结
-
npm 软件包管理器作用?
答案
- 下载软件包以及管理版本
-
初始化项目清单文件 package.json 命令?
答案
- npm init -y
-
下载软件包的命令?
答案
- npm i 软件包名字
-
下载的包会存放在哪里?
答案
- 当前项目下的 node_modules 中,并记录在 package.json 中
6. npm安装所有依赖
问题:我们拿到了一个别人编写的项目,但是没有 node_modules,项目能否正确运行?
答案:不能,因为缺少了项目需要的依赖软件包,比如要使用 dayjs 和 lodash 但是你项目里没有这个对应的源码,项目会报错的
为何没有给我 node_modules?
- 因为每个人在自己的本机使用 npm 下载,要比磁盘间传递要快(npm 有缓存在本机)
如何得到需要的所有依赖软件包呢?
- 直接在项目终端运行命令:
npm i
即可安装 package.json 里记录的所有包和对应版本到本项目中的 node_modules
小结
-
当前项目中只有 package.json 没有 node_modules 怎么办?
答案
- 当前项目目录下,打开终端,执行 npm i 安装所有依赖软件包
-
为什么 node_modules 不进行传递?
答案
- 因为用 npm 下载有缓存在本机,比磁盘之间传递要快
7. npm全局软件包-nodemon
掌握本地软件包和全局软件包区别,体验 nodemon 的使用
软件包区别:
- 本地软件包:当前项目内使用,封装属性和方法,存在于 node_modules
- 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置
nodemon 作用:替代 node 命令,检测代码更改,自动重启程序
使用:
- 安装:npm i nodemon -g (-g 代表安装到全局环境中)
- 运行:nodemon 待执行的目标 js 文件
小结
-
本地软件包和全局软件包区别?
答案
- 本地软件包,作用在当前项目,封装属性和方法
- 全局软件包,本地所有项目使用,封装命令和工具
-
nodemon 作用?
答案
- 替代 node 命令,检测代码更改,自动重启程序
-
nodemon 怎么用?
答案
- 先确保安装 npm i nodemon -g
- 使用 nodemon 执行目标 js 文件
8. Node.js概念和常用命令总结
把上面学的模块化语法,包的概念,常用命令进行总结
Node.js 模块化
-
概念:把每个文件当做一个模块,独立作用域,按需加载
-
使用:使用特定的标准语法导出和导入使用
CommonJS 标准:一般应用在 Node.js 项目环境中
ECMAScript 标准:一般应用在前端工程化项目中

Node.js 包
-
概念:把模块文件,代码文件,其他资料聚合成一个文件夹就是包
项目包:编写项目需求和业务逻辑的文件夹
软件包:封装工具和方法进行使用的文件夹(一般使用 npm 管理)
- 本地软件包:作用在当前项目,封装的属性/方法,供项目调用编写业务需求
- 全局软件包:作用在所有项目,一般封装的命令/工具,支撑项目运行

Node.js 常用命令:

小结
-
安装本地软件包的命令是什么?
答案
- npm i 软件包名
3. Webpack模块打包工具
1. Webpack 简介以及体验
了解 Webpack 的概念和作用,以及使用
定义:Webpack 是一个静态模块打包工具,从入口构建依赖图,打包有关的模块,最后用于展示你的内容
静态模块:编写代码过程中的 html,css, js,图片等固定内容的文件
打包过程,注意:只有和入口有直接/间接引入关系的模块,才会被打包
Webpack 的作用/打包:把静态模块内容,压缩,整合,转译等(前端工程化)
- 把 less/sass 转成 css 代码
- 把 ES6+ 降级成 ES5 等
- 支持多种模块标准语法

为何不学 vite?
- 现在很多项目还是基于 Webpack 来进行构建的,所以还是要掌握 Webpack 的使用
需求:封装 utils 包,校验手机号和验证码长度,在 src/index.js 中使用,使用 Webpack 打包
步骤:
-
新建并初始化项目:新建项目文件夹 Webpack_study,初始化包环境,得到 package.json 文件
bashnpm init -y
-
编写业务源代码:新建 src 源代码文件夹(书写代码)包括 utils/check.js 封装用户名和密码长度函数,引入到 src/index.js 进行使用
-
src/utils/check.js
js// 封装校验手机号长度和校验验证码长度的函数 export const checkPhone = phone => phone.length === 11 export const checkCode = code => code.length === 6
-
src/index.js
js/** * 目标1:体验 webpack 打包过程 */ // 1.1 准备项目和源代码 import { checkPhone, checkCode } from './utils/check.js' console.log(checkPhone('13900002020')) console.log(checkCode('123123123123')) // 1.2 准备 webpack 打包的环境 // 1.3 运行自定义命令打包观察效果(npm run 自定义命令)
-
-
下载 webpack webpack-cli 到当前项目中(版本独立),并配置局部自定义命令
bashnpm i webpack webpack-cli --save-dev
js//package.json中配置 build自定义命令 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" },
注意:虽然 webpack 是全局软件包,封装的是命令工具,但是为了保证项目之间版本分别独立,所以这次比较特殊,下载到某个项目环境下,但是需要把 webpack 命令配置到 package.json 的 scripts 自定义命令,作为局部命令使用
-
运行打包命令:项目中运行工具命令,采用自定义命令的方式(局部命令)
bashnpm run build
npm run 自定义命令名字
注意:实际上在终端运行的是 build 右侧的具体命名
注意:Node.js 默认只支持 CommonJS 标准语法,想要在当前项目环境下使用 ECMAScript 标准语法(import/export),需要在 package.json 文件设置 type: 'module'(注意浏览器也不支持ES6模块化的js代码,打包后的代码才能在浏览器上运行)从 webpack2 开始,已经内置了对 ES6、CommonJS、AMD 模块化语句的支持。但不包括新的ES6语法转为ES5代码,这部分工作还是留给了babel及其插件。
webpack对于ES模块/CommonJS模块的实现,是基于自己实现的webpack_require,所以打包后的代码能跑在浏览器中。
webpack根据webpack.config.js(wepack的配置文件,下一小节就会提到,目前用的是默认配置)中的入口文件,在入口文件里识别模块依赖,不管这里的模块依赖是用CommonJS写的,还是ES6 Module规范写的,webpack会自动进行分析,并通过转换、编译代码,打包成最终的文件。最终文件中的模块实现是基于webpack自己实现的webpack_require(es5代码),所以打包后的文件可以跑在浏览器上。
-
自动产生 dist 分发文件夹(压缩和优化后,用于最终运行的代码)
需求最终流程图:

小结
-
Webpack 有什么用?
答案
- 压缩,转译,整合,打包我们的静态模块
-
Webpack 怎么用?
答案
- 初始化环境,编写代码,安装 Webpack 软件包,配置自定义命令,打包体验查看结果
-
如何运行 package.json 里的自定义命令?
答案
- npm run 自定义命令
-
Webpack 默认入口和出口?
答案
- src/index.js 和 dist/main.js
2. Webpack 修改打包入口和出口
了解 Webpack 配置文件使用,影响 Webpack 打包过程和结果
Webpack 配置:影响 Webpack 打包过程和结果
步骤:
-
项目根目录,新建 webpack.config.js 配置文件
-
导出配置对象,配置入口,出口文件路径(别忘了修改磁盘文件夹和文件的名字)
jsconst path = require('path') module.exports = { entry: path.resolve(__dirname, 'src/login/index.js'), output: { path: path.resolve(__dirname, 'dist'), filename: './login/index.js', //注意:这里是login文件夹下的index.js clean: true // 生成打包后内容之前,清空输出目录 } }
-
重新打包观察
注意:只有和入口产生直接/间接的引入关系,才会被打包
小结
-
如何影响 Webpack 打包过程?
答案
- 查文档,新建配置文件和配置属性
3. 案例-用户登录-长度判断
体验前端项目代码,如何被 Webpack 打包和使用
需求:点击登录按钮,判断手机号和验证码长度是否符合要求
步骤:
-
准备用户登录页面:新建 public/login.html 准备网页模板(方便查找标签和后期自动生成 html 文件做准备)
-
编写核心 JS 逻辑代码:核心 JS 代码写在 src/login/index.js 文件
js/** * 目标3:用户登录-长度判断案例 * 3.1 准备用户登录页面 * 3.2 编写核心 JS 逻辑代码 * 3.3 打包并手动复制网页到 dist 下,引入打包后的 js,运行 */ // 3.2 编写核心 JS 逻辑代码 document.querySelector('.btn').addEventListener('click', () => { const phone = document.querySelector('.login-form [name=mobile]').value const code = document.querySelector('.login-form [name=code]').value if (!checkPhone(phone)) { console.log('手机号长度必须是11位') return } if (!checkCode(code)) { console.log('验证码长度必须是6位') return } console.log('提交到服务器登录...') })
-
打包并手动复制网页到 dist 下,引入打包后的 js,运行:运行自定义命令,让 webpack 打包 JS 代码;手动复制 public/login.html 到 dist 下,手动引入打包后的 JS 代码文件,运行 dist/login.html 在浏览器查看效果
注意:webpack默认只能处理
js
和json
资源,所以这里需要将打包后的js代码引入html里面查看效果,后续可以使用webpack插件让其支持更多类型
核心:Webpack 打包后的代码,作用在前端网页中使用

小结
-
Webpack 打包后的前端代码是如何运行的?
答案
- 手动引入到 html 文件中,再交给浏览器运行
4. Webpack 自动生成 html 文件
让 Webpack 拥有自动生成 html 文件能力,并引入打包后的其他资源
插件 html-webpack-plugin 作用:在 Webpack 打包时生成 html 文件,并引入其他打包后的资源
步骤:
-
下载 html-webpack-plugin 本地软件包到项目中
bashnpm i html-webpack-plugin --save-dev
-
配置 webpack.config.js 让 Webpack 拥有插件功能
js// ... const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // 插件(给webpack提供更多功能) plugins: [ //注意:output生成的js文件会自动加到html里面 new HtmlWebpackPlugin({ template: path.resolve(__dirname, "public/login.html"), // 模板文件 filename: path.resolve(__dirname, "dist/login/index.html") // 输出文件,注意最好以output.path开头 }) ] }
-
重新打包观察效果:指定以 public/login.html 为模板复制到 dist/login/index.html,并自动引入其他打包后资源;运行打包命令,观察打包后 dist 文件夹下内容并运行查看效果
小结
-
html-webpack-plugin 插件怎么用?
答案
- 找到插件文档,下载到项目中,配置到 Webpack 的配置文件中即可使用
5. Webpack-打包 css 代码
注意:Webpack 默认只识别 JS 和 JSON 文件内容,所以想要让 Webpack 识别更多不同内容,需要使用加载器
介绍需要的 2 个加载器来辅助 Webpack 才能打包 css 代码
- 加载器 css-loader:解析 css 代码
- 加载器 style-loader:把解析后的 css 代码插入到 DOM(style 标签之间)
步骤:
-
准备 css 文件代码引入到 src/login/index.js 中(压缩转译处理等)
js/** * 目标5:打包 css 代码 * 5.1 准备 css 代码,并引入到 js 中 * 5.2 下载 css-loader 和 style-loader 本地软件包 * 5.3 配置 webpack.config.js 让 Webpack 拥有该加载器功能 * 5.4 打包后观察效果 */ // 5.1 准备 css 代码,并引入到 js 中 import 'bootstrap/dist/css/bootstrap.min.css' import './index.css'
注意:这里只是引入代码内容让 Webpack 处理,不需定义变量接收在 JS 代码中继续使用,所以没有定义变量接收
-
下载 css-loader 和 style-loader 本地软件包
bashnpm i css-loader style-loader --save-dev
-
配置 webpack.config.js 让 Webpack 拥有该加载器功能
js// ... module.exports = { // ... module: { // 加载器 rules: [ // 规则列表 { test: /\.css$/i, // 匹配 .css 结尾的文件 use: ['style-loader', 'css-loader'], // 使用从后到前的加载器来解析 css 代码和插入到 DOM } ] } };
-
打包后观察效果:打包后运行 dist/login/index.html 观察效果,看看准备好的样式是否作用在网页上
小结
-
加载器的作用是什么?
答案
- 让 Webpack 识别更多的代码内容类型
6. 优化-提取 css 代码
让 Webpack 能够提取 css 代码到独立的 css 文件中
需求:让 webpack 把 css 代码内容字符串单独提取到 dist 下的 css 文件中
需要:mini-css-extract-plugin 插件来实现
插件 mini-css-extract-plugin:提取 css 代码
步骤:
-
下载 mini-css-extract-plugin 插件软件包到本地项目中
bashnpm i --save-dev mini-css-extract-plugin
-
配置 webpack.config.js 让 Webpack 拥有该插件功能
js// ... const MiniCssExtractPlugin = require("mini-css-extract-plugin") module.exports = { // ... module: { rules: [ { test: /\.css$/i, // use: ['style-loader', 'css-loader'] use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, plugins: [ // ... new MiniCssExtractPlugin() ] };
-
打包后观察效果
注意:不能和 style-loader 一起使用
好处:css 文件可以被浏览器缓存,减少 JS 文件体积,让浏览器并行下载 css 和 js 文件
注意:
js
plugins: [
// 不设置输出路径,默认是 dist/main.css
new MiniCssExtractPlugin()
]
js
plugins: [
// 若设置输出路径,需要是相对路径,否则打包时会报如下所示错误
//相对路径,是相对dist下的路径
new MiniCssExtractPlugin({
filename: "./login/index.css"
})
]

7. 优化-压缩过程
把单独提取的 css 文件内代码压缩
问题:css 代码提取后没有压缩
解决:使用 css-minimizer-webpack-plugin 插件
步骤:
-
下载 css-minimizer-webpack-plugin 插件软件包到本地项目中
bashnpm i css-minimizer-webpack-plugin --save-dev
-
配置 webpack.config.js 让 Webpack 拥有该插件功能
js// ... const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); module.exports = { // ... // 优化 optimization: { // 最小化 minimizer: [ // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 // `terser-webpack-plugin`),将下一行取消注释(保证 JS 代码还能被压缩处理) `...`, new CssMinimizerPlugin(), ], } };
-
打包后观察 css 文件内自己代码是否被压缩了
8. Webpack-打包 less 代码
加载器 less-loader:把 less 代码编译为 css 代码
注意:less-loader 需要配合 less 软件包使用
步骤:
-
新建 login/index.less 文件,设置背景图样式
lesshtml { body { background: url('./assets/login-bg.png') no-repeat center/cover; } }
-
less 样式引入到 src/login/index.js 中
js
/**
* 目标:打包 less 代码
* 8.1 新建 less 代码(设置背景图)并引入到 src/login/index.js 中
* 8.2 下载 less 和 less-loader 本地软件包
* 8.3 配置 webpack.config.js 让 Webpack 拥有功能
* 8.4 打包后观察效果
*/
// 8.1 新建 less 代码(设置背景图)并引入到 src/login/index.js 中
import './index.less'
-
下载 less 和 less-loader 本地软件包
bashnpm i less less-loader --save-dev
-
配置 webpack.config.js 让 Webpack 拥有功能
js// ... module.exports = { // ... module: { rules: [ // ... { test: /\.less$/i, use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"] } ] } }
-
打包后运行 观察效果
小结
-
Webpack 支持 less 代码打包需要哪 2 个软件包?
答案
- 需要 less less-loader 这 2 个软件包
9. Webpack-打包图片
资源模块:Webpack5 内置资源模块(字体,图片等)打包,无需额外 loader
步骤:
-
配置 webpack.config.js 让 Webpack 拥有打包图片功能
占位符 【hash】对模块内容做算法计算,得到映射的数字字母组合的字符串
占位符 【ext】使用当前模块原本的占位符,例如:.png / .jpg 等字符串
占位符 【query】保留引入文件时代码中查询参数(只有 URL 下生效)
-
打包后观察效果和区别
注意:判断临界值默认为 8KB
大于 8KB 文件:发送一个单独的文件并导出 URL 地址
小于 8KB 文件:导出一个 data URI(base64字符串)
-
在 src/login/index.js 中给 img 标签添加 logo 图片
js/** * 目标9:打包资源模块(图片处理) * 9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js * 9.2 打包后观察效果和区别 */ // 9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js // 注意:js 中引入本地图片资源要用 import 方式(如果是网络图片http地址,字符串可以直接写) import imgObj from './assets/logo.png' const theImg = document.createElement('img') theImg.src = imgObj document.querySelector('.login-wrap').appendChild(theImg)
-
配置 webpack.config.js 让 Webpack 拥有打包图片功能
js// ... module.exports = { // ... module: { rules: [ // ... { test: /\.(png|jpg|jpeg|gif)$/i, type: 'asset', generator: { filename: 'assets/[hash][ext][query]' } } ] } }
小结
-
资源模块指的是什么?
答案
- 图片,字体文件等等
10.案例-用户登录-完成功能
在 Webpack 环境下,使用 npm 下包作用在前端项目
-
需求:点击登录按钮,基于 npm 下载 axios 包,完成验证码登录功能
-
步骤:
-
使用 npm 下载 axios
bashnpm i axios
-
引入到 src/login/index.js 中编写业务实现
js/** * 目标10:完成登录功能 * 10.1 使用 npm 下载 axios(体验 npm 作用在前端项目中) * 10.2 准备并修改 utils 工具包源代码导出实现函数 * 10.3 导入并编写逻辑代码,打包后运行观察效果 */ // 10.3 导入并编写逻辑代码,打包后运行观察效果 import myAxios from '../utils/request.js' import { myAlert } from '../utils/alert.js' document.querySelector('.btn').addEventListener('click', () => { const phone = document.querySelector('.login-form [name=mobile]').value const code = document.querySelector('.login-form [name=code]').value if (!checkPhone(phone)) { myAlert(false, '手机号长度必须是11位') console.log('手机号长度必须是11位') return } if (!checkCode(code)) { myAlert(false, '验证码长度必须是6位') console.log('验证码长度必须是6位') return } myAxios({ url: '/v1_0/authorizations', method: 'POST', data: { mobile: phone, code: code } }).then(res => { myAlert(true, '登录成功') localStorage.setItem('token', res.data.token) location.href = '../content/index.html' }).catch(error => { myAlert(false, error.response.data.message) }) })
-
打包后运行观察效果
-
小结
-
npm 下载的包如何作用在前端项目上?
答案
- 被 Webpack 打包处理后,再引入到 html 文件中运行
10. Webpack 搭建开发环境
体验 webpack-dev-server 开发服务器,快速开发应用程序
问题:每次改动代码,都要重新打包,很麻烦,效率很低
开发环境:配置 webpack-dev-server 快速开发应用程序
作用:启动 Web 服务,打包输出源码在内存,并会自动检测代码变化,热更新到网页
步骤;
-
下载 webpack-dev-server 软件包到当前项目
bashnpm i webpack-dev-server --save-dev
-
设置打包的模式为开发模式,并配置自定义命令
js// ... module.exports = { // ... mode: 'development' }
json"scripts": { // ... "dev": "webpack serve --open //"dev": "webpack serve --mode=development" },
-
使用 npm run dev 来启动开发服务器,访问提示的域名+端口号,在浏览器访问打包后的项目网页,修改代码后试试热更新效果
在 js / css 文件中修改代码保存后,会实时反馈到浏览器
注意
-
webpack-dev-server 借助 http 模块创建 8080 默认 Web 服务
-
默认以 public 文件夹作为服务器根目录
-
webpack-dev-server 根据配置,打包相关代码在内存当中,以 output.path 的值作为服务器根目录(所以可以直接自己拼接访问 dist 目录下内容)
小结
-
webpack-dev-server 的作用?
答案
- 启动 Webpack 开发服务器,会启动一个 Web 服务,实时检测代码变化重新打包,并快速反应最新效果到浏览器页面上
11. Webpack 打包模式
了解不同打包模式对代码和环境的影响
-
打包模式:告知 Webpack 使用相应模式的内置优化
-
分类:
模式名称 模式名字 特点 场景 开发模式 development 调试代码,实时加载,模块热替换等 本地开发 生产模式 production 压缩代码,资源优化,更轻量等 打包上线 -
如何设置影响 Webpack呢?
-
方式1:在 webpack.config.js 配置文件设置 mode 选项
js// ... module.exports = { // ... mode: 'production' }
-
方式2:在 package.json 命令行设置 mode 参数
json"scripts": { "build": "webpack --mode=production", "dev": "webpack serve --mode=development" },
-
-
注意:命令行设置的优先级高于配置文件中的,推荐用命令行设置
小结
-
两种模式的区别?
答案
- 开发模式注重代码热替换更快,让开发调试代码更便捷,生产模式注重项目体积更小,更轻量,适配不同的浏览器环境
12. Webpack 打包模式的应用
了解 Webpack 打包模式的应用
需求:在开发模式下用 style-loader 内嵌更快,在生产模式下提取 css 代码
-
方案2:借助 cross-env (跨平台通用)包命令,设置参数区分环境
其中方案2 步骤:
- 下载 cross-env 软件包到当前项目
js
npm i cross-env --save-dev
- 配置自定义命令,传入参数名和值(会绑定到 process.env 对象下)
json
"scripts": {
"build": "cross-env NODE_ENV=production webpack --mode=production",
"dev": "cross-env NODE_ENV=development webpack serve --open --mode=development"
},
- 在 webpack.config.js 区分不同环境使用不同配置
js
module: {
rules: [
{
test: /\.css$/i,
// use: ['style-loader', "css-loader"],
use: [process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
}
],
},
- 重新打包观察两种配置区别
13. Webpack 前端注入环境变量
前端项目中,开发模式下打印语句生效,生产模式下打印语句失效
需求:前端项目中,开发模式下打印语句生效,生产模式下打印语句失效
问题:cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV
解决:使用 Webpack 内置的 DefinePlugin 插件
作用:在编译时,将前端代码中匹配的变量名,替换为值或表达式
配置 webpack.config.js 中给前端注入环境变量
js
// ...
const webpack = require('webpack')
module.exports = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
// key 是注入到打包后的前端 JS 代码中作为全局变量
// value 是变量对应的值(在 corss-env 注入在 node.js 中的环境变量字符串)
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]
}
14. Webpack 开发环境调错 source map
在开发环境如何精准定位到报错源码位置
问题:代码被压缩和混淆,无法正确定位源代码位置(行数和列数)
source map:可以准确追踪 error 和 warning 在原始代码的位置
设置:webpack.config.js 配置 devtool 选项
js
// 仅用于开发环境
module.exports = {
// ...
devtool: 'inline-source-map'
}
// 最好写成
//config里面是所有配置
const config = {...}
if (process.env.NODE_ENV === "development") {
config.devtool = "inline-source-map";
}
module.exports = config;
inline-source-map 选项:把源码的位置信息一起打包在 JS 文件内
注意:source map 适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)
小结
-
为何打包后,在控制台无法准确定位到源码的位置信息?
答案
- 因为 Webpack 把代码压缩和混淆了
15. Webpack 设置解析别名路径
设置 Webpack 如何设置路径别名,方便我们引入目标模块
解析别名:配置模块如何解析,创建 import 或 require 的别名,来确保模块引入变得更简单
例如:原来路径如图,比较长而且相对路径不安全
解决:在 webpack.config.js 中配置解析别名 @ 来代表 src 绝对路径
例如:
-
原来路径如下:
jsimport { checkPhone, checkCode } from '../src/utils/check.js'
-
配置解析别名:在 webpack.config.js 中设置
js// ... const config = { // ... resolve: { alias: { '@': path.resolve(__dirname, 'src') } } }
-
这样我们以后,引入目标模块写的路径就更简单了
jsimport { checkPhone, checkCode } from '@/utils/check.js'
-
修改代码的路径后,重新打包观察效果是否正常!
小结
-
路径中的 '@' 符号代表什么意思?
答案
- 看在 webpack 配置中的别名路径是什么,就会在打包时替换成哪个路径使用
16. 优化-CDN使用
开发模式使用本地第三方库,生产模式下使用 CDN 加载引入
作用:把静态资源文件/第三方库放在 CDN 网络中各个服务器中,供用户就近请求获取
好处:减轻自己服务器请求压力,就近请求物理延迟低,配套缓存策略
需求:开发模式使用本地第三方库,生产模式下使用 CDN 加载引入

步骤:
html
<% if(htmlWebpackPlugin.options.useCdn){ %>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
<% } %>
- 配置 webpack.config.js 中 externals 外部扩展选项(防止某些 import 的包被打包)
js
// 生产环境下使用相关配置
if (process.env.NODE_ENV === 'production') {
// 外部扩展(让 webpack 防止 import 的包被打包进来)
config.externals = {
// key:import from 语句后面的字符串
// value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios': 'axios'
}
}
js
// ...
const config = {
// ...
plugins: [
new HtmlWebpackPlugin({
// ...
// 自定义属性,在 html 模板中
// 生产模式下使用 cdn 引入的地址
<%=htmlWebpackPlugin.options.useCdn%> 访问使用
useCdn: process.env.NODE_ENV === 'production'
})
]
}
- 两种模式下打包观察效果
17. Webpack 多页面打包
让 Webpack 同时打包登录和内容列表页面
概念:
-
单页面:单个 html 文件,切换 DOM 的方式实现不同业务逻辑展示,后续 Vue/React 会学到
-
多页面:多个 html 文件,切换页面实现不同业务逻辑展示
需求:把黑马头条-数据管理平台-内容页面一起引入打包使用
步骤:
-
准备源码(html,css,js)放入相应位置,并改用模块化语法导出
-
下载 form-serialize 包并导入到核心代码中使用
-
配置 webpack.config.js 多入口和多页面的设置
js// ... const config = { entry: { '模块名1': path.resolve(__dirname, 'src/入口1.js'), '模块名2': path.resolve(__dirname, 'src/入口2.js'), }, output: { path: path.resolve(__dirname, 'dist'), filename: './[name]/index.js' } plugins: [ new HtmlWebpackPlugin({ template: './public/页面2.html', // 模板文件 filename: './路径/index.html', // 输出文件 chunks: ['模块名2'] }) new HtmlWebpackPlugin({ template: './public/页面2.html', // 模板文件 filename: './路径/index.html', // 输出文件 chunks: ['模块名2'] }) ] }
-
重新打包观察效果
18. 案例-发布文章页面打包
需求:把发布文章页面一起打包
步骤:
-
准备发布文章页面源代码,改写成模块化的导出和导入方式
-
修改 webpack.config.js 的配置,增加一个入口和出口
-
打包观察效果
19. 优化-分割公共代码
需求:把 2 个以上页面引用的公共代码提取
步骤:
-
配置 webpack.config.js 的 splitChunks 分割功能
js// ... const config = { // ... optimization: { // 代码分割 splitChunks: { chunks: 'all', // 所有模块动态非动态移入的都分割分析 cacheGroups: { // 分隔组 commons: { // 抽取公共模块 minSize: 0, // 抽取的chunk最小大小字节 minChunks: 2, // 最小引用数 reuseExistingChunk: true, // 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用 name(module, chunks, cacheGroupKey) { // 分离出模块文件名 const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名1~模块名2 return `./js/${allChunksNames}` // 输出到 dist 目录下位置 } } } }
-
打包观察效果
Git 版本控制系统
1. Git 初识
-
概念:一个免费开源,分布式的代码版本控制系统,帮助开发团队维护代码
-
作用:记录代码内容,切换代码版本,多人开发时高效合并代码内容
-
如何学:
个人本机使用:Git 基础命令和概念
多人共享使用:团队开发同一个项目的代码版本管理
安装
Mac系统:dmg 程序,默认下一步即可
检验成功:
1.打开 bash 终端(git 专用)
2.命令:git -v(查看版本号)
配置用户信息
配置:用户名和邮箱,应用在每次提交代码版本时表明自己身份
命令:
git config --global user.name "itheima"
git config --global user.email "itheima@itcast.cn"
总结
-
为何学习 Git ?
管理代码版本,记录,切换,合并代码
-
Git 学习:
现在本机自己使用
再学习多人共享使用
-
如何安装使用?
程序双击安装
在 VSCode 中使用 bash 终端以及 git 命令
2. Git 仓库
Git 仓库(repository):记录文件状态内容的地方,存储着修改的历史记录
创建:
1.把本地文件夹转换成 Git 仓库:命令 git init
2.从其他服务器上克隆 Git 仓库
总结
-
什么是 Git 仓库 ?
记录文件状态内容和历史记录的地方(.git 文件夹)
-
如何创建 Git 仓库?
把本地文件夹转换成 Git 仓库:命令 git init
从其他服务器上克隆 Git 仓库
3.Git 的三个区域
Git 使用时:
工作区:实际开发时操作的文件夹
暂存区:保存之前的准备区域(暂存改动过的文件)
版本库:提交并保存暂存区中的内容,产生一个版本快照
命令 | 作用 |
---|---|
git add 文件名 | 暂存指定文件 |
git add . | 暂存所有改动的文件 |
git commit -m "注释说明" | 提交并保存,产生版本快照 |

总结
-
Git 使用时有哪些区域 ?
工作区,暂存区,版本库
-
工作区的内容,最终要如何保存在版本库中?
git add 添加到暂存区
等待时机后 git commit 提交保存到版本库,产生一次版本快照记录
4. Git 文件状态
Git 文件 2 种状态:
-
未跟踪:新文件,从未被 Git 管理过
-
已跟踪:Git 已经知道和管理的文件
文件状态 | 概念 | 场景 |
---|---|---|
未跟踪(U) | 从未被 Git 管理过 | 新文件 |
新添加(A) | 第一次被 Git 暂存 | 之前版本记录无此文件 |
未修改('') | 三个区域统一 | 提交保存后 |
已修改(M) | 工作区内容变化 | 修改了内容产生 |
使用:修改文件,暂存,提交保存记录,如此反复
总结
-
Git 文件状态分为哪 2 种 ?
未跟踪和已跟踪(新添加,未修改,已修改)
-
如何查看暂存区和工作区文件状态?
git status -s
5. Git 暂存区作用
暂存区:暂时存储,可以临时恢复代码内容,与版本库解耦
暂存区 -> 覆盖 -> 工作区,命令:git restore 目标文件(注意:完全确认覆盖时使用)
从暂存区移除文件,命令:git rm --cached 目标文件

总结
-
如何移除暂存区已暂存的文件?
git rm --cached 目标文件
7. Git-切换版本
概念:把版本库某个版本对应的内容快照,恢复到工作区/暂存区
查看提交历史:git log --oneline
回退命令:
git reset --soft 版本号(其他文件未跟踪)
git reset --hard 版本号
git reset --mixed 版本号 (与 git reset 等价)
注意1:只有记录在版本库的提交记录才能恢复
注意2:回退后,继续修改->暂存->提交操作即可(产生新的提交记录过程)
总结
-
什么是 Git 回退版本?
把版本库某个版本对应的内容快照,恢复到工作区/暂存区
-
强制覆盖暂存区和工作区的命令?
git reset --hard 版本号
-
如何查看提交历史?
git log --oneline
git reflog --oneline
8. 删除文件
需求:删除 editor.js 文件,并产生一次版本记录
步骤:
1.手动删除工作区文件
2.暂存变更/手动删除暂存区文件造成变更
3.提交保存
总结:
工作区只要改变,都可以暂存提交产生新记录

9. 忽略文件
概念:.gitignore 文件可以让 git 彻底忽略跟踪指定文件
目的:让 git 仓库更小更快,避免重复无意义的文件管理
例如:
1.系统或软件自动生成的文件
2.编译产生的结果文件
3.运行时生成的日志文件,缓存文件,临时文件等
4.涉密文件,密码,秘钥等文件
创建:
1.项目根目录新建 .gitignore 文件
2.填入相应配置来忽略指定文件
注意:如果文件已经被暂存区跟踪过,可以从暂存区移除即可

10. 分支的概念
概念:本质上是指向提交节点的可变指针,默认名字是 master
注意:HEAD 指针影响工作区/暂存区的代码状态
场景:开发新需求 / 修复 Bug,保证主线代码随时可用,多人协同开发提高效率
例如:在现有代码上创建新分支完成内容列表业务,突然需要紧急修复 Bug - 单独创建分支解决 Bug
需求:创建内容列表 content 分支,并产生 3 次提交记录
步骤:
-
创建分支命令:git branch 分支名
-
切换分支命令:git checkout 分支名
-
工作区准备代码并暂存提交,重复 3 次
总结
-
什么是 Git 分支?
指针,指向提交记录
-
HEAD 指针的作用?
影响暂存区和工作区的代码
-
如何创建和切换指针?
git branch 分支名
git checkout 分支名
11. 练习-登录 bug 修复
需求:新建 login-bug 分支,做 2 次提交记录(对手机号长度,验证码长度做判断)
步骤:
1.切回到主分支:git checkout master
2.创建新分支:git branch login-bug
3.切换新分支:git checkout login-bug
4.修改代码,暂存,提交产生版本记录
12. 分支-合并与删除
需求:把 login-bug 合并回到 master 分支并删除 login-bug 分支
步骤:
1.切回到要合入的分支上:git checkout master
2.合并其他分支过来:git merge login-bug
3.删除合并后的分支指针:git branch -d login-bug
13. 分支-合并与提交
合并提交:发生于原分支产生了新的提交记录后,再合并回去时发生,自动使用多个快照记录合并后产生一次新的提交
步骤:
1.切回到要合入的分支上:git checkout master
2.合并其他分支过来:git merge content
3.删除合并后的分支:git branch -d content
最后合并回到主分支上时,提交记录流程图:
注意:提交记录的顺序按照产生的先后顺序排列,而非合并的先后顺序
14. 分支-合并冲突
需求1:基于 master 新建 publish 分支,完成发布文章业务,然后修改内容页面的 html 文件的 title 标签,并提交一次
需求2:切换到 master,也在修改内容页面的 html 文件的 title 标签,并提交一次
冲突:把 publish 分支合并到 master 回来,产生合并冲突
概念:不同分支中,对同一个文件的同一部分修改,Git 无法干净的合并,产生合并冲突
解决:
1.打开 VSCode 找到冲突文件并手动解决
2.解决后需要提交一次记录
避免:(多交流)
1.按页面划分不同分支开发
2.公共代码在统一文件夹维护
3.Node等软件版本统一,npm 包统一下载
15. Git 常用命令
命令 | 作用 | 注意 |
---|---|---|
git -v | 查看 git 版本 | |
git init | 初始化 git 仓库 | |
git add 文件标识 | 暂存某个文件 | 文件标识以终端为起始的相对路径 |
git add . | 暂存所有文件 | |
git commit -m '说明注释' | 提交产生版本记录 | 每次提交,把暂存区内容快照一份 |
git status | 查看文件状态 - 详细信息 | |
git status -s | 查看文件状态 - 简略信息 | 第一列是暂存区状态,第二列是工作区状态 |
git ls-files | 查看暂存区文件列表 | |
git restore 文件标识 | 从暂存区恢复到工作区 | 如果文件标识为 . 则恢复所有文件 |
git rm --cached 文件标识 | 从暂存区移除文件 | 不让 git 跟踪文件变化 |
git log | 查看提交记录 - 详细信息 | |
git log --oneline | 查看提交记录 - 简略信息 | 版本号 分支指针 提交时说明注释 |
命令 | 作用 | 注意 |
---|---|---|
git reflog --oneline | 查看完整历史 - 简略消息 | 包括提交,切换,回退等所有记录 |
git reset 版本号 | 切换版本代码到暂存区和工作区 | --soft 模式保留暂存区和工作区原本内容 --hard 模式不保留暂存区和工作区原本内容 --mixed 模式不保留暂存区,工作区保留(默认) 先覆盖到暂存区,再用暂存区对比覆盖工作区 |
git branch 分支名 | 创建分支 | |
git branch | 查看本地分支 | |
git branch -d 分支名 | 删除分支 | 请确保记录已经合并到别的分支下,再删除分支 |
git checkout 分支名 | 切换分支 | |
git checkout -b 分支名 | 创建并立刻切换分支 | |
git merge 分支名 | 把分支提交历史记录合并到当前所在分支 | |
16. Git 远程仓库
概念:托管在因特网或其他网络中的你的项目的版本库
作用:保存版本库的历史记录,多人协作
创建:公司自己服务器 / 第三方托管平台(Gitee,GitLab,GitHub...)
需求:创建远程版本库,并把本地 Git 仓库推送上去保存
步骤:
-
注册第三方托管平台网站账号
-
新建仓库得到远程仓库 Git 地址
-
本地 Git 仓库添加远程仓库原点地址
命令:git remote add 远程仓库别名 远程仓库地址
例如:git remote add origin gitee.com/lidongxu/wo...
-
本地 Git 仓库推送版本记录到远程仓库
命令:git push -u 远程仓库别名 本地和远程分支名
例如:git push -u origin master
完整写法:git push --set-upstream origin master:master
总结
-
远程版本库的作用?
保存提交历史记录,多人共享
-
远程版本库使用步骤?
创建远程版本库(自己服务器/第三方托管平台)
本地版本库设置远程地址
推送本地版本库到远程
-
推送的命令?
git push -u origin master
17. Git 远程仓库 - 克隆
克隆:拷贝一个 Git 仓库到本地,进行使用
命令:git clone 远程仓库地址,例如:git clone gitee.com/lidongxu/wo...
效果:在运行命令所在文件夹,生成 work 项目文件夹(包含版本库,并映射到暂存区和工作区)
注意1:Git 本地仓库已经建立好和远程仓库的链接
注意2:仓库公开随意克隆,推送需要身为仓库团队成员
18. 多人协同开发
需求:小传新代码共享给小智
步骤:
1.小传开发代码 -> 工作区 -> 暂存区 -> 提交 -> 拉取(可选)-> 推送
2.小智 -> 拉取(后续也可以开发代码 -> ... -> 推送)
3.想要看到别人同步上去的最新内容:git pull origin master 等价于
git fetch origin master:master(获取远程分支记录到本地,未合并)
git merge origin/master (把远程分支记录合并到所在分支下)
19. VSCode 中使用 Git
使用:源代码管理,进行暂存,撤销,对比差异,提交等操作
注意:VSCode 打开的项目文件夹下需要有 .git 仓库
20. 案例-发布黑马头条数据管理平台
需求:把 Webpack 压缩好的 dist 分发文件夹网页,部署到码云上,开启 Page 服务在互联网中浏览
步骤:
1.初始化本地 Git 仓库(这次是非空文件夹-配套素材 dist 文件夹)
2.初始化远程 Git 仓库(这一次也是非空的)
3.本地配置远程仓库链接
4.本地拉取合并一下(确认本地要包含远程内容时使用)
5.本地推送到远程 Git 仓库
6.开启 page 网页服务得到地址浏览
21. Git 常用命令
命令 | 作用 | 注意 |
---|---|---|
git remote add 远程仓库别名 远程仓库地址 | 添加远程仓库地址 | 别名唯一,地址是 .git 结尾的网址 |
git remote -v | 查看远程仓库地址 | |
git remote remove 远程仓库别名 | 删除远程仓库地址 | |
git pull 远程仓库别名 分支名 | 拉取 | 完整写法:git pull 远程仓库别名 远程分支名:本地分支名 等价于:git fetch 和 git merge |
git push 远程仓库别名 分支名 | 推送 | 完整写法:git push 远程仓库别名 本地分支名:远程分支名 -u:建立通道后以后可以简写 git push |
git pull --rebase 远程仓库别名 分支名 | 拉取合并 | 合并没有关系的记录 |
git clone 远程仓库地址 | 克隆 | 从0得到一个远程的Git仓库到本地使用 |