AJAX

Axios

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="box"></div>
<!--
axios库地址: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
省份数据地址: http://ajax-api.itheima.net/api/province
-->
<!-- 先引入axios库 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// axios 基本语法
axios({
url : 'http://ajax-api.itheima.net/api/province'
}).then (result =>{
document.querySelector(".box").innerHTML = result.data.data.join("</br>");
})
</script>

查询参数

浏览器可以给服务器提供额外的参数,让服务器返回浏览器想要的数据

语法:http://xxx.com/xxx/xxx?参数名1=值1&参数名2=值2

axios中使用params选项来传递参数

1
2
3
4
5
6
7
8
axios({
url : '',
params:{
键:值
}
}).then(result =>{
console.log(result)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="box"></div>
<!--
axios库地址: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
省份数据地址: http://ajax-api.itheima.net/api/province
-->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
axios({
url : 'http://ajax-api.itheima.net/api/city',
params : {
pname: '河北省'
}
}).then (result =>{
document.querySelector(".box").innerHTML = result.data.data.join("</br>");
})
</script>

请求方法

axios请求配置

URL:请求的URL网址

method: 请求的方法,GET可以省略

data:提交数据

1
2
3
4
5
6
7
8
9
axios({
url : '',
method : '请求方法',
data : {
参数名 : 值
}
}).then(result =>{

})
  1. GET 获取数据
  2. POST 提交数据
  3. PUT 修改数据(全部)
  4. DELETE 删除数据
  5. PATCH 修改数据(部分)

错误处理

通过catch捕获错误

1
2
3
4
5
6
7
axios({

}).then(result =>{

}).catch(error =>{

})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
document.querySelector(".btn").addEventListener("click", () =>{
axios({
url : "http://hmajax.itheima.net/api/register",
method : 'post',
data : {
username : "NuyoahII",
password : "123456"
}
}).then(result=>{
console.log(result.data)
}).catch(error =>{
alert(error.response.data.message)
})
})

HTTP协议-请求报文

HTTP报文:规定了浏览器发送即服务器返回内容的格式

请求报文:浏览器按照HTTP协议要求的格式,发送给服务器的内容

请求报文组成部分:

  1. 请求行:请求方法,URL,协议
  2. 请求头:以键值对的格式携带的附加信息,比如Content-Type

F12中网络中的Fetch/XHR是专门查看AJAX请求信息的

HTTP协议-相应报文

HTTP报文:规定了浏览器发送即服务器返回内容的格式

相应报文:服务器按照HTTP协议要求的格式,返回给浏览器的内容

响应报文内容

  1. 响应行 (状态行) :协议、HTTP 响应状态码、状态信息
  2. 响应头:以键值对的格式携带的附加信息,比如: Content-Type
  3. 空行:分隔响应头,空行之后的是服务器返回的资源
  4. 响应体:返回的资源

响应状态码:表明这次请求是否成功

  1. 1xx 信息
  2. 2xx 成功
  3. 3xx 重定向消息
  4. 4xx 客户端错误
  5. 5xx 服务端错误

接口文档

描述接口的文章(后端管)

接口:使用AJAX和服务器通讯时,使用的URL,请求方法,以及参数

黑马AJAX接口文档

form-serialize插件

作用:快速收集表单元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>

图书管理

Bootstrap弹窗

  1. 引入bootstrap.css和bootstrap.js
  2. 准备弹窗标签,确认结构
  3. 通过自定义属性,控制弹窗的显隐
    • data-bs-toggle=“modal” // 通过该属性控制弹窗的显隐
    • data-bs-target=“css选择器” // 通过该属性控制那个弹窗

控制显隐

  1. 通过属性控制,弹框显示或隐藏

    通过添加data-bs-toggle与data-bs-target来控制弹框的显隐

    1
    2
    3
    <button type="button" class="btn btn-primary mybtn" data-bs-toggle="modal" data-bs-target=".my-box">
    显示弹框
    </button>
  2. 通过JS控制弹框显示或隐藏

    创建弹窗对象

    const modalDom = document.querySelector(“css选择器”)

    const modal = new bootstrap.Modal(modalDom)

    显示弹窗

    modal.show()

    关闭弹窗

    modal.hide()

获取图书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const creator = "老张"
// 获取图书列表
function getBooksList(){
axios({
url:"http://hmajax.itheima.net/api/books",
params:{
creator
}
}).then(result =>{
//获取数据 并渲染
document.querySelector(".list").innerHTML = result.data.data.map(({id,bookname, author, publisher},index) => {
return `<tr>
<td>${index+1}</td>
<td>${bookname}</td>
<td>${author}</td>
<td>${publisher}</td>
<td data-id="${id}">
<span class="del">删除</span>
<span class="edit">编辑</span>
</td>
</tr>`}).join("")

}).catch(error =>{
console.log(error)
})
}

添加图书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 获取modal对象
const addModal = new bootstrap.Modal(document.querySelector(".add-modal"))
// 添加图书
function addBooks(){
// 获取form表单数据
const addFrom = document.querySelector(".add-form")
const {bookname, author, publisher} = serialize(addFrom, {hash:true, empty:true})

if(!bookname){
alert("书名不能为空")
}else if(!author){
alert("作者不能为空")
}else if(!publisher){
alert("出版商不能为空")
}

axios({
url:"http://hmajax.itheima.net/api/books",
method:"post",
data:{
bookname,
author,
publisher,
creator
}
}).then(result =>{
getBooksList()
addModal.hide()
})

}

删除图书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 获取保存数据
const addBtn = document.querySelector(".add-btn")
addBtn.addEventListener("click", addBooks)

// 删除图书
document.querySelector(".list").addEventListener("click", e =>{
if(e.target.className === "del"){
axios({
url:`http://hmajax.itheima.net/api/books/${e.target.parentNode.dataset.id}`,
method:"delete",
}).then(result=>{
getBooksList()
})

}
})

修改图书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const editModal = new bootstrap.Modal(document.querySelector(".edit-modal"))
// 修改图书-展示
document.querySelector(".list").addEventListener("click", e=>{
if(e.target.className === "edit"){
axios({
url:`http://hmajax.itheima.net/api/books/${e.target.parentNode.dataset.id}`,
method:'get'
}).then(result=>{
const data = result.data.data
Object.keys(data).forEach((key)=>{
document.querySelector(`.edit-form .${key}`).value = data[key]
})
})
editModal.show()
}
})
// 修改图书-提交
document.querySelector(".edit-btn").addEventListener("click", ()=>{
// 获取form表单数据
const editFrom = document.querySelector(".edit-form")
const {id, bookname, author, publisher} = serialize(editFrom, {hash:true, empty:true})

axios({
url:`http://hmajax.itheima.net/api/books/${id}`,
method:"put",
data:{
bookname,
author,
publisher,
creator
}
}).then(result=>{
getBooksList()
})
editModal.hide()
})

上传图像

图像上传一般使用FormData类型上传

  1. 获取图片对象

  2. 使用FormData携带图片文件

    1
    2
    const fd = new FormData()
    fd.append("参数名", "值")
  3. 提交表单数据到服务器,使用图片URL网址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!-- 文件选择元素 -->
    <input type="file" class="upload">
    <img src="" alt="" class="my-img">

    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
    document.querySelector(".upload").addEventListener("change", (e)=>{
    const fd = new FormData()
    fd.append("img", e.target.files[0])

    axios({
    url:"http://hmajax.itheima.net/api/uploadimg",
    method:"post",
    data:fd
    }).then(result=>{
    // 取出返回的url网址并展示到页面上
    const imgUrl = result.data.data.url
    document.querySelector(".my-img").src = imgUrl
    })
    })
    </script>

用户管理

获取用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const creator = "老张"
// 用户信息渲染
function renderInfo(){
axios({
url:"http://hmajax.itheima.net/api/settings",
method:"get",
params:{
creator,
}
}).then(result=>{
const {avatar, email, nickname, gender, desc} = result.data.data
document.querySelector(".email").value = email
document.querySelector(".nickname").value = nickname
document.querySelectorAll(".gender")[gender].checked = true
document.querySelector(".desc").value = desc
document.querySelector(".prew").src = avatar
})
}
// 用户渲染
renderInfo()

头像修改

1
2
3
4
5
6
7
8
9
10
11
12
13
// 头像修改
document.querySelector("#upload").addEventListener("change", (e)=>{
const fd = new FormData()
fd.append("avatar", e.target.files[0])
fd.append("creator", creator)
axios({
url:"http://hmajax.itheima.net/api/avatar",
method:"put",
data: fd
}).then(result=>{
document.querySelector(".prew").src = result.data.data.avatar
})
})

信息修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 信息修改
document.querySelector(".submit").addEventListener("click", ()=>{
const userObj = serialize(document.querySelector(".user-form"), {hash:true, empty:true})
userObj.gender = +userObj.gender
userObj.creator = creator
axios({
url:"http://hmajax.itheima.net/api/settings",
method:"put",
data:userObj
}).then(result=>{
const {email, nickname, gender, desc} = result.data.data
document.querySelector(".email").value = email
document.querySelector(".nickname").value = nickname
document.querySelectorAll(".gender")[gender].checked = true
document.querySelector(".desc").value = desc

// 控制提示框的显隐
const toastDom = document.querySelector(".my-toast")
const toast = new bootstrap.Toast(toastDom)
toast.show()
})
})

AJAX原理

AJAX的原理就是XMLHttpRequest对象

XMLHttpRequest基本用法

用户可以通过XMLHttpRequest在不刷新页面的情况下请求特定的URL获取数据

使用方法

  1. 创建XMLHttpRequest 对象

    1
    const xhr = new XMLHttpRequest()
  2. 配置请求方法和请求 url 地址

    1
    xhr.open("请求方法", "请求URL网址")
  3. 监听 loadend 事件,接收响应结果

    1
    2
    3
    4
    xhr.addEventListener("loadend", ()=>{
    // xhr返回的是字符串,需要手动转换成对象
    const data = JSON.parse(xhr.response)
    })
  4. 发起请求

    1
    xhr.send()

XMLHttpRequest查询参数

XML查询参数是放在网址中的

1
2
3
4
5
6
const xhr = new XMLHttpRequest()
xhr.open("GET", "http://hmajax.itheima.net/api/city?panme=河北省")
xhr.addEventListener("loadend", ()=>{
document.querySelector("p").innerHtml = JSON.parse(xhr.response).join("<br>")
})
xhr.send()

使用模板字符串

1
2
3
4
5
6
7
8
9
10
const xhr = new XMLHttpRequest()
document.querySelector(".sel-btn").addEventListener("click", ()=>{
const pname = document.querySelector(".province").value
const cname = document.querySelector(".city").value
xhr.open("get", `http://hmajax.itheima.net/api/area?pname=${pname}&cname=${cname}`)
xhr.addEventListener("loadend", ()=>{
document.querySelector(".list-group").innerHTML = JSON.parse(xhr.response).list.map(item=>`<li class="list-group-item">${item}</li>`).join("")
})
xhr.send()
})

使用URLSearchParams 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
document.querySelector(".sel-btn").addEventListener("click", ()=>{
const pname = document.querySelector(".province").value
const cname = document.querySelector(".city").value
// 组成对象
const qObj = {
pname,
cname
}
// 使用URLSearchParams组成参数 键=值&键=值
const paramObj = new URLSearchParams(qObj)
const queryString = paramObj.toString()

// 使用XHR获取对象
const xhr = new XMLHttpRequest()
xhr.open("get", `http://hmajax.itheima.net/api/area?${queryString}`)
xhr.addEventListener("loadend", ()=>{
document.querySelector(".list-group").innerHTML = JSON.parse(xhr.response).list.map(item=>`<li class="list-group-item">${item}</li>`).join("")
})
xhr.send()
})

XMLHttpRequest传递数据

需要设置请求头和请求体

请求头:Content-Type: application/json

请求体携带JSON字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const xhr = new XMLHttpRequest()
xhr.open("请求方式", "请求网址")
xhr.addEventListener("loadend", ()=>{
console.log(xhr.response)
})

// 设置请求头请求体
xhr.setRequestHeader("Content-Type", "application/json")

// 准备数据
const user = {usename: "zhang", password:"123456"}
const userStr = JSON.stringify(user)

// 发送数据
xhr.send(userStr)

Promise

promise:对象用于表示一个异步操作最终完成或失败及其结果值

好处

  1. 逻辑更加清晰
  2. 了解axios函数内部运作机制
  3. 能解决回调函数地狱问题

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 创建Promise对象
const p = new Promise((resolve, reject)=>{
// 2.执行异步任务并传递结果值
// 成功调用:resolve(),触发执行then
// 失败调用:reject(),触发执行catch
})
// 3. 接受结果
p.then(result=>{

}).catch(error=>{

})

Promise三种状态

  1. 待定:pending :初始状态,既没有被兑现,也没有别拒绝,刚创建
  2. 已兑现:fulfilled:操作成功,调用resolve()
  3. 已拒绝:rejected:操作失败,调用reject()

一旦成为兑现,拒绝状态,promise就无法变为其他状态

Promise+XHR获取省份列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. 创建Promise对象,异步控制对象
const p = new Promise((resolve, reject) => {
// 2. 创建XHR发送请求对象
const xhr = new XMLHttpRequest()
xhr.open("GET", `http://hmajax.itheima.net/api/province`)
xhr.addEventListener("loadend", ()=>{
// 判断执行成功与失败
if(xhr.status >= 200 && xhr.status < 300){
// 执行成功
resolve(JSON.parse(xhr.response))
} else {
// 实行失败
reject(new Error(xhr.response))
}
})
xhr.send()
})

// 根据成功失败进行下一步操作
p.then(result =>{
document.querySelector(".list-group").innerHTML = result.list.map(item => `<li class="list-group-item">${item}</li>`).join("")
}).catch(error =>{
// 错误对象需要使用console.dir详细打印
console.dir(error)
})

封装axios获取省份列表(无传参)

promise+xhr封装成myAxios函数

  1. 定义myAxios函数,接收配置对象,返回Promise对象
  2. 发起XHR请求,默认请求方法为GET
  3. 调用成功/失败的处理程序
  4. 使用myAxios函数,获取省份列表展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myAxios(config){
return new Promise((resolve, reject)={
// xhr请求
})
}

// myAxios返回的是promise对象,promise对象拥有then 和 catch方法
myAxios({

}).then(result={

}).catch(error=>{

})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1.定义myAxios函数
function myAxios(config){
return new Promise((resolve, reject)=>{
// 2.发起xhr请求
const xhr = new XMLHttpResponse()
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()
})
}

// 发起请求
myAxios({
url:"http://hmajax.itheima.net/api/province"
method:"get"
}).then(result =>{

}).catch(error =>{

})

封装axios(有传参)

传递的参数通过URLSearchParams将参数转换成对应的键值字符串

  1. 调用myAxios函数,传入Params参数
  2. 基于URLSearchParams转换查询参数为字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1.定义myAxios函数
function myAxios(config){
return new Promise((resolve, reject)=>{
// 2.发起xhr请求
const xhr = new XMLHttpResponse()
// 判断用户是否传参
if(config.params){
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()
})
}
myAxios({
url:"http://hmajax.itheima.net/api/area",
params:{
pname:"河北省"
cname:"邯郸市"
}
}).then(result=>{

}).catch(error=>{

})

封装axios(有传值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 1.定义myAxios函数
function myAxios(config){
return new Promise((resolve, reject)=>{
// 2.发起xhr请求
const xhr = new XMLHttpResponse()
// 判断用户是否传参
if(config.params){
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))
}
})
// 判断是否有数据传送
if(config.data){
// 设置数据传送头格式
xhr.setRequestHeader("Content-Type", "application/json")
// 传送数据
xhr.send(JSON.stringify(config.data))
}else {
xhr.send()
}
})
}

