危险

为之则易,不为则难

0%

04_Vue 基础

今日学习目标

✔ 能够说出 Vue 组件生命周期。

✔ 能够掌握 Axios 的使用。

✔ 能够完成购物车案例开发。

Vue 生命周期

目标

了解生命周期的概念。

人的生命周期

image-20210111193143574

组件生命周期

一个组件从创建到销毁的整个过程就是生命周期。

image-20210511152835915

小结

组件生命周期是什么?

答案
  • 从 Vue 实例创建到销毁的过程。

Vue 钩子函数

目标

掌握组件的生命周期阶段对应的钩子函数。

讲解

官网文档

作用:生命周期阶段对应的钩子函数会自动执行,我们可以在函数内部指定特定的操作。

分类:4 大阶段 8 个方法。

阶段 方法名 方法名
初始化 beforeCreate created
挂载 beforeMount mounted
更新 beforeUpdate updated
销毁 beforeDestroy destroyed

小结

Vue 组件生命周期有几个阶段,几个函数,分别是什么?

答案
  • 参考上面笔记。

Vue 初始化阶段

目标

掌握初始化阶段 2 个钩子函数的作用和执行时机。

讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>Life</div>
</template>
<script>
export default {
name: 'LifeComponent',
data() {
return {
msg: 'Hello,Vue',
}
},
beforeCreate() {
// 实例创建前,这儿还不能拿到 data 里面的数据或 methods 里面的方法
console.log(this.msg) // undefined
},
created() {
// 实例创建完毕,可以拿到 data 里面的数据和 methods 里面的方法了
// 场景:往往会在这个阶段发请求(因为发请求的操作可能需要依赖 data 里面的数据或 methods 里面的方法)
},
}
</script>

小结

Vue 初始化阶段执行了哪些钩子函数?

答案
  • beforeCreate / created。

你觉得在哪个阶段发请求比较合适,为什么?

答案
  • created

Vue 挂载阶段

目标

掌握挂载阶段 2 个钩子函数的作用和执行时机。

讲解

components/Life.vue

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
<template>
<div>
<p>学习生命周期 - 看控制台打印</p>
<p id="myP">{{ msg }}</p>
</div>
</template>

<script>
export default {
data() {
return {
msg: 'Hello World',
}
},
// 场景:可以预处理 data(对 data 的修改不会触发 updated 钩子函数)
beforeMount() {
// 预处理 data
// this.msg = '重新值'
// 获取不到 DOM
console.log(document.getElementById('myP')) // null
},
// 场景:获取渲染完毕的 DOM
mounted() {
console.log(document.getElementById('myP').innerHTML) // 'Hello World'
},
}
</script>

小结

Vue 实例从创建到渲染完成都经历了哪些钩子函数?

答案
  • beforeCreate / created / beforeMount / mounted

哪个阶段能获取到真实 DOM?

答案
  • mounted

Vue 更新阶段

目标

掌握更新阶段 2 个钩子函数作用和执行时机。

讲解

components/Life.vue

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
<template>
<div>
<ul id="myUL">
<li v-for="(val, index) in arr" :key="index">{{ val }}</li>
</ul>
<button @click="arr.push(1000)">点击末尾加值</button>
</div>
</template>

<script>
export default {
data() {
return {
arr: [5, 8, 2, 1],
}
},
// 数据发生变化,还未更新到 DOM
beforeUpdate() {
console.log(document.querySelectorAll('#myUL>li')[4]) // undefined
},
// 数据发生变化,已经更新到 DOM
// 场景:获取更新后的真实 DOM
updated() {
console.log(document.querySelectorAll('#myUL>li')[4].innerHTML) // li
},
}
</script>

小结

什么时候执行 updated 钩子函数?

答案
  • 数据发生变化,并且内容已经更新到 DOM。

Vue 销毁阶段

目标

掌握销毁阶段 2 个钩子函数的作用和执行时机。

讲解

