【Vue】表单数据双向绑定 vue生命周期 fetch和axios发送请求 Vue全局组件
昨日回顾
#1 style,class
-字符串
-数组
-对象
-横杠转驼峰体
# 2 v-if=条件 v-else-if=条件 v-else
# 3 v-for='(i,k)in 数字,数组,字符串,对象'
-基于索引的循环 (i=0;i<10;i++)
-in 循环出来的是索引
-of 基于迭代的,循环出来就是值
-数组.each(item=>{})
-ajax forEach 循环
# 4 key值得解释:v-for上的标签上,尽量写 :key=唯一值
# 5 数组和对象的检测与更新:
Vue.set(要改得值,0,改成的值)
# 6 input: v-model
# 7 input的事件:blur,change,input
# 8 过滤案例
-1 数组的filter方法
数组=数组.filter(function(item){return true})
-2 字符串.indexOf(子字符串) 返回子字符串在当前字符串的位置 如果 >=0 说明在里面
-3 es6 对象写法
{'name':'lqz'}--->{name:'lqz'}
var a='lqz'
var f=function(itemt){}
{a,f(){}}
-4 箭头函数
数组.filter(item=>true)
有一个参数的,多个参数,有返回值并且只有一行
箭头函数没有自己的this
# 9 事件修饰符
.stop
.self
.prevent
.once
# 10 按键修饰符
@keyup.esc='函数'
@keyup.enter='函数'
@keyup.13='函数' (Keycode值,这种写法在Vue3中被弃用)
# 11 表单控制
-radio :字符串类型 多个radio使用v-model绑定,选中某个会把 value 赋值给这个变量
-chekbox
-单选:布尔类型
-多选:数组中
表单数据双向绑定(重要)
# input:checkbox(单选,多选),radio(单选)
checkbox单选 --- 布尔值
给checkbox的v-model
该如何绑定?
这样如果选中checkbox,变量isRemeber
的值就会变成true
。
checkbox多选 --- 数组
所有checkbox都使用一个数组:
如:
每次选中checkbox,都会把value值放入数组:
radio单选 --- 字符串
如何对radio单选框进行双向数据绑定?
这里的意思是如果你选了男
,就会将标签value
所对应的值赋值给gender
,此时gender:'男'
。
value需要每个都不一样。
通常会这样表示不同的选项:
给后端发送数据
写一个按钮:
所有的数据都是双向绑定的,并且所有的数据都在Vue
对象的data
属性中,直接获取即可,相当方便。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>表单控制</h1>
<p>用户名:<input type="text" v-model="name"></p>
<p>密码:<input type="text" v-model="password"></p>
<p><input type="checkbox" v-model="isRemember"> 记住密码</p>
<p>
<input type="radio" v-model="gender" value="1"> 男
<input type="radio" v-model="gender" value="2"> 女
<input type="radio" v-model="gender" value="0"> 未知
</p>
<p>
爱好:
<input type="checkbox" value="篮球" v-model="hobby"> 篮球
<input type="checkbox" value="足球" v-model="hobby"> 足球
<input type="checkbox" value="乒乓球" v-model="hobby"> 乒乓球
<input type="checkbox" value="橄榄球" v-model="hobby"> 橄榄球
</p>
{{hobby}}
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
name: '',
password: '',
isRemember: false, // checkbox单选,使用布尔类型
gender: '', // radio单选,使用字符串
hobby: [], // checkbox 多选使用数组
},
})
</script>
</html>
购物车案例
使用v-model
将checkbox与数组进行双向绑定,每次点击或者取消checkbox都会导致数组的变化:
需要注意的是,checkbox的value属性应该放一个商品对象item
,这样后续获取数据方便。
编写getPrice
函数:
methods: {
getPrice() {
// 通过checkGroup里面的对象,计算出总价格
var total = 0
for (item of this.checkGroup) {
total += item.price * item.number
}
return total
}
}
页面上使用插值语法:{{ getPrice() }}
,即可实现需求。
每次点击checkbox,checkGroup
数组都会发生变化,都会运行getPrice
函数,函数的返回值会通过插值语法{{ getPrice() }}
即时的渲染到页面上。
- js的变量只要发生变化,html页面中使用该变量的地方,就会重新渲染。(通过虚拟dom)
也就是因为checkGroup
发生了变化,所以引用到这个变量的 getPrice()
也会发生变化。虚拟dom会和真实dom进行比较,发现页面有变化的地方,针对这些地方进行更新。使得最终页面只有部分发生变化,而不是整体重新渲染。
全选按钮
这个全选按钮和之前的checkbox不能绑定同一个数组,而是使用新的数组。
给全选按钮添加@change
事件,点击全选按钮之后,让数组添加所有数据,因为数组的数据是跟页面绑定的,所以只需修改数组即可,前端页面也会发生变化:
再给每个商品的选择按钮添加一个点击事件,用于判断是否需要将全选按钮勾选上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="app">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>购物车案例</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>商品编号</th>
<th>商品名字</th>
<th>商品价格</th>
<th>商品数量</th>
<th>全选/全不选 <input type="checkbox" v-model="checkAll" @change="handleCheckAll"></th>
</tr>
</thead>
<tbody>
<tr v-for="item in goodList">
<th scope="row">{{item.id}}</th>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.number}}</td>
<td><input type="checkbox" v-model="checkGroup" :value="item" @change="handelCheckOne"></td>
</tr>
</tbody>
</table>
<hr>
<p>选中商品:{{checkGroup}}---{{checkAll}}</p>
<p>总价格:{{getPrice()}}</p>
</div>
</div>
</div>
</div>
</body>
<script>
var vm = new Vue({
el: '.app',
data: {
goodList: [
{id: '1', name: '钢笔', price: 20.1, number: 2},
{id: '2', name: '饼干', price: 4, number: 1},
{id: '3', name: '辣条', price: 5, number: 5},
],
checkGroup: [],
checkAll: false,
},
methods: {
getPrice() {
// 通过checkGroup里面的对象,计算出总价格
var total = 0
for (item of this.checkGroup) {
total += item.price * item.number
}
return total
},
handleCheckAll() {
if (this.checkAll) {
this.checkGroup = this.goodList
} else {
this.checkGroup = []
}
},
handelCheckOne() {
// console.log('一个被点了')
if (this.checkGroup.length==this.goodList.length){
this.checkAll=true
}else {
this.checkAll=false
}
}
}
})
</script>
</html>
点击事件对应的函数:
商品添加删除
商品加减功能,给按钮绑定点击事件:
@click="item.number++"
:点击事件后面可以写简单的逻辑代码。
商品的添加不能超过库存:
页面加载好时,就已经获取到商品的库存值,如果添加的商品超过库存,那么就禁止商品添加。
减少商品的时候,会出现负数的问题:
需要添加函数限制一下。将item传入点击事件:
<!-- 部分html -->
<td>
<button @click="handleDown(item)">-</button>
{{item.number}}
<button @click="item.number++">+</button>
</td>
<!-- 部分js -->
handleDown(item) {
if (item.number > 1) {
item.number--
} else {
alert('太少了,受不了了')
}
注意:减少商品,不能减少到0,最少减少到1。再添加一个删除按钮,对购物车的商品进行删除。(如果是真正的购物车,需要发送一个ajax请求去数据库将购物车的数据删除)
库存校验
添加库存数据:
代码:
# 部分html
<td>
<button class=" btn-default" @click="addGood(good)">➕</button>
<button>{{ good.count }}</button>
<button class="btn-default" @click="delGood(good)">➖</button>
</td>
# 部分js
addGood(good) {
if (good.count < good.store) {
good.count++
}
},
delGood(good) {
if (good.count > 1) {
good.count--
}
},
可变类型不可变类型
python函数参数是值传递还是引用传递?
python中没有值传递和引用传递的概念。取而代之的是可变类型和不可变类型。
把一个变量传入函数内,在函数内将变量进行修改。
- 如果传入的是不可变类型,外部的变量不会一同被修改。
- 如果传入的是可变类型,外部的变量会被一同修改。
js中的对象在函数内部修改,外部的变量也会被影响。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<link rel="stylesheet" href="./js/bootstrap.css">
</head>
<body>
<div id="app">
<div class="container-fluid">
<div class="row top">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">购物车</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>编号</th>
<th>商品</th>
<th>价格</th>
<th>数量</th>
<th>全选
<input v-model="checkAll" type="checkbox" @change="handleCheckAll">
</th>
</tr>
</thead>
<tbody>
<tr v-for="good in shopCar">
<td>{{good.id}}</td>
<td>{{good.name}}</td>
<td>{{good.price}}</td>
<td>
<button class=" btn-default" @click="addGood(good)">➕</button>
{{ good.count }}
<button class="btn-default" @click="delGood(good)">➖</button>
</td>
<td :id="good.id">
<input class="btn btn-default" type="checkbox" v-model="goodList" :value="good"
@change="handleCheckOne">
<!-- 不能使用点击事件,因为点击事件快于v-model的双向数据绑定 -->
</td>
</tr>
</tbody>
</table>
<h4>总价格:{{ getPrice() }}</h4>
</div>
</div>
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
shopCar: [
{id: 1, name: '曹操戏张飞', price: 10, count: 1, store: 5},
{id: 2, name: '仿真玩偶', price: 100, count: 1, store: 5},
{id: 3, name: '印度飞饼', price: 1000, count: 1, store: 5},
{id: 4, name: '仿真玩偶plus', price: 10000, count: 1, store: 5},
],
goodList: [],
checkAll: false,
},
methods: {
getPrice() {
var total = 0
for (item of this.goodList) {
total += item.price * item.count
}
return total
},
handleCheckAll() {
if (this.checkAll) {
this.goodList = this.shopCar
} else {
this.goodList = []
}
},
handleCheckOne() {
this.checkAll = this.goodList.length === this.shopCar.length;
},
addGood(good){
if (good.count<good.store){
good.count++
}
},
delGood(good) {
if (good.count > 1) {
good.count--
}
},
}
})
</script>
</body>
</html>
v-model进阶(了解)
lazy:等待input框的数据绑定失去焦点之后再变化
number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留
trim:去除首位的空格
v-mode.lazy
示例:
由于数据做了双向绑定,每次只要输入数据,页面都会发生变化(其实页面是在更新的),这样是非常消耗资源的。
而我们通常只想要用户输入的数据,所以这个时候就可以使用v-model.lazy
:
此时当input输入框失去焦点的时候,页面才会发生变化:
v-model.number
当数字开头时,会对输入的数字进行双向绑定,增删数字会即时的渲染在页面上;如果后续输入字母,不会对字母渲染:
当字母开头时,就相当于就是普通的input框数据和页面双向绑定了:
v-model.trim
当input输入框带空格时,v-model.trim
可以删除首尾的空格,同时对输入数据进行双向绑定:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>v-model进阶</h1>
<input type="text" v-model.lazy="name1"> ---->{{name1}}
<br>
<input type="text" v-model.number="name2"> ---->{{name2}}
<br>
<input type="text" v-model.trim="name3"> ---->{{name3}}
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
name1: '',
name2: '',
name3: '',
},
})
</script>
</html>
vue生命周期
Vue对象的四个过程
# var vm=new Vue实例() 这行代码做了什么事?
-1 实例创建,data数据存放到实例中
-2 挂载模板:el --挂载到--> div
- 挂载结束后,数据与变量实现双向绑定
-3 改页面,改变量,都会相互影响 (update)
- 修改页面或修改变量都不会重新执行上面1、2步
- 浏览器再转圈
-4 销毁实例(页面关闭)
八个钩子函数
上述Vue生命周期的4个过程对应如下8个方法。也叫8个钩子函数。
# 4个过程,对应八个函数,依次执行(到某个过程就会执行某个函数
# 每个过程对应两个钩子函数
-1 实例创建
beforeCreate 创建Vue实例之前调用,data,el都没有
created 创建Vue实例成功后调用(可以在此处发送异步请求后端数据),data有了,el没有的
-2 挂载模板
beforeMount 渲染DOM之前调用 ,data有了,el没有
mounted 渲染DOM之后调用
-3 修改页面、修改变量
beforeUpdate 重新渲染之前调用(数据更新等操作时,控制DOM重新渲染)
updated 重新渲染完成之后调用
-4 销毁实例
beforeDestroy 销毁之前调用
destroyed 销毁之后调用
-
什么叫做钩子函数?(hook)
Hook 技术又叫做钩子函数,在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递。简单来说,就是把系统的程序拉出来变成我们自己执行代码片段。
-
钩子函数是AOP的体现(面向切面编程):
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
python中装饰器可以实现aop。
简单的说,就是你在程序中写了钩子函数,钩子函数就执行,不写就不执行。
# 5 钩子函数(hook)
AOP的体现:面向切面编程 --> 装饰器实现aop
# 6 vm实例:看不到它销毁 组件vc
销毁vm实例需要关闭页面 --> 无法看见销毁vm的打印
组件vc --> 是一个对象 --> 可以看到完整的生命周期
研究Vue的生命周期
钩子函数是写到配置项里的,和el
是同级的,并且他的名字是固定的,比如:
一个八个,这些钩子函数会依次去执行。
页面一刷新,钩子函数就开始执行:
此时已经挂载完成了,也就是执行到Vue生命周期的第二个过程。
当我们进行了变量的修改,此时页面会更新,也就会执行钩子函数:
如果再次修改,这两个钩子函数还会执行。
当页面被销毁,我们就看不到控制台的输出了,但是页面销毁会执行剩余的两个钩子函数。
全局组件
全局组件如何定义?
使用Vue.component('组件名',{配置项})
。
Vue.component
的第二个参数就相当于Vue()
的el
配置项。
注意:data的写法有些不同,在这个data函数的返回值的对象里写组件相关的数据。
为什么组件的数据使用data()
函数存放?
组件可以使用多次,每个组件用自己的数据,每个组件都是用的函数返回的数据(每次都返回一个新的对象)。如果不是函数,是对象,相当于每次调用组件都使用的是同一份数据(同一个对象)。
将8个钩子函数放入全局组件:
template
怎么写?
在template
里写html代码,组件内data内的数据,可以使用插值语法渲染在组件内。
如何将组件放在我们原来的html页面?
相当于自定义了一个标签<child></child>
,会把template
的内容渲染到child标签的位置。
组件template
内的名字和组件外的名字是可以重复的,不会互相影响。
实现组件的显示和消失(在根组件写代码):
组件有自己的样式、自己的事件。
根组件和子组件:
组件如何更新?
在组件的methods部分写事件相关函数。
数据更新时,会执行组件的更新相关的两个钩子函数。
但使用根组件让子组件销毁,会执行子组件的销毁相关钩子函数:
在组件created方法向后端发送ajax
为什么要在created?
# 8 学习生命周期重点掌握的
-1 组件向后端发送请求,获取数据,应该放在 created 写,此时data已经有数据了
-2 destroyed做一些资源清理性的工作
子组件有自己的数据,数据是从后端加载过来的,子组件是什么时候向后端发送请求获取数据的?是在生命周期的哪个位置?
应该在created
函数执行时。data数据已经获取了,挂载还没有进行,所以el还不存在。
this
是当前子组件:
查看前端:
- beforeCreate时,data和el都没有.
- created时,data有了,但是由于还未挂载所以el没有
- beforeMount:此时data有了,el还未挂载
- mounted: 终于el挂载到template
注意:前端向后端发送ajax请求放在created
函数里,不要放在beforeCreate
此时前端数据不存在。ajax请求在mounted
也可以,如果前端加载很慢的话,前端可能会先显示一个默认值,然后再显示从后端接受到的数据。数据变化之后,页面会刷新。
destroyed资源清理
destroyed做一些资源清理性的工作:
如果在组件的created中执行了一些任务,那么当组件销毁了,可能这些任务还没销毁,这种情况就不太合适。所以就需要使用destroyed
和beforeDestory
做一些资源清理的工作。
组件销毁时,销毁定时器:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>vue声明周期</h1>
<button @click="handleShow">点我组件显示和消失</button>
<hr>
<child v-if="show"></child>
<hr>
</div>
</body>
<script>
// 定义一个全局组件
Vue.component('child', {
template: `
<div>
<button>后退</button>
{{ title }}
<button @click="handleClick">前进</button>
</div>`,
data() {
return {
title: '好看的首页',
t:''
}
},
methods: {
handleClick() {
// alert('前进')
this.title = 'lqz'
}
},
beforeCreate() {
console.log('beforeCreate')
console.log(this.$data)
console.log(this.$el)
},
created() {
console.log('created')
console.log(this.$data)
console.log(this.$el)
// 开启定时器,每隔3s,打印hello
this.t=setInterval(()=>{
console.log('hello')
},3000)
},
beforeMount() {
console.log('beforeMount')
console.log(this.$data)
console.log(this.$el)
},
mounted() {
console.log('mounted')
console.log(this.$data)
console.log(this.$el)
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('当前状态:beforeDestroy')
},
destroyed() {
console.log('当前状态:destroyed')
// 销毁定时器
clearInterval(this.t)
this.t=null
},
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleShow() {
this.show = !this.show
}
}
})
</script>
</html>
定时任务和延时任务的使用场景
# 小案例:组件创建,开启定时器,不停的打印hello,在destroyed中对定时器进行销毁
-补充:js 定时任务和延时任务
# 延时任务
setTimeout(()=>{
console.log('3s后执行我')
},3000)
# 定时任务
setInterval(()=>{
console.log('hello')
},3000)
# 什么场景下用定时任务?
1 实时跟后端交互 基于http+定时任务 (有缺陷)
- 一直通过定时任务发送请求,十分消耗资源
- 因为上述方案有缺陷所以产生了:websocket协议 ---> 服务端主动推送消息:手机app的消息推送
2 秒杀场景:先提交秒杀请求,每隔3s,查询是否秒到
setTimeout延时任务:3s之后执行
setInterval定时任务:每隔3s执行一次
在线聊天室案例
# 实现实时聊天效果(在线聊天室)
-轮询:定时器+ajax http:http版本区别
-长轮询:定时器+ajax http
-websocket协议:服务端主动推送消息
-https://zhuanlan.zhihu.com/p/371500343
# 扩展
-http websocket都是基于tcp
-http版本区别 1.1 1.9 2.0
什么场景下用定时任务?
扫二维码登录微信时。比如初次扫码的时候,会有一个请求是pending状态:
浏览器在等待你确认登录时,会一直向服务端发送请求:
使用手机扫二维码之后,手机会向微信的服务器发送登录相关的请求,但是浏览器不知道你发送了这个请求,所以浏览器需要一刻不停的向后端发送请求(轮询),以验证当前用户是否扫码。如果发现用户扫了码,浏览器这个定时任务的请求,就会把用户头像数据带回来,显示在页面上。
也就是说,数据不是服务器主动发送给客户端的,而是客户端一直发送定时任务,主动去拿的。(类似于拿快递,服务器是寄存点)(这都是因为http协议的特性:无状态、无连接)
比如: A ---> B A向B发送消息
实际上是: A --发送信息--> 服务器
B --查询信息--> 服务器
示例:
页面一直发送定时任务,查询是否有新增的消息。如果有新增的消息,将消息带回来,进行页面更新。
秒杀案例
什么场景下使用延时任务?
先提交秒杀请求,每隔3s,查询是否秒到。
首先,在组件中开启一个定时器(存放在data()
):
全局组件:
组件销毁了之后,定时器还是在运行,这显然不合适,所以需要在组件的destroyed()
钩子函数中将定时器销毁:
与后端交互
使用jquery的ajax与后端交互
需求:点击按钮,数据加载,页面更新。
Vue:
点击事件函数:
使用jquery
封装的ajax方法。
创建flask服务端:
只要有请求访问根路径,就会返回数据。
跨域问题
# 跨越问题
-浏览器的原因,只要向不是地址栏中的 [域:地址和端口]发送请求,拿的数据,浏览器就给拦截了
# 处理跨域问题
-后端代码处理----》只需要在响应头中加入允许即可
前端点击按钮,会向后端发送请求,获取数据,但是发送了跨域请求报错:
解决跨域问题需要修改后端代码,需要在响应头里加一句话:
示例:
前端渲染数据:
注意从后端获取到的数据是json字符串,需要通过JSON.parse
转换成对象。
fetch和axios发送请求
jquery不仅封装了ajax,还有很多的dom操作,而axio只能发请求。
axio使用promsie风格,promsie风格是es6的新语法。
promsie风格是为了解决如下嵌套发送请求的情况,优化代码:
fetch
# fetch 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应
-新的发送ajax 接口
-用起来比较方便
-支持promise写法[最新的异步写法]
-解决了原生的XMLHttpRequest兼容性的问题
-不是所有浏览器都支持
-主流现在是用axios[第三方]发送请求
# XMLHttpRequest: 原生js提供的
-比较老,不同浏览器需要做一些兼容性的处理,写起来比较麻烦
-jq基于它做了封装
'''大公司会基于 javascript XMLhttprequest 或者 fetch 进行封装来使用'''
fetch是js原生的,所以可以直接使用:
第一个then
会返回一个数据,传给第二个then
。
查看前端:
渲染数据:
axios
# axios是在Vue上的第三方的模块
# Axios 是一个基于 promise 的 HTTP 库,还是基于XMLHttpRequest封装的
官网:起步 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
使用cdn引入axios:
只要引入了,就可以在Vue组件里使用!
示例:
查看前端:
后端返回的数据在res.data
中。
修改数据:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<div id="app">
<h1>jquery的ajax与后端交互</h1>
<!-- <button @click="handleLoad1">点击加载数据</button>-->
<!-- <br>-->
<!-- <p>名字是:{{name}}</p>-->
<!-- <p>年龄是:{{age}}</p>-->
<!-- <hr>-->
<h1>js原生的fetch与后端交互</h1>
<!-- <button @click="handleLoad2">点击加载数据</button>-->
<!-- <br>-->
<!-- <p>名字是:{{name}}</p>-->
<!-- <p>年龄是:{{age}}</p>-->
<!-- <hr>-->
<h1>axios与后端交互</h1>
<button @click="handleLoad3">点击加载数据</button>
<br>
<p>名字是:{{name}}</p>
<p>年龄是:{{age}}</p>
<hr>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
name: '',
age: 0
},
methods: {
handleLoad1() {
$.ajax({
url: "http://127.0.0.1:5000/",
type: 'get',
success: data => {
console.log(typeof data)
data = JSON.parse(data) // data 是字符串类型,需要转成对象类型
console.log(typeof data)
this.name = data.name
this.age = data.age
}
})
},
handleLoad2() { // 用的很少
fetch('http://127.0.0.1:5000/').then(res => res.json()).then(res => {
console.log(res)
console.log(typeof res)
this.name = res.name
this.age = res.age
})
},
handleLoad3() { // 用的很少
axios.get('http://127.0.0.1:5000/').then(res => {
console.log(res.data) // 后端真正的数据在res.data中
this.name = res.data.name
this.age = res.data.age
})
},
}
})
</script>
</html>
显示电影案例
需求:后端加载电影相关数据,前端显示。
数据获取:
把数据放置在本地文件。
后端代码:
在前端created
方法内,向后端获取数据:
循环展示电影:json.cn
如果后端返回的数据格式嵌套的层级比较多,前端需要一层一层的点出来:
前端还需要判断一下状态码是否为100,然后再将后端获取到的数据进行赋值。
有的公司觉得后端使用json格式返回不安全,比如接口返回出了患者的身份证号。需要对数据进行加密。后端在中间件中解密获取数据,再加密返回给前端。但是这种加密十分影响接口的效率,通常只对重要的接口使用。
示例:
进行了数据的加密:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<h1>电影小案例</h1>
<ul>
<li v-for="film in filmList">
<h2>电影名:{{film.name}}</h2>
<img :src="film.poster" alt="" height="400px" width="300px">
</li>
</ul>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
filmList: []
},
created() {
axios.get('http://127.0.0.1:5000/films').then(res => {
this.filmList = res.data.data.films
})
}
})
</script>
</html>
Vue组件
# 组件化开发的好处:重用代码
# 组件分类
-全局组件:在任意组件中都可以使用
-局部组件:只能在当前组件中使用
示例:
组件化之后可复用代码:
组件的分类:全局组件和局部组件。
全局组件在任意组件中都可以使用。
在组件中也可以使用全局组件:
局部组件只能在当前组件中使用。
定义局部组件:
使用components
定义局部组件。
局部组件只能在Vue对象监控的代码内使用:
可以将组件的代码放在变量里:
这样在html上使用标签<foo>
就可以加载局部组件。
工程化开发之后一个组件就是一个xx.Vue
文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<script src="./js/axios.js"></script>
</head>
<body>
<div class="app">
<h1>组件</h1>
<child></child>
<foo></foo>
<!-- <lqz></lqz>-->
</div>
</body>
<script>
// 全局组件-----》在任意组件中都可以使用
// 跟之前学的没有区别
Vue.component('child', {
template: `
<div>
<button>后退</button>
<span style="font-size: 40px">首页--{{ name }}</span>
<button @click="handleFor">前进</button>
<lqz1></lqz1>
</div>`,// 里面写html内容,必须包在一个标签中
data() { // data必须是方法,返回对象
return {
name: '彭于晏',
t: null
}
},
methods: {
handleFor() {
this.name = 'lqz'
}
},
components: {
'lqz1': {
template: `
<div>
<h1>局部组件---{{ age }}</h1>
</div>`,
data() {
return {
age: 19
}
}
},
}
})
Vue.component('child3', {
template: `
<div>
<button>后退</button>
</div>`,
})
var foo={
template: `
<div>
<h1>局部组件---{{ age }}</h1>
</div>`,
data() {
return {
age: 19
}
}
}
var vm = new Vue({
el: '.app',
data: {
show: false
},
methods: {
handleShow() {
this.show = !this.show
}
},
components: {
foo
}
})
</script>
</html>
补充
'''可变类型和不可变类型'''
# python
-不可变类型:数字,字符串,元组
-可变类型:列表,字典,集合
-python中没有值类型和引用类型的叫法----【因为python一切皆对象,对象都是地址都是引用】
-可变类型当参数传到函数中,在函数中修改会影响原来的
-不可变类型当参数传到函数中,在函数中修改不会影响原来的
# python 函数参数传递是值传递还是引用传递? 这个问题不应该有
# js 传入了item 对象,在函数中修改,影响了原来的
-js 的对象是引用类型
'''json格式安全性问题'''
# 之前前后端交互,使用xml格式
# 后来json 格式出现,前后端交互,主流都是json格式
-可能觉得不安全
-前后端加密解密方式
# 目前有些比json更安全,高效,节约空间的编码格式,后期可能前后端交互使用某种
练习
# 购物车带删除,有库存校验 ok
# 小电影案例---》后端用django,解决跨域问题
# 整理一下vue生命周期 ok
---------------------------------
# 对象当参数传递,在函数中修改对象,会不会影响原来的 会 ok
# 深浅拷贝 ok
# 搜索什么是跨域
深浅拷贝
(43条消息) javascript 数组以及对象的深拷贝(复制数组或复制对象)的方法_FungLeo的博客-CSDN博客
(43条消息) JavaScript深拷贝看这篇就行了!(实现完美的ES6+版本)_码飞_CC的博客-CSDN博客_js深拷贝
跨域问题
[跨域问题及CORS解决跨域问题方法 - 腾讯云开发者社区-腾讯云 (tencent.com)]
购物车
有问题:选择1,2,4删除3,价格会错乱。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<link rel="stylesheet" href="./js/bootstrap.css">
<script src="js/layui/layui.js"></script>
</head>
<body>
<div id="app">
<div class="container-fluid">
<div class="row top">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">购物车</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>商品</th>
<th>价格</th>
<th>数量</th>
<th>全选
<input v-model="checkAll" type="checkbox" @change="handleCheckAll">
</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(good,index) in shopCar">
<td>{{good.name}}</td>
<td>{{good.price}}</td>
<td>
<button class=" btn-default" @click="addGood(good)">➕</button>
<button>{{ good.count }}</button>
<button class="btn-default" @click="delGood(good)">➖</button>
</td>
<td :id="good.id">
<input class="btn btn-default" type="checkbox" v-model="goodList" :value="good"
@change="handleCheckOne">
<!-- 不能使用点击事件,因为点击事件快于v-model的双向数据绑定 -->
</td>
<td>
<button class="btn btn-danger" @click="delShopCar(good,index)">删除</button>
</td>
</tr>
</tbody>
</table>
<h4>总价格:{{ getPrice() }}</h4>
<h4>购物车:{{ goodList }}</h4>
</div>
</div>
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
shopCar: [
{id: 1, name: '曹操戏张飞', price: 10, count: 1, store: 5},
{id: 2, name: '仿真玩偶', price: 100, count: 1, store: 5},
{id: 3, name: '印度飞饼', price: 1000, count: 1, store: 5},
{id: 4, name: '仿真玩偶plus', price: 10000, count: 1, store: 5},
],
goodList: [],
checkAll: false,
}, methods: {
getPrice() {
var total = 0
for (item of this.goodList) {
total += item.price * item.count
}
return total
},
handleCheckAll() {
if (this.checkAll) {
this.goodList = this.shopCar.slice(0) // 深浅拷贝 slice(0)返回一个新列表并且在内存中新产生了一份数据 不再引用原来内存中的数据
} else {
this.goodList = []
}
},
handleCheckOne() {
this.checkAll = this.goodList.length === this.shopCar.length;
},
addGood(good) {
if (good.count < good.store) {
good.count++
} else {
layer.msg('超过库存上限')
}
},
delGood(good) {
if (good.count > 1) {
good.count--
} else {
layer.msg('商品数最小为1')
}
},
delShopCar(item, index) { // {id: 1, name: '曹操戏张飞', price: 10, count: 1, store: 5},
if (this.goodList.length === 0) {
this.shopCar.splice(index, 1)
} else {
this.goodList.forEach((good, ix) => {
if (good.id === item.id) {
this.goodList.splice(ix, 1)
}
})
this.shopCar.splice(index, 1)
}
},
},
})
</script>
</body>
</html>
显示电影页面
后端:
# view.py
import json
import os
from django.conf import settings
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
static_path = os.path.join(settings.BASE_DIR, 'static')
class MovieView(ViewSet):
def list(self, request, *args, **kwargs):
with open(f'{static_path}/movie.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return Response({'data': data}, headers={'Access-Control-Allow-Origin': '*'})
# urls.py
from django.contrib import admin
from django.urls import path
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
router.register('movie', views.MovieView, 'movie')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
<script src="./js/axios.js"></script>
<link rel="stylesheet" href="./js/bootstrap.css">
</head>
<body>
<div id="app">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-default" v-for="item in filmList">
<div class="panel-body">
<a href="">
<h4 class="media-heading">{{item.name}}</h4>
</a>
<div class="media">
<div class="media-left">
<a href="#">
<img style="width: 100px;" class="media-object" :src="item.poster" alt="...">
</a>
</div>
<div class="media-body">
{{ item.synopsis }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
// // 根组件
let vm = new Vue({
el: '#app',
data: {
data: '',
filmList: []
},
created() {
axios.get('http://127.0.0.1:8000/movie/').then(res => {
this.filmList = res.data.data.data.films
})
},
})
</script>
</html>