天气案例-展示天气

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
function getWeather(cityCode){
myAxios({
url:"http://hmajax.itheima.net/api/weather",
params:{
city:cityCode
}
}).then(result=>{
console.log(result)
const {dateShort, dateLunar, area, psPm25, psPm25Level, temperature, windDirection, windPower} = result.data
const {humidity,
sunriseTime,
sunsetTime,
temDay,
temNight,
ultraviolet,
weather,} = result.data.todayWeather
document.querySelector(".title").innerHTML = `
<span class="dateShort">${dateShort}</span>
<span class="calendar">农历&nbsp;
<span class="dateLunar">${dateLunar}</span>
</span>`
document.querySelector(".area").innerHTML = area
document.querySelector(".weather-box").innerHTML = `
<div class="tem-box">
<span class="temp">
<span class="temperature">${temperature}</span>
<span>°</span>
</span>
</div>
<div class="climate-box">
<div class="air">
<span class="psPm25">${psPm25}</span>
<span class="psPm25Level">${psPm25Level}</span>
</div>
<ul class="weather-list">
<li>
<img src="./imgs/小雨-line.png" class="weatherImg" alt="">
<span class="weather">${weather}</span>
</li>
<li class="windDirection">${windDirection}</li>
<li class="windPower">${windPower}</li>
</ul>
</div>`

document.querySelector(".today-weather").innerHTML = `
<div class="range-box">
<span>今天:</span>
<span class="range">
<span class="weather">${weather}</span>
<span class="temNight">${temNight}</span>
<span>-</span>
<span class="temDay">${temDay}</span>
<span>℃</span>
</span>
</div>
<ul class="sun-list">
<li>
<span>紫外线</span>
<span class="ultraviolet">${ultraviolet}</span>
</li>
<li>
<span>湿度</span>
<span class="humidity">${humidity}</span>%
</li>
<li>
<span>日出</span>
<span class="sunriseTime">${sunriseTime}</span>
</li>
<li>
<span>日落</span>
<span class="sunsetTime">${sunsetTime}</span>
</li>
</ul>`

document.querySelector(".week-wrap").innerHTML = result.data.dayForecast.map(item => `<li class="item">
<div class="date-box">
<span class="dateFormat">${item.dateFormat}</span>
<span class="date">${item.date}</span>
</div>
<img src="./imgs/多云.png" alt="" class="weatherImg">
<span class="weather">${item.weather}</span>
<div class="temp">
<span class="temNight">${item.temNight}</span>-
<span class="temDay">${item.temDay}</span>
<span>℃</span>
</div>
<div class="wind">
<span class="windDirection">${item.windDirection}</span>
<span class="windPower">&lt;${item.windPower}</span>
</div>
</li>`).join("")


}).catch(error=>{

})
}