触发时机:组件卸载/销毁时会触发 beforeDestroy 和 destroyed。

应用场景:解绑事件或清理定时器。

  1. 演示销毁。

components/Life.vue

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>Life</div>
</template>
<script>
export default {
name: 'LifeComponent',
beforeDestroy() {
console.log('~')
},
destroyed() {},
}
</script>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<Life v-if="bBar" />
<button @click="bBar = !bBar">销毁组件</button>
</div>
</template>

<script>
import Life from './components/Life.vue'

export default {
name: 'App',
data() {
return {
bBar: true,
}
},
components: {
Life,
},
}
</script>
  1. 演示应用场景。

components/Life.vue,清理定时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>Life</div>
</template>
<script>
export default {
name: 'LifeComponent',
created() {
this.timer = setInterval(() => {
console.log('~')
}, 1000)
},
beforeDestroy() {
clearInterval(this.timer)
},
}
</script>

components/Life.vue,解绑事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>Life</div>
</template>
<script>
export default {
name: 'LifeComponent',
created() {
document.documentElement.addEventListener('click', this.handleClick)
},
methods: {
handleClick() {
console.log('~')
},
},
beforeDestroy() {
document.documentElement.removeEventListener('click', this.handleClick)
},
}
</script>

小结

一般在 beforeDestroy/destroyed 里做什么?

答案
  • 手动消除计时器/解绑事件。

Axios 基本使用

目标

了解什么是 Axios,以及如何使用。

讲解

  • 基于原生 AJAX 封装的请求库。

  • 同时支持客户端和服务端(Node)发送 Ajax 请求。

  • 支持 Promise 相关用法。

  • 支持请求和响应的拦截器功能。

  • 自动转换 JSON 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
axios({
method: '请求方式', // GET、POST
url: '请求地址',
data: {
// 拼接到请求体的参数,POST 请求的参数
xxx: xxx,
},
params: {
// 拼接到请求行的参数,GET 请求的参数
xxx: xxx,
},
})
.then((res) => {
console.log(res.data) // 后台返回的结果
})
.catch((err) => {
console.log(err) // 后台报错返回
})

小结

Axios 是什么?

答案
  • 基于原生 AJAX 封装的请求库。

Axios 获取图书

目标

调用获取所有图书信息的接口。

讲解

image-20210511154911824

components/UseAxios.vue

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
<template>
<div>
<p>获取所有图书信息</p>
<button @click="getAllFn">点击-查看控制台</button>
</div>
</template>

<script>
// 目标:获取所有图书信息
// 1. 下载 axios
// 2. 引入 axios
// 3. 发起 axios
import axios from 'axios'
export default {
methods: {
getAllFn() {
axios({
method: 'GET', // 默认就是 GET 方式请求,可以省略不写
url: 'http://123.57.109.30:3006/api/getbooks',
}).then((res) => {
console.log(res)
})
},
},
}
</script>

小结

axios 函数调用返回的结果是什么?

答案
  • 是一个 Promise 对象。

如何拿到 Promise 里 AJAX 的成功或失败的结果?

答案
  • then() / catch()

Axios 传递参数

目标

调用接口,获取某本书籍信息。

讲解

image-20210511160538891

components/UseAxios.vue

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
<template>
<div>
<p>查询某本书籍信息</p>
<input type="text" placeholder="请输入要查询 的书名" v-model="bName" />
<button @click="findFn">查询</button>
</div>
</template>

<script>
import axios from 'axios'
export default {
data() {
return {
bName: '',
}
},
methods: {
findFn() {
axios({
url: 'http://123.57.109.30:3006/api/getbooks',
method: 'GET',
params: {
// 最终会拼接到 url? 后面
bookname: this.bName,
},
}).then((res) => {
console.log(res)
})
},
},
}
</script>

小结

通过 Axios 哪个配置项会把参数自动写到 url? 后面?

答案
  • params

Axios 发布书籍

目标

完成发布书籍功能。

讲解

image-20210511161239034

components/UseAxios.vue

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
<template>
<div>
<p>新增图书信息</p>
<div>
<input type="text" placeholder="书名" v-model="bookObj.bookname" />
</div>
<div>
<input type="text" placeholder="作者" v-model="bookObj.author" />
</div>
<div>
<input type="text" placeholder="出版社" v-model="bookObj.publisher" />
</div>
<button @click="sendFn">发布</button>
</div>
</template>

<script>
import axios from 'axios'
export default {
data() {
return {
bName: '',
bookObj: {
// 参数名提前和后台的参数名对上-发送请求就不用再次对接了
bookname: '',
author: '',
publisher: '',
},
}
},
methods: {
sendFn() {
axios({
url: 'http://123.57.109.30:3006/api/addbook',
method: 'POST',
data: {
// 会序列化为 JSON 格式的字符串后再传递到后台
appkey: '7250d3eb-18e1-41bc-8bb2-11483665535a',
...this.bookObj,
// 等同于下面
// bookname: this.bookObj.bookname,
// author: this.bookObj.author,
// publisher: this.bookObj.publisher
},
})
},
},
}
</script>

小结

Axios 哪个选项,可以把参数自动装入到请求体中?

答案
  • data

Axios 全局配置

目标

统一设置请求前缀/基准地址。

讲解

1
2
3
4
5
6
7
8
9
10
11
axios.defaults.baseURL = "http://123.57.109.30:3006"

// 所有请求的 url 前置可以去掉,请求时,axios 会自动拼接 baseURL 的地址在前面
getAllFn() {
axios({
url: "/api/getbooks",
method: "GET", // 默认就是 GET 方式请求,可以省略不写
}).then((res) => {
console.log(res);
});
},

小结

Axios 如何配置基地址?

答案
  • axios.defaults.baseURL

购物车案例

项目初始化

目标

初始化新项目,清空不要的东西,下载 bootstrap 库,下载 less 模块。

讲解

1
2
3
vue create shopcar
yarn add bootstrap
yarn add less less-loader -D

image-20210307092110985

  1. 按照需求,把项目页面拆分成几个组件,在 components 文件夹下创建:MyHeader、MyFooter、MyGoods、MyCount 组件。

  2. 在 App.vue 中引入上面的组件并注册。

  3. 在 main.js 中引入 bootstrap 库。

1
import 'bootstrap/dist/css/bootstrap.css' // 引入第三方包里的某个 CSS 文件

components/MyHeader.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div class="my-header">购物车案例</div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.my-header {
height: 45px;
line-height: 45px;
text-align: center;
background-color: #1d7bff;
color: #fff;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 2;
}
</style>

components/MyGoods.vue

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
<template>
<div class="my-goods-item">
<div class="left">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="input" />
<label class="custom-control-label" for="input">
<img src="http://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" alt="" />
</label>
</div>
</div>
<div class="right">
<div class="top">商品名字</div>
<div class="bottom">
<span class="price">¥ 100</span>
<span> 数量组件 </span>
</div>
</div>
</div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.my-goods-item {
display: flex;
padding: 10px;
border-bottom: 1px solid #ccc;
.left {
img {
width: 120px;
height: 120px;
margin-right: 8px;
border-radius: 10px;
}
.custom-control-label::before,
.custom-control-label::after {
top: 50px;
}
}
.right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.top {
font-size: 14px;
font-weight: 700;
}
.bottom {
display: flex;
justify-content: space-between;
padding: 5px 0;
align-items: center;
.price {
color: red;
font-weight: bold;
}
}
}
}
</style>

components/MyCount.vue

image-20210223135638409

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
<template>
<div class="my-counter">
<button type="button" class="btn btn-light">-</button>
<input type="number" class="form-control inp" />
<button type="button" class="btn btn-light">+</button>
</div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.my-counter {
display: flex;
.inp {
width: 45px;
text-align: center;
margin: 0 10px;
}
.btn,
.inp {
transform: scale(0.9);
}
}
</style>