天气案例-用户输入检测并查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 节流函数
function throttle(fn, time){
let timer = null
return function(){
// 如果不存在计时器的话则创建,否则不进行任何操作
if(!timer){
timer = setTimeout(function(){
fn()
timer = null
}, time)
}
}
}

// 检测输入,并展示
function checkInput(){
const city = document.querySelector(".search-city").value
if(city){
myAxios({
url : "http://hmajax.itheima.net/api/weather/city",
params:{
city
}
}).then(result=>{
console.log(result.data)
document.querySelector(".search-list").innerHTML = result.data.map(item => `<li class="city-item" data-code="${item.code}">${item.name}</li>`).join("")
}).catch(error=>{
console.log(error)
})
}

}
document.querySelector(".search-city").addEventListener("input", throttle(checkInput, 1000))

// 用户点击并查询
document.querySelector(".search-list").addEventListener("click", e => {
console.log(e.target.innerText)
if(e.target.tagName === "LI"){

document.querySelector(".search-city").value = e.target.innerText
getWeather(e.target.dataset.code)
}

})

同步代码异步代码

同步代码:逐行执行需原地等待结果后,才继续向下执行

异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待)在将来完成后触发一个回调函数

回调函数地狱

在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱

缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身,内部axios出错外部无法捕获

1
2
3
4
5
6
7
axios({url:"xxx"}).then(result=>{
axios({url:"xxx"}).then(result=>{
axios({url:"xxx"}).then(result=>{

})
})
})

Promise-链式调用

promise链式调用能够有效的解决回调函数地狱

promise函数的then函数会返回一个promise函数对象,通过返回的promise对象来进行下一个异步函数的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 创建promise对象
const p = new Promise((resolve, reject) =>{
// 使用延迟函数来模拟xhr访问资源
setTimeout(()=>{
resolve("北京")
}, 2000)
})

// 2. 通过上一级的promise对象获取的结果来进行下一次请求
const p2 = p.then(result=>{
return new Promise((resolve, reject) =>{
setTimeout(()=>{
resolve(result+"北京市")
}, 2000)
})

})

image-20240204090438032

通过promise对象的返回值,将axios函数嵌套,转变为线性关系

1
2
3
4
5
6
7
8
axios({url:"xxx"}).then(result=>{
return axios({url:"xxx"})

}).then(result=>{ // 第二个回调函数的then
return axios({url:"xxx"})
}).then(result=>{ // 第三个回调函数的then

})

async函数和await

async函数是使用async关键字声明的函数,其中允许await关键字的使用,async和await关键字可以让我们使用更为简介的方式写出promise中的异步

基本用法

1
2
3
4
5
6
7
8
9
10
// 在函数前面加上async函数可以让该函数变为异步函数
async function getDefaultArea(){
// 使用await可以让异步函数,不在跳过,而是等待异步函数的完成之后,在进行下一行代码的执行
const pObj = await axios({url:"xxx"})
const pname = pObj.data.list[0]
const cObj = await axios({url:"xxx"})
const cname = cObj.data.list[0]
const aObj = await axios({url:"xxx"})
const aname = aObj.data.list[0]
}

捕获错误

try…catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 在函数前面加上async函数可以让该函数变为异步函数
async function getDefaultArea(){
// 使用await可以让异步函数,不在跳过,而是等待异步函数的完成之后,在进行下一行代码的执行
try{
const pObj = await axios({url:"xxx"})
const pname = pObj.data.list[0]
const cObj = await axios({url:"xxx"})
const cname = cObj.data.list[0]
const aObj = await axios({url:"xxx"})
const aname = aObj.data.list[0]
} catch(error){
console.log(error)
}
}

事件循环

原因:JS是单线程的,为了让耗时代码不阻塞其他代码运行,设计了事件循环

什么是事件循环

执行代码和收集异步任务,在调用栈空闲时,返回调用任务队列里面的回调函数机制

JS执行过程

先执行同步代码,遇到异步代码交给宿主浏览器环境执行

异步有结果之后,将代码交给任务队列

当调用栈空闲时,反复调用任务队列中的回调函数

宏任务微任务

宏任务:由浏览器环境执行的异步代码

  1. JS脚本执行事件
  2. setTimeout/setInterval
  3. AJAX请求完成事件
  4. 用户交互事件

微任务:由JS引擎环境执行的异步代码

Promise对象.then()

Promise对象本身是同步的,但是then和catch回调函数是异步的

调用栈,先执行微任务队列,在执行宏任务队列

promise.All静态方法

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败)做后续逻辑

image-20240204112505378

使用方法