components/MyFooter.vue

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
<template>
<!-- 底部 -->
<div class="my-footer">
<!-- 全选 -->
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="footerCheck" />
<label class="custom-control-label" for="footerCheck">全选</label>
</div>
<!-- 合计 -->
<div>
<span>合计:</span>
<span class="price">¥ 0</span>
</div>
<!-- 按钮 -->
<button type="button" class="footer-btn btn btn-primary">结算 ( 0 )</button>
</div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.my-footer {
position: fixed;
z-index: 2;
bottom: 0;
width: 100%;
height: 50px;
border-top: 1px solid #ccc;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
background: #fff;

.price {
color: red;
font-weight: bold;
font-size: 15px;
}
.footer-btn {
min-width: 80px;
height: 30px;
line-height: 30px;
border-radius: 25px;
padding: 0;
}
}
</style>

App.vue

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
<template>
<div class="app">
<MyHeader />
<MyGoods />
<MyFooter />
</div>
</template>

<script>
import MyHeader from './components/MyHeader.vue'
import MyGoods from './components/MyGoods.vue'
import MyFooter from './components/MyFooter.vue'
export default {
name: 'App',
components: {
MyHeader,
MyGoods,
MyFooter,
},
}
</script>

<style scoped>
.app {
padding-top: 45px;
}
</style>

小结

拿到项目需求,做什么?

答案
  • 新建项目,下载需要的包。
  • 分析,拆分,创建组件。
  • 引入组件到对应的位置并注册。

头部自定义

目标

头部的标题,颜色,背景色可以随便修改。

讲解

  1. MyHeader.vue 的 props 中准备变量,然后使用。

  2. 在父组件传入相应的值 (color 和 backgroundColor)。

MyHeader.vue

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
<template>
<div class="my-header" :style="{ backgroundColor: background, color }">{{ title }}</div>
</template>

<script>
// 1. 分析哪些可以自定义 (背景色, 文字颜色, 文字内容)
// 2. 可以对 props 的变量的值进行校验
// 3. 内部使用 props 变量的值
export default {
props: {
background: String, // 外部插入此变量的值,必须是字符串类型,否则报错
color: {
type: String, // 约束 color 值的类型
default: '#fff', // color 变量默认值(外部不给 color 传值,使用默认值)
},
title: {
type: String,
required: true, // 必须传入此变量的值
},
},
}
</script>

<style lang="less" scoped>
.my-header {
height: 45px;
line-height: 45px;
text-align: center;
background-color: #1d7bff;
color: #fff;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 2;
}
</style>

App.vue 传入相应自定义的值。

1
<MyHeader title="购物车案例"></MyHeader>
  • props: [],只能接收外部传入的数据。

  • props: {},能对接收到的数据进行校验。

小结

封装组件,如何个性化可能变化的部分?

答案
  • 传值。

props 有哪 2 种定义方式,区别是?

答案
  • props: [],只能接收外部传入的数据。
  • props: {},能对接收到的数据进行校验。

请求数据

目标

使用 Axios 把数据请求回来。

讲解

数据地址:https://www.escook.cn/api/cart (GET 方式)。

  1. 下载 axios。
1
yarn add axios
  1. 原型上挂载 axios,main.js
1
2
3
4
5
6
7
8
import axios from 'axios'
axios.defaults.baseURL = 'https://www.escook.cn'
// 挂载到 Vue.prototype 上的属性,任何组件都可以通过实例(this)进行访问
Vue.prototype.$axios = axios

new Vue({
render: (h) => h(App),
}).$mount('#app')
  1. App.vue 请求使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
data() {
return {
list: [], // 商品所有数据
}
},
created() {
// 不必再自己引入 axios 变量,而是直接使用全局属性 $axios
this.$axios({
url: '/api/cart',
}).then((res) => {
console.log(res)
this.list = res.data.list
})
},
}
</script>

小结

在哪里定义的数据,任意组件都可以通过 this 访问到?

答案
  • Vue.prototype

数据渲染

目标

把上面请求的数据,铺设到页面上。

讲解

App.vue

1
<MyGoods v-for="obj in list" :key="obj.id" :gObj="obj"></MyGoods>

MyGoods.vue

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
<template>
<div class="my-goods-item">
<div class="left">
<div class="custom-control custom-checkbox">
<!-- 注意 label 上的 for 属性值和 input 上的 id 对应起来,且不能重复 -->
<input type="checkbox" class="custom-control-input" :id="gObj.id" v-model="gObj.goods_state" />
<label class="custom-control-label" :for="gObj.id">
<img :src="gObj.goods_img" alt="" />
</label>
</div>
</div>
<div class="right">
<div class="top">{{ gObj.goods_name }}</div>
<div class="bottom">
<span class="price">¥ {{ gObj.goods_price }}</span>
<span>
<MyCount :obj="gObj"></MyCount>
</span>
</div>
</div>
</div>
</template>

<script>
import MyCount from './MyCount'
export default {
props: {
gObj: Object,
},
components: {
MyCount,
},
}
</script>

MyCount.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div class="my-counter">
<button type="button" class="btn btn-light">-</button>
<input type="number" class="form-control inp" v-model.number="obj.goods_count" />
<button type="button" class="btn btn-light">+</button>
</div>
</template>

<script>
export default {
props: {
obj: Object, // 商品对象
},
}
</script>

小结

拿到数据如何铺设页面?

答案
  • 循环组件并传入数据。
  • 组件内接收数据并使用。

商品选中

目标

点击发现总是第一个被选中。

步骤

lable 的 for 值对应 input 的 id,点击 label 就能让对应 input 处于激活。

1
2
3
4
<input type="checkbox" class="custom-control-input" :id="gObj.id" v-model="gObj.goods_state" />
<label class="custom-control-label" :for="gObj.id">
<img :src="gObj.goods_img" alt="" />
</label>

小结

label 标签有什么用?

答案
  • 配合 for 属性及表单的 id 属性,能扩大表单的点击区域。

组件传递数据,传递对象本质上传的是什么?

答案
  • 引用地址。

数量控制

目标

点击 + 和 - 或者直接修改输入框的值影响商品购买的数量。

讲解

components/MyCount.vue

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
<template>
<div class="my-counter">
<button type="button" class="btn btn-light" :disabled="obj.goods_count === 1" @click="obj.goods_count > 1 && obj.goods_count--">-</button>
<input type="number" class="form-control inp" v-model.number="obj.goods_count" />
<button type="button" class="btn btn-light" @click="obj.goods_count++">+</button>
</div>
</template>

<script>
// #1 外部传入数据对象
// #2 v-model 关联对象的 goods_count 属性和输入框(双向绑定)
// #3 商品按钮 + 和 -,商品数量最少 1 件
// #4 侦听数量改变,小于 1,直接强制覆盖 1
export default {
props: {
obj: Object, // 商品对象
},
// 因为数量控制要通过对象"互相引用的关系"来影响外面对象里的数量值,所以最好传对象进来
watch: {
obj: {
deep: true,
handler() {
// 拿到商品数量,判断小于1,强制修改成1
if (this.obj.goods_count < 1) {
this.obj.goods_count = 1
}
},
},
},
}
</script>

小结

子组件改变对象里属性会影响外面数组里对象否?

答案
  • 会,因为对象之间引用了相同的内存地址

控制输入框值合法性?

答案
  • 先控制按钮,然后侦听输入框值,强制覆盖

全选功能

目标

在底部组件上,完成全选功能。

image-20210223141427684

讲解

  1. 确定当前全选按钮的状态。

  2. 点击全选状态后,把改变后的新值同步给每一个单选。