1
2
3
4
5
6
const p = Promise.all([promise对象, promise对象])
p.then(result=>{
// 所有promise都成功才到这一步
}).catch(error =>{
// 有一个promise对象出错就到这
})
1
2
3
4
5
6
7
8
9
10
const arrCode = ["110100", "310520", "640213"]
const arrPromise = arrCode.map(item => {
return axios({url:"http://hmajax.itheima.net/api/weather", params:{city:item})
})
const p = Promise.all(arrPromise)
p.then(result=>{

}).catch(error =>{

})

商品分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 获取顶部分类
axios({
url: "http://hmajax.itheima.net/api/category/top"
}).then(result=>{
// 获取多个Promise对象
const arrSubGoods = result.data.data.map(item => axios({url:"http://hmajax.itheima.net/api/category/sub", params:{id:item.id}}))

// 进行多个对象的请求,并获取返回值
const p = Promise.all(arrSubGoods)

//根据返回值来进行字符串填充
p.then(result=>{
const strResult = result.map(item => {
return `
<div class="item">
<h3>${item.data.data.name}</h3>
<ul>
<!-- 可以再模板字符串中在进行map遍历 -->
${item.data.data.children.map(sub => `<li>
<a href="javascript:;">
<img src="${sub.picture}" />
<p>${sub.name}</p>
</a>
</li>`).join("")}
</ul>
</div>`
}).join("")
document.querySelector(".sub-list").innerHTML = strResult
})

})

学习反馈项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 获取省份列表
function getProvince(){
axios({
url:"http://hmajax.itheima.net/api/province"
}).then(result=>{
document.querySelector(".province").innerHTML = "<option value=''>省份</option>" + result.data.list.map(item => `<option value="${item}">${item}</option>`).join("")
document.querySelector(".city").innerHTML = "<option value=''>城市</option>"
document.querySelector(".area").innerHTML = "<option value=''>地区</option>"
})
}

// 获取对应省份城市列表
function getCity (province){
axios({
url:"http://hmajax.itheima.net/api/city",
params:{
pname:province
}
}).then(result=>{
document.querySelector(".city").innerHTML = "<option value=''>城市</option>" + result.data.list.map(item => `<option value="${item}">${item}</option>`).join("")
document.querySelector(".area").innerHTML = "<option value=''>地区</option>"
})
}

// 获取对应城市地区列表
function getArea (province, city){
axios({
url:"http://hmajax.itheima.net/api/area",
params:{
pname:province,
cname:city
}
}).then(result=>{
document.querySelector(".area").innerHTML = "<option value=''>地区</option>" + result.data.list.map(item => `<option value="${item}">${item}</option>`).join("")
})
}

getProvince()

document.querySelector(".province").addEventListener("change", (e)=>{
getCity(e.target.value)
})
document.querySelector(".city").addEventListener("change", e=>{
getArea(document.querySelector(".province").value, e.target.value)
})

document.querySelector(".submit").addEventListener("click", e=>{
const form = document.querySelector(".info-form")
const data = serialize(form, { hash: true, empty: true })
axios({
url:"http://hmajax.itheima.net/api/feedback",
method:"post",
data:{
...data
}
}).then(result=>{
alert("提交成功")
}).catch(error=>{
alert(error.response.data.message)
})
})

黑马头条

  1. 基于Bootstrap 搭建网站标签和样式
  2. 集成 wangEditor 插件实现富文本编辑器
  3. 使用原生jS 完成增删改查等业务
  4. 基于axios与黑马头条线上接口交互
  5. 使用axios 拦截器进行权限判断

验证码登陆

  1. 点击发送验证码
  2. 给后端发送请求,并携带手机号,调用后端验证码的接口
  3. 后端为手机号生成验证码,并记录在服务器
  4. 后端携带手机号给运营商发送请求
  5. 通过基站给指定的手机发送验证码短信
  6. 基站返回给服务器发送成功信息
  7. 服务器给前端发送,验证码发送成功请求
  8. 根据手机收到的验证码,填入到页面中

image-20240205101822503

token

概念:访问权限的令牌,本质上是一串字符串

创建:正确登录后,由后端签发并返回

作用:判断是否有登录状态,控制访问权限

注意:前端只能判断token的有无,后端才能判断token的有效性

image-20240205123857902

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// page/index.js
document.querySelector(".btn").addEventListener("click", ()=>{
const form = document.querySelector(".login-form")
const params = serialize(form, {hash:true, empty:true})
axios({
url:"/v1_0/authorizations",
method:"post",
data:params
}).then(result=>{
myAlert(true, "登陆成功")
// 登陆成功保存token 后续前端可根据token来访问对应页面
localStorage.setItem("token", result.data.data.token)
// 登陆成功之后1.5秒后跳转到对应页面
setTimeout(() => {
location.href = "../content/index.html"
}, 1500);

}).catch(error=>{
myAlert(false, error.response.data.message)
})
})

// utils.auth.js
const token = localStorage.getItem("token")
if(!token){
location.href = "../login/index.html"
}

个人信息设置和axios拦截器

需求:根据登录信息设置个人页面展示

语法:axios可以再headers选项中设置请求头参数

1
2
3
4
5
6
axios({
url:"目标资源地址网址",
headers:{
Authorization: `Bearer ${localStorage.getItem("token")}`
}
})

问题:每一次axios请求都需要携带token

解决:在请求拦截器统一设置公共headers选项

**axios请求拦截器:**发起请求之前,触发配置函数,对请求参数进行额外配置

1
2
3
4
5
6
7
8
9
axios.interceptors.request.use(function(config){
const token = location.getItem("token")
token && (config.headers.Authorization = `Bearer ${token}`)
// 发送请求之前需要做的事情
return config
}, function(error){
// 出错之后需要做的事情
return Promise.reject(error)
})

axios相应拦截器:axios在相应回到then和catch之前触发的拦截函数,对相应结果统一处理

1
2
3
4
5
6
7
8
9
10
11
12
axios.interceptors.response.use(function(response){
// 对响应做出的结果
return response
}, function(error){
// 出错之后需要做的事情
if(error?.response?.status === 401){
alert("登录状态过期,请从新登录")
localStorage.clear()
location.href = "../login/index.html"
}
return Promise.reject(error)
})

优化axios响应结果:让相应结果直接是我们想要的数据,而不包括其他杂七杂八的东西

还是使用axios相应拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
axios.interceptors.response.use(function(response){
// 对响应做出的结果,
// 将result处理成我们想要的数据
const result = response.data
return result
}, function(error){
// 出错之后需要做的事情
if(error?.response?.status === 401){
alert("登录状态过期,请从新登录")
localStorage.clear()
location.href = "../login/index.html"
}
return Promise.reject(error)
})

富文本编辑器

富文本:带样式,多格式的文本编辑器

使用插件:wangEditor插件

步骤:参考wangEditor插件

  1. 引入Css定义样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <link href="https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet">
    <style>
    #editor—wrapper {
    border: 1px solid #ccc;
    z-index: 100; /* 按需定义 */
    }
    #toolbar-container { border-bottom: 1px solid #ccc; }
    #editor-container { height: 500px; }
    </style>
  2. 定义HTML结构,控制文本编辑器的位置

    1
    2
    3
    4
    <div id="editor—wrapper">
    <div id="toolbar-container"><!-- 工具栏 --></div>
    <div id="editor-container"><!-- 编辑器 --></div>
    </div>
  3. 引入JS创建编辑器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>
    <script>
    const { createEditor, createToolbar } = window.wangEditor

    const editorConfig = {
    placeholder: 'Type here...',
    onChange(editor) {
    const html = editor.getHtml()
    console.log('editor content', html)
    // 也可以同步到 <textarea>
    }
    }

    const editor = createEditor({
    selector: '#editor-container',
    html: '<p><br></p>',
    config: editorConfig,
    mode: 'default', // or 'simple'
    })

    const toolbarConfig = {}

    const toolbar = createToolbar({
    editor,
    selector: '#toolbar-container',
    config: toolbarConfig,
    mode: 'default', // or 'simple'
    })
    </script>
  4. 监听内容改变,保存在隐藏文本域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 编辑器配置对象
    const editorConfig = {
    // 占位提示文字
    placeholder: 'Type here...',
    // 编辑器变化是回调函数
    onChange(editor) {
    // 获取富文本内容
    const html = editor.getHtml()
    // 也可以同步到 <textarea>
    document.querySelector(".publish-content").value = html
    }
    }

发布文章页面

频道列表

1
2
3
4
5
6
7
async function setChannleList() {
const res = await axios({
url:"/v1_0/channels"
})
document.querySelector(".form-select").innerHTML = '<option value="" selected>请选择文章频道</option>' + res.data.channels.map(item => `<option value="${item.id}">${item.name}</option>`).join("")
}
setChannleList()

文章封面

label 中的for属性可以绑定对应的input标签,通过ID值绑定

1
2
3
4
5
6
<div class="cover">
<label for="img">封面:</label>
<label for="img" class="place">+</label>
<input class="img-file" type="file" name="img" id="img" hidden>
<img class="rounded">
</div>
  1. 准备标签结构和样式
  2. 选择文件并保存在FromData
  3. 单独上传图片并得到图片URL地址
  4. 回显并切换img标签显示(隐藏+号,上传图片)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
document.querySelector(".img-file").addEventListener("change", async e=>{
// 获取图像对象
const file = e.target.files[0]
const fd = new FormData()
fd.append("image", file)

const res = await axios({
url:"/v1_0/upload",
method:"post",
data:fd
})
// 文章回显
document.querySelector(".rounded").src = res.data.url

document.querySelector(".rounded").classList.add("show")
document.querySelector(".place").classList.add("hide")
})

// 点击图片同样也可以触发选择图片选项
document.querySelector(".rounded").addEventListener("click", ()=>{
document.querySelector(".img-file").click()
})

收集并保存

收集文章内容并提交保存

  1. 使用form-serialize插件收集表单数据对象
  2. 基于axios提交到服务器保存
  3. 调用Alert警告框反馈结果给用户
  4. 重置表单并跳转到列表页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
document.querySelector(".send").addEventListener("click", async ()=>{
// 获取表单数据
const form = document.querySelector(".art-form")
const data = serialize(form, {hash: true, empty:true})

// 发布文章的时候不需要id所以可以进行删除
delete data.id


// 添加图片地址
data.cover = {
type: 1, // type默认为1
images: [document.querySelector(".rounded").src]
}

// 获取提示框
const alert = document.querySelector(".info-box")
console.log(data)

try {
// 发送请求
const res = await axios({
url:"/v1_0/mp/articles",
method:"post",
data
})
// 使用Alert提示框
myAlert(true, "发布成功")
// 重置表单,并跳转
form.reset()

// 图片表单需要手动恢复
document.querySelector(".rounded").src = ""
document.querySelector(".rounded").classList.remove("show")
document.querySelector(".place").classList.remove("hide")
// 富文本编辑器要重置
editor.setHtml("")

// 等待1.5s之后在跳转
setTimeout(()=>{
location.href = "../content/index.html"
}, 1500)
} catch (error) {
// 使用Alert提示框
myAlert(false, error.response.data.message)
}

})

内容管理

文章列表显示

  1. 准备查询参数对象

  2. 获取文章列表数据

  3. 展示到指定的标签结构中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    const queryObj = {
    status: "", // 1-待审核 2-审核通过
    channel_id: "", // 文章频道id, 空字符串-全部
    page: 1, // 当前页码
    per_page: 2, // 每一页的条数
    }

    async function setArticlesList(){
    // 获取文章对象
    const res = await axios({
    url:"/v1_0/mp/articles",
    params:queryObj
    })

    console.log(res)
    // 填充字符
    document.querySelector(".art-list").innerHTML = res.data.results.map(item => `<tr>
    <td>
    <img
    src="${item.cover.type === 0 ? `https://img2.baidu.com/it/u=2640406343,1419332367&fm=253&fmt=auto&app=138&f=JPEG?w=708&h=500"` : item.cover.images[0]}"
    alt="">
    </td>
    <td>${item.title}</td>
    <td>
    ${item.status===1 ? 'span class="badge text-bg-primary">待审核</span>' : '<span class="badge text-bg-primary">待审核</span>'}
    </td>
    <td>
    <span>${item.pubdate}</span>
    </td>
    <td>
    <span>${item.read_count}</span>
    </td>
    <td>
    <span>${item.comment_count}</span>
    </td>
    <td>
    <span>${item.like_count}</span>
    </td>
    <td>
    <i class="bi bi-pencil-square edit"></i>
    <i class="bi bi-trash3 del"></i>
    </td>
    </tr>`).join("")
    }

筛选功能

  1. 设置频道列表数据
  2. 监听筛选条件改变,保存查询信息到查询参数对象
  3. 点击筛选时,传递查询参数对象到服务器
  4. 获取匹配数据,覆盖到页面展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 设置频道列表
async function setChannleList() {
const res = await axios({
url:"/v1_0/channels"
})
document.querySelector(".form-select").innerHTML = '<option value="" selected>请选择文章频道</option>' + res.data.channels.map(item => `<option value="${item.id}">${item.name}</option>`).join("")
}
setChannleList()

// 绑定点击事件
document.querySelector(".sel-btn").addEventListener("click", ()=>{
const form = document.querySelector(".sel-form")
const data = serialize(form, {hash:true, empty:true})
queryObj.channel_id = data.channel_id
queryObj.status = data.status
setArticlesList()
})

翻页功能

  1. 保存并设置文章总条数
  2. 点击下一页,做临界值判断,并切换页码参数并请求最新数据
  3. 点击上一页,做临界值判断,并切换页码参数并请求最新数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
document.querySelector(".last").addEventListener("click", function(){
if(queryObj.page > 1){
queryObj.page = queryObj.page - 1
setArticlesList()
}
})

document.querySelector(".next").addEventListener("click", function(){
// 临界值判断 上取整
if(queryObj.page < Math.ceil(totalCount / queryObj.per_page)){
queryObj.page = queryObj.page+1
setArticlesList()
}
})

删除功能

  1. 关联文章 id 到删除图标
  2. 点击删除时,获取文章 id
  3. 调用删除接口,传递文章 id 到服务器
  4. 重新获取文章列表,并覆盖展示
  5. 删除最后一页的最后一条,需要自动向前翻页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
document.querySelector(".art-list").addEventListener("click", async e=>{
if(e.target.classList.contains("del")){
const res = await axios({
url:`/v1_0/mp/articles/${e.target.parentNode.dataset.id}`,
method:"DELETE"
})

// 当删除最后一页的元素的时候,需要让page自动减一
if(document.querySelector(".art-list").childNodes.length === 1 && queryObj.page !== 1){
queryObj.page--
}

setArticlesList()
}
})

编辑回显

  1. 页面跳转传参(URL 查询参数方式)
  2. 发布文章页面接收参数判断(共用同一套表单)
  3. 修改标题和按钮文字
  4. 获取文章详情数据并回显表单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 编辑文章
document.querySelector(".art-list").addEventListener("click", async e=>{
if(e.target.classList.contains("edit")){
// 跳转到编辑页面 并带有对应文章ID
location.href = `../publish/index.html?id=${e.target.parentNode.dataset.id}`

}
})

;(function(){
// 获取网址中的参数部分
const paramsStr = location.search
const params = new URLSearchParams(paramsStr)
params.forEach(async (value, key)=>{
console.log(key, value)
// 如果关键字是id的话则进行文章回显
if(key === "id"){
// 先将页面中的发布文章修改为编辑文章
document.querySelector(".title span").innerHTML = "编辑文章"
document.querySelector(".send").innerHTML = "修改"

// 进行文章回显
// 先请求数据
const res = await axios({
url:`/v1_0/mp/articles/${value}`
})

// 获取需要的文章数据,不需要的不获取
const dataObj= {
title:res.data.title,
rounded:res.data.cover.images[0],
channels_id:res.data.channels_id,
content:res.data.content,
id:res.data.id
}

Object.keys(dataObj).forEach(key=>{
// 回显文章有封面
if(key === "rounded"){
if(dataObj[key]){
document.querySelector(".rounded").src = dataObj[key]
document.querySelector(".rounded").classList.add("show")
document.querySelector(".place").classList.add("hide")
}
} else if( key === 'content'){ // 回显文章内容
editor.setHtml(dataObj[key])
} else{
// 通过标签name属性选择器来找到匹配的标签
document.querySelector(`[name=${key}]`).value = dataObj[key]
}

})
}
})
})();

编辑提交

  1. 判断按钮文字,区分业务(因为共用一套表单)
  2. 调用编辑文章接口,保存信息到服务器
  3. 基于 Alert 反馈结果消息给用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
document.querySelector(".send").addEventListener("click", async (e)=>{
// 如果不是修改的话下面代码不用执行
if(e.target.innerHTML !== "修改") return

// 获取表单数据
const form = document.querySelector(".art-form")
const data = serialize(form, {hash: true, empty:true})


try {
// 发送请求
const res = await axios({
url:`/v1_0/mp/articles/${data.id}`,
method:"PUT",
data:{
...data,
cover:{
type: document.querySelector(".rounded").src ? 1 : 0,
images:[document.querySelector(".rounded").src]
}
}
})
myAlert(true, "修改成功")
// 等待1.5s之后在跳转
setTimeout(()=>{
location.href = "../content/index.html"
}, 1500)
} catch (error) {
console.log(error)
myAlert(false, error.response.data.message)
}

})

退出功能

  1. 绑定点击事件
  2. 清空本地缓存,跳转到登录页面
1
2
3
4
document.querySelector(".quit").addEventListener("click", ()=>{
localStorage.clear()
location.href="../login/index.html"
})

Node.js

Node.js是什么:基于Chrome的v8引擎封装,独立执行JavaScript代码环境

Node.js与浏览器环境的JS最大区别:Node.js中没有BOM和DOM

Node.js有什用:编写后端程序,提供网页资源,前端工程化:集成开发中使用的工具和技术

Node.js如何执行代码:node xxx.js

fs模块-读写文件

  1. 加载fs模块

    1
    const fs = require("fs")
  2. 写入文件内容

    1
    2
    3
    fs.writeFile('文件路径', '写入内容', err=>{

    })
  3. 读取文件内容,读出来的是Buffer数据流,需要使用toString()转换成字符串

    1
    2
    3
    fs.readFile("文件路径", (err, data)=>{
    // data是文件内容的Buffer数据流
    })

path模块-路径处理

建议:在Node.js代码中,使用绝对路径

补充:(两个下划线)__dirname内置变量(获取当前模块目录-绝对路径)

windows和mac的路径分隔符是反斜杠与斜杠

path.join() 会使用特定于平台的分隔符作为定界符,将所有给定的路径片段连接在一起

语法

  1. 加载path模块

    1
    const path = require("path")
  2. 使用path.join方法拼接路径

    1
    2
    3
    4
    fs.readFile(path.join(__dirname, "../test.txt"), (err, data)=>{
    if(err) console.log(err)
    else console.log(data.toString())
    })

压缩前端

将回车\r和换行符\n去掉,写入新的html文件

步骤:

  1. 读取源HTML中的内容
  2. 正则替换字符串
  3. 写入到新的html文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.1 读取源 html 文件内容
// 加载读文件模块
const fs = require('fs')
// 加载路径模块
const path = require('path')
// 读取文件, fs.readFile 第一个参数为路径参数,第二个参数为回调函数
fs.readFile(path.join(__dirname, 'public/index.html'), (err, data) => {
if (err) console.log(err)
else {
// 获取对应文件的字符串
const htmlStr = data.toString()
// 进行正则化替换
const resultStr = htmlStr.replace(/[\r\n]/, "")
// 写文件 fs.writeFile 有三个参数,第一个是路径参数,第二个是需要写入的文件,第三个是回调函数
fs.writeFile(path.join(__dirname, "dist/index.html"), resultStr, err=>{
if(err) console.log(err)
else console.log('写入成功')
})
}
})

HTTP模块-创建web服务

URL端口号

URL:统一资源定位符,用于访问服务器里面的资源

端口号:标记服务器里不同的服务程序

端口号范围:0-65535之间的任意整数

HTTP端口默认是80

web服务程序:用于提供网上信息浏览的功能,0-1023和一些特定的端口号被占用,编写程序的时候注意绕开

image-20240215090903225

Node.js创建Web服务

创建web服务并相应

  1. 加载Http模块,创建web服务对象

    1
    2
    const http = require('http')
    const server = http.createServer()
  2. 监听request请求事件,设置请求头和相应体

    1
    2
    3
    4
    5
    6
    server.on('request', (req, res) => {
    //设置相应头:内容类型, 普通文本,编码格式为UTF-8
    res.setHeader("Content-Type", "text/html;charset=utf-8")
    // 因为后端返回中含有中文字符串,为了避免乱码,所以需要设置请求头来防止前台乱码
    res.end("欢迎使用Node.js")
    })
  3. 配置端口号并启动Web服务

    1
    2
    3
    server.listen(3000, () => {
    console.log("Web 服务已启动")
    })
  4. 浏览器请求http://localhost:3000 测试

模块化标准

概念:项目是由多个模块文件组成

好处:提高了代码的复用性,按需加载,独立作用域

使用:需要标准语法导出和导入

CommonJS标准

需求:定义utils.js模块,封装基地址和求数组总和的函数

使用:

  1. 导出:module.exports = {}
  2. 导入:require(“模块名或路径”) 内置模块直接写模块名,自定义模块需要写路径

utils.js:

1
2
3
4
5
6
7
8
9
10
11
12
const baseURL = "http://localhost:8080"
const getArrSum = function (arr){
return arr.reduce(function(prev, current){
return prev + current
}, 0)
}

// 使用module导出
module.exports = {
url:baseURL,
arraySum: getArrSum
}

index.js

1
2
3
// 导入
const obj = require("./utils.js")
console.log(obj)

ECMAScript标准

默认导出和导入

方式:

  1. 导出:export default{}

    1
    2
    3
    4
    export default{
    对外属性名:baseURL,
    对外属性名:getArraySum
    }
  2. 导入:import 变量名 from ‘模块名或路径’

    1
    import obj from '模块名或路径'

**注意:**Node.js默认支持CommonJS标准语法

如果需要使用ECMAScript标准语法,在运行模块所在文件夹新建package.json文件,并设置{“type”:“module”}

utils.js:

1
2
3
4
5
6
7
8
9
10
11
12
const baseURL = "http://localhost:8080"
const getArrSum = function (arr){
return arr.reduce(function(prev, current){
return prev + current
}, 0)
}

// 使用默认导出
export default = {
url:baseURL,
arraySum: getArrSum
}

index.js

1
2
3
4
// 导入
import obj from "./utils.js"
console.log(obj.url)
console.log(obj.getArrSum([1,2,3]))

命名导出和导入

方式:

  1. 导出:export修饰定义语句

    1
    2
    export const baseURL = 'http://localhost:8080'
    export const getArraySum = arr => arr.reduce((sum, val) => sum+val, 0)
  2. 导入:import {同名变量} from ‘模块名或路径’

    1
    import {baseURL, getArraySum} from '模块名或路径'

概念:将模块,代码,其他资料聚合成一个文件夹

分类

项目包:主要用于编写项目和业务逻辑

软件包:封装工具和方法进行使用

要求:根目录中,必须要有package.json 文件

1
2
3
4
5
6
7
8
{
"name":"utils", // 软件包的名称
"version":"1.0.0", // 软件包的版本
"description":"对包的描述",
"main":"index.js", // 软件包的入口点
"author":"Nuyoah", // 软件包的作者
"license":"MIT" // 软件包的许可证
}

**注意:**导入软件包时,引入的默认是index.js模块文件/main属性指定的模块文件

image-20240215123527480

arr.js

1
2
3
4
5
const getArraySum = arr => arr.reduce((sum, val) => sum += val, 0)

module.exports = {
getArraySum
}

str.js

1
2
3
4
5
6
7
8
9
10
11
12
const checkUserName = username => {
return username.length >= 8
}

const checkPassword = password => {
return password.length >= 8
}

module.exports = {
checkUser:checkUserName,
checkPwd:checkPassword
}

index.js

1
2
3
4
5
6
7
8
9
10
11
// 导入其余的包文件
const { getArraySum } = require("./libs/arr.js")
const { checkUser, checkPwd } = require("./libs/str.js")


// 导出
module.exports = {
getArraySum,
checkPwd,
checkUser
}

package.json

1
2
3
4
5
6
7
8
{
"name":"utils",
"version":"1.0.0",
"description":"工具包",
"main":"index.js",
"author":"Nuyoah",
"license":"MIT"
}

npm包管理器

使用方法

  1. 初始化清单文件 npm init -y
  2. 下载软件包 npm i 软件包名称
  3. 使用软件包

npm-安装所有依赖

当项目中缺少node_modules的时候,项目无法正确运行,所以需要我们手动下载

npm i 下载 package.json中记录的所有软件包

npm-全局软件包 nodemon

软件包的区别:

  1. 本地软件包:当前项目使用,封装属性和方法,存在于node_modules
  2. 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置
1
nodemon server.js

使用nodemon运行的server.js项目并不会关闭,当server.js中的内容修改后,nodemon会自动重启并跟新对应内容

Webpack

静态模块打包工具

静态模块:指的是编写代码过程中的,html,css,js,图片等固定内容的文件

打包:把静态模块内容,压缩,整合,转译等(前端工程化)

步骤

image-20240215200146572

  1. 新建并初始化项目,编写业务源代码

    初始化文件夹,并使用npm init -y 初始化package.json文件

    建立src文件夹,并创建utils/check.js文件和index.js文件

  2. 下载webpack webpack-cli到当前项目中,并配置局部自定义命令

    1
    npm i webpack webpack-cli --save-dev
    1
    2
    3
    "scripts" : {
    "build" : "webpack"
    }
  3. 运行打包命令,自动产生dis分发文件夹

    1
    npm run build

修改Webpack打包入口和出口

步骤:

  1. 在项目根目录创建webpack.config.js配置文件

  2. 导出配置对象,配置入口,出口文件的路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const path = require("path")

    module.exports = {
    // 配置入口路径
    entry:path.resolve(__dirname, "src/login/index.js"),
    // 配置出口路径
    output : {
    // 路径
    path:path.resolve(__dirname, 'dist'),
    // 文件名
    filename:"./login/index.js",
    // 生成新的打包文件之后,删除上一个打包文件
    clean:true
    }
    }

webpack自动生成html文件

使用插件html-webpack-plugin,在webpack打包文件的时候自动生成html文件

插件网址

安装

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

步骤

  1. 下载html-webpack-plugin本地软件包

    1
    npm install --save-dev html-webpack-plugin
  2. 配置webpack.config.js让webpack拥有插件国能

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

    module.exports = {
    entry: 'index.js',
    output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js',
    },
    plugins: [
    new HtmlWebpackPlugin({
    template:'./public/login.html', // 模板文件,我们准备的文件
    filename:'./login/index.html' // 输出文件
    })
    ],
    };

webpack打包css代码

将css代码打包到js中

引入文件css-loader:解析css代码

下载

1
npm install --save-dev css-loader

引入文件 style-loader:把解析后的css代码插入到DOM中

下载

1
npm install --save-dev style-loader

步骤

  1. 准备css文件代码到src/login/index.js中

    在index.js中使用

    1
    import css from "file.css"; // 来引入对应的css
  2. 下载css-loader 和 style-loader本地软件包

  3. 配置webpack.config.js让webpack拥有该加载器功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    module.exports = {
    module: {
    rules: [
    {
    test: /\.css$/i,
    // 位置不能错,从后往前加载
    use: ["style-loader", "css-loader"],
    },
    ],
    },
    };
  4. webpack打包

webpack提取css代码

将css代码单独提取出来,方便加载

插件:mini-css-extract-plugin提取css代码

下载:

1
npm install --save-dev mini-css-extract-plugin

步骤

  1. 现在mini-css-extract-plugin本地软件包

  2. 配置webpack.config.js让Webpack拥有该插件功能

    需要创建声明变量MiniCssExtractPlugin

    并在plugins中new MiniCssExtractPlugin

    在module中使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");

    module.exports = {
    plugins: [new MiniCssExtractPlugin()],
    module: {
    rules: [
    {
    test: /\.css$/i,
    use: [MiniCssExtractPlugin.loader, "css-loader"],
    },
    ],
    },
    };
  3. 打包

注意:不能和style-loader一起使用

webpack压缩css

使用css-minimizer-webpack-plugin来压缩css

下载方式

1
npm install css-minimizer-webpack-plugin --save-dev

配置文件webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
module: {
rules: [
{
test: /.s?css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
};

webpack打包less代码

加载器less-loader:把less代码编译为css代码

安装:

1
npm install less less-loader --save-dev

配置,在webpack.config.js中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
module: {
rules: [
{
test: /\.less$/i,
use: [
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader',
],
},
],
},
};

webpack打包图片

资源模块:Webpack5内置资源模块(字体图片)打包,无需额外的loader

步骤:

  1. 配置webpack.config.js让Webpack拥有打包图片的功能

    占位符【hash】对模块内容做算法计算,得到映射的数字字母组合的字符串

    占位符【ext】使用当前模块原本的占位符,例如:.png / .jpg等字符串

    占位符【query】保留引入文件时代码中的查询参数(只有URL下生效)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(png|jpg|jpeg|gif)$/i,
    type: 'asset',
    generator: {
    filename: 'assets/[hash][ext][query]'
    }
    },
    ],
    },
    };

用户登录功能

步骤

  1. 现在axios包

    1
    npm i axios
  2. 准备修改utils工具包源代码导出实现函数

    request.js导入axios,外加导出方便外部使用

    1
    2
    3
    import axios from 'axios'

    export default axios

    alert.js 通过export导出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 通过export导出方便外部使用
    export function myAlert(isSuccess, msg) {
    const myAlert = document.querySelector('.alert')
    myAlert.classList.add(isSuccess ? 'alert-success' : 'alert-danger')
    myAlert.innerHTML = msg
    myAlert.classList.add('show')

    setTimeout(() => {
    myAlert.classList.remove(isSuccess ? 'alert-success' : 'alert-danger')
    myAlert.innerHTML = ''
    myAlert.classList.remove('show')
    }, 2000)
    }
  3. 导入并边界写逻辑代码,打包后运行观察效果

搭建开发环境

开发环境官方文档

注意:只适合开发环境使用

问题:之前改代码,需重新打包才能运行查看,效率很低

开发环境:配置webpack-dev-server 快速开发应用程序

作用:启动 Web 服务,自动检测代码变化,热更新到网页

步骤

  1. 下载webpack-dev-server软件包到当前项目

    1
    npm install --save-dev webpack-dev-server
  2. 设置模式为开发者模式,并配置自定义命令

    webpack.config.js

    1
    2
    3
    module.exports = {
    mode:'development'
    }

    package.json

    1
    2
    3
    "scripts": {
    "dev":"webpack serve --open"
    },
  3. 使用npm run dev来启动开发者服务器

注意:

  1. webpack-dev-server 借助 http 模块创建 88 默认 Web 服务
  2. 默认以 public 文件夹作为服务器根目录
  3. webpack-dev-server 根据配置,打包相关代码在内存当中,作为服务器根目录,以output.path的值作为服务器的根目录,可以直接访问dis目录下的内容

打包模式

模式区别

告诉webpack使用相应模式的内置优化

模式名称 模式名字 特点 场景
开发模式 development 调试代码,实时加载,模块热替换 本地开发
生产模式 production 压缩代码,资源优化,更轻量 打包上线

设置

  1. 在 webpack.config.js 配置文件设置 mode 选项

    1
    2
    3
    module.exports = {
    mode:'production'
    }
  2. 在 packagejson 命令行设置 mode 参数

    1
    2
    3
    "scripts": {
    "dev":"webpack serve --mode=dev"
    },

命令行配置优先级高于配置文件中的,推荐使用命令行

模式应用

需求:在开发模式下用 style-loader 内嵌更快,在生产模式下提取 css代码

方案1: webpack.config.is 配置导出函数,但是局限性大 (只接受 2种模式)

方案2:借助 cross-env (跨平台通用)包命令,设置参数区分环境

步骤:

  1. 下载cross-env软件包到当前项目

    1
    npm i corss-env --save-dev
  2. 配置自定义命令,传入参数名和值(会绑定到process.env对象下)

    cross-env 创建env对象

    NODE_ENV:参数名

    1
    2
    3
    4
    5
    "scripts":{
    "test": "echo "Error: no test specified\" && exit 1",
    "build" : "cross-env NODE_ENV=production webpack --mode=production",
    "dev" : "cross-env NODE_ENV=development webpack serve --open --mode=development"
    }

根据不同的模式来进行不同的模块

需求:在开发模式下用 style-loader 内嵌更快,在生产模式下提取 css代码

1
2
3
4
5
6
7
8
9
module: {
rules: [
{
test: /\.css$/i,
// use: ["style-loader", "css-loader"],
use: [process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader, "css-loader"]
},
],
},

注入环境变量

需求:

前端项目中,开发模式下打印语句生效,生产模式下打印语句失效

问题:cross-env 设置的只在 Nodejs 环境生效,前端代码无法访问 process.env.NODE ENV

解决:使用Webpack 内置的 DefinePlugin 插件

作用:在编译时,将前端代码中匹配的变量名,替换为值或表达式

1
2
3
4
5
6
7
8
9
const webpack = require("webpack")

module.exports = {
plugins : [
new webpack.DefinePlugin({
'process.env.NODE_ENV' : JSON.stringify(process.env.NODE_ENV)
})
]
}

在出口文件index.js文件中

1
2
3
4
if(process.env.NODE_ENV === 'production'){
console.log = function(){}
}
console.log("开发模式下使用,生产模式下失效")

开发环境调错-source map

问题:代码被压缩和混淆,无法正确定位源代码位置(行数和列数

source map:可以准确追踪 error和 warning 在原始代码的位置

设置:webpack.config.js配置devtool选项

1
2
3
model.exports = {
devtool:'inline-source-map'
}

inline-source-map选项:把源码的位置信息一起打包在js文件内

注意:source map仅适用于开发环境,不要再生产环境中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

const config = {
// 入口
entry:path.resolve(__dirname, "src/login/index.js"),
// 出口
output : {
path:path.resolve(__dirname, 'dist'),
filename:"./login/index.js",
clean:true
},
// 插件
plugins : [
new HtmlWebpackPlugin({
template:path.resolve(__dirname, "public/login.html"),
filename:path.resolve(__dirname, "dist/login/index.html")
}),
new MiniCssExtractPlugin()
]
}

// 加上判断是否是开发者模式,通过不同的模式设置不同的调试
if(process.env.NODE_ENV === 'development'){
config.devtool = 'inline-source-map'
}
module.exports = config

解析别名alias

解析别名:配置模块如何解析,创建 import 引入路径的别名,来确保模块引入变得更简单

例如:原来路径如图,比较长而且相对路径不安全

1
import {checkCode, checkPhone} from "../utils/check.js"

解决:在webpack.config.js 中配置解析别名@来代表 src绝对路径

1
2
3
4
5
6
7
const config = {
resolve : {
alias: {
'@' : path.resolve(__dirname, "src")
}
}
}
1
import {checkCode, checkPhone} from "@/utils/check.js"

优化:CDN使用

CDN定义:内容分发网络,指的是一组分布在各个地区的服务器

作用:把静态资源文件/第三方库放在CDN 网络中各个服务器中,供用户就近请求获取

好处:减轻自己服务器请求压力,就近请求物理延迟低,配套缓存策略

git

配置个人信息

命令:

  1. git -v 查看git版本

配置用户信息,每次使用git提交的时候需要说明你的身份

  1. git config --global user.name “用户名”
  2. git config --global user.email “用户邮箱”

管理个人仓库

个人仓库是一个名字为.git的文件夹

Git仓库:记录文件状态内容的地方,存储着修改的历史记录

创建

  1. 把本地文件夹转换成 Git 仓库: 命令 git init
  2. 从其他服务器上克隆 Git 仓库

git三个区域

  1. 工作区:

    实际开发时操作的文件夹, 自己创建的文件夹

  2. 暂存区

    保存之前的准备区域, .git/index文件夹

  3. 版本库

    提交并保存暂存区中的内容,产生一个版本快照 .git/objects

命令 作用
git add 文件名 暂存指定文件
git add . 暂存所有改动过的文件
git commit -m “注释说明” 提交并保存,产生版本快照
git ls-files 查找当前暂存区中的文件

通过git add 文件名和git add . 将工作区中的内容保存到暂存区中

通过git commit -m “注释说明” 将暂存区中的内容保存到版本库中

git 文件状态

两种:

未跟踪:新文件,从未被Git管理过

已跟踪:Git已经知道和管理的文件

新文件需要通过git add 文件路径,来称为已跟踪状态

文件状态 概念 场景
未跟踪(U) 从未被Git管理过 新文件
未添加(A) 第一次被Git暂存 之前版本记录无此文件
未修改(’’) 三个区域统一 提交保存后
已修改(M) 工作区内容变化 修改内容产生

使用git status -s查看文件状态

暂存区的使用

暂时存储,可以临时恢复代码内容,与版本库解耦

可以从暂存区恢复到工作区,命令git restore 目标文件

从暂存区移除文件,命令:git rm --cached 目标文件

image-20240217115417016

步骤:

先使用git ls-files 获取当前文件的目录

找到我们想要恢复的文件

使用git restore 文件路径 恢复文件

查看git提交版本:命令 git log --oneline

git 回退版本

概念:把版本库某个版本对应的内容快照,恢复到工作区/暂存区

查看提交历史: git log --oneline

1
2
3
$ git log --oneline
5feae4c (HEAD -> master) 2.新增内容页面,修改login页面的位置
487e712 生成登录页

回退命令

  1. git reset --soft 版本号 保留当前文件,恢复版本库中的文件,如版本库中只含有html文件,则使用soft恢复之后,只会修改html中的内容,不会修改别的已经生成的内容

  2. git reset --hard 版本号 覆盖当前文件

  3. git reset --mixed 版本号 保留工作区的文件

如果回退过多想要回退到上一步的,但是已经找不到原先的版本号可以使用 git reflog --oneline

image-20240217121225480

删除文件

删除某一个文件,并产生一次版本记录

步骤

  1. 手动删除工作区文件
  2. 使用git add .暂存变更
  3. 提交保存

总结:只要工作区变更,都可以暂存提交产生新纪录

忽略文件

概念:.gitignore 文件可以让git 彻底忽略跟踪指定文件

目的:让git 仓库更小更快,避免重复无意义的文件管理

在项目根目录创建 .gitignore文件

例如:

  1. 系统或软件自动生成的文件

    如node_modules

  2. 编译产生的结果文件

    如:dist

  3. 运行时生成的日志文件,缓存文件,临时文件等

    如:.vscode *.pem *.cer *.log

  4. 涉密文件,密码,秘钥等文件

1
2
3
4
5
6
7
node_modules
dist
.vscode
*.prm
*.cer
*.log
password.txt

分支

创建分支

概念:本质上是指向提交节点的可变指针,默认名字是 master

注意:HEAD 指针影响工作区/暂存区的代码状态

场景:开发新需求/修复bug,保证主线代码可以随时使用,多人协作同时开发提交效率

例如:

在现有代码上创建新分支完成内容列表业务

突然需要紧急修复 Bug- 单独创建分支解决Bug

image-20240217152524627

步骤:

  1. 创建分支命令:git branch 分支名
  2. 切换分支命令:git checkout 分支名
  3. 工作区准备代码暂存提交,重复三次
  4. git branch 可以查看当前项目有多少个分支

分支合并与删除

步骤

  1. 切回要合并的分支上:git checkout master
  2. 合并其他分支过来:git merge publish
  3. 删除合并之后的指针:git branch -d login-bug

分支合并与提交

合并提交:发生于原分支产生了新的提交记录后,再合并回去时发生,自动使用多个快照记录合并后产生一次新的提交

image-20240217152524627.png

  1. 切回要合并的分支上:git checkout master
  2. 合并其他分支过来:git merge publish
  3. 删除合并之后的指针:git branch -d login-bug

分支合并冲突

需求1:基于 master 新建 publish 分支,完成发布文章业务,然后修改内容页面的 html文件的 title 标签,并提交一次

需求2:切换到 master,也在修改内容页面的 html文件的 title 标签,并提交一次

冲突:把 publish 分支合并到 master 回来,产生合并冲突

概念:不同分支中,对同一个文件的同一部分修改,Git 无法干净的合并,产生合并冲突

解决:

  1. 打开VSCode找到冲突文件并手动解决
  2. 解决后需要提交一次记录

git远程仓库

概念:托管在因特网或其他网络中的你的项目的版本库

作用:保存版本库的历史记录,多人协作

步骤

  1. 创建远程仓库账号

  2. 新建仓库,获取远程仓库git地址

  3. 给本地Git仓库添加远程仓库原点地址

    命令:git remote add 远程仓库别名 远程仓库地址

  4. 将本地Git仓库推送版本记录到远程仓库

    命令:git push -u 远程仓库别名 本地和远程分支名

    例如:git push -u origin master

删除远程连接 git remote remove 远程仓库别名

克隆

从无到有的进程

克隆:拷贝一个Git仓库到本地,进行使用

命令:git clone 远程仓库地址

效果:在运行命令所在文件夹,生成对应的仓库名文件夹(包含版本库,并映射到暂存区和工作区)

更新

更新文件夹中的内容

命令:git pull origin(远程仓库) master(远程仓库分支)

gitee创建网页

  1. 初始化本地 Git 仓库(这次是非空文件夹-配套素材 dist 文件夹)
  2. 初始化远程 Git 仓库(这一次也是非空的)
  3. 本地配置远程仓库链接
  4. 本地拉取合并一下(确认本地要包含远程内容时使用)
  5. 本地推送到远程Git仓库
  6. 开启page网页服务得到地址浏览

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 分支名 把分支提交的历史记录合并到当前分支

远程

命令 作用 注意
git remote add 远程仓库别名 远程仓库地址 添加远程仓库地址 唯一别名,地址是以.git结尾的网址
git remote -v 查看远程仓库地址
git remote remove 远程仓库别名 删除远程仓库地址
git pull 远程仓库别名 分支名 拉取 完整写法:git pull 远程仓库别名 远程仓库分支名:本地分支名
等价于:git fetch 和 git merge
git push 远程仓库名 分支名 -u 推送 完整写法:git pull 远程仓库别名 远程仓库分支名:本地分支名 -u
建立通道后可以简写成 git push
git pull --rebase 远程仓库别名 分支名 拉取合并 合并没有关系的记录
git clone 远程仓库地址 克隆 从0到一的