MyFooter.vue

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
<template>
<!-- 底部 -->
<div class="my-footer">
<!-- 全选 -->
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="footerCheck" v-model="isAll" />
<label class="custom-control-label" for="footerCheck">全选</label>
</div>
<!-- 合计 -->
<div>
<span>合计:</span>
<span class="price">¥ {{ allPrice }}</span>
</div>
<!-- 按钮 -->
<button type="button" class="footer-btn btn btn-primary">结算 ( {{ allCount }} )</button>
</div>
</template>

<script>
export default {
props: {
list: Array,
},
computed: {
isAll: {
set(val) {
this.$emit('changeAll', val)
},
get() {
return list.arr.every((obj) => obj.goods_state === true)
},
},
},
}
</script>

App.vue

1
2
3
4
5
6
7
8
9
<MyFooter @changeAll="allFn" :list="list"></MyFooter>

<script>
methods: {
allFn(bool){
this.list.forEach(obj => obj.goods_state = bool)
}
}
</script>

小结

全选和单选,互相影响的思路是什么?

答案
  • 根据所有单选按钮的状态来确定全选按钮的状态。
  • 点击全选状态后,把改变后的新值同步给每一个单选。

总数量

目标

完成底部组件,显示选中的商品的总数量。

讲解

MyFooter.vue

1
2
3
4
5
6
7
8
allCount() {
return this.list.reduce((acc, cur) => {
if (cur.goods_state === true) {
acc += cur.goods_count
}
return acc
}, 0)
},

小结

统计选中商品的总数量思路是什么?

答案
  • 计算属性。
  • reduce 求累计值。

总价

目标

完成选中商品计算价格。

讲解

components/MyFooter.vue

1
2
3
4
5
6
7
8
allPrice(){
return this.arr.reduce((acc, cur) => {
if (cur.goods_state){
acc += cur.goods_count * cur.goods_price
}
return acc;
}, 0)
}

小结

统计总价如何做的?

答案
  • 计算属性。
  • 统计数组里的商品数量 * 商品单价。
  • 只要依赖项修改,计算属性都会重新计算总价。

今日作业

课上案例

图书管理

获取

  • 根域名:http://123.57.109.30:3006

  • 请求方式:GET。

  • 请求地址:根域名/api/getbooks。

  • 请求参数:不传参获取所有默认书籍,也可以选择传递下面任意 1-多个参数,获取指定的相关书籍信息。

参数名称 参数类型 是否必选 参数说明
id Number 图书 Id
bookname String 图书名称
author String 作者
publisher String 出版社
appkey String 个人 ID
  • 返回示例。
1
2
3
4
5
6
7
8
9
10
{
"status": 200, // 状态码200都代表完全成功 - 无任何意义随便写,只是方便前端写判断条件
"msg": "获取图书列表成功", // 后台返回的提示消息,随便写,只是方便前端直接打印提示消息
"data": [
// 后台返回的数据
{ "id": 1, "bookname": "西游记", "author": "吴承恩", "publisher": "北京图书出版社" },
{ "id": 2, "bookname": "红楼梦", "author": "曹雪芹", "publisher": "上海图书出版社" },
{ "id": 3, "bookname": "三国演义", "author": "罗贯中", "publisher": "北京图书出版社" }
]
}

添加

  • 请求方式:POST。
  • 请求地址:根域名/api/addbook。
  • 请求参数
参数名称 参数类型 是否必选 参数说明
bookname String 图书名称
author String 作者
publisher String 出版社
appkey String 个人 ID - 用’7250d3eb-18e1-41bc-8bb2-11483665535a’
  • 返回示例
1
2
3
4
5
6
7
8
9
10
{
"status": 201, // 后台返回数据逻辑层的状态码,201代表后台已经新增加了一个资源
"data": {
"author": "施大神"
"bookname": "水浒传2"
"id": 41
"publisher": "未来出版社"
}
"msg": "添加图书成功"
}