【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该如何绑定?

image-20230214222130981

这样如果选中checkbox,变量isRemeber的值就会变成true

checkbox多选 --- 数组

所有checkbox都使用一个数组:

image-20230214223242499

如:

image-20230214223351666

每次选中checkbox,都会把value值放入数组:

image-20230214223519837

radio单选 --- 字符串

如何对radio单选框进行双向数据绑定?

image-20230214222755119

这里的意思是如果你选了,就会将标签value所对应的值赋值给gender,此时gender:'男'
value需要每个都不一样。

通常会这样表示不同的选项:

image-20230214223106404

给后端发送数据

写一个按钮:

image-20230214223917573

所有的数据都是双向绑定的,并且所有的数据都在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都会导致数组的变化:

image-20230215171405886

需要注意的是,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进行比较,发现页面有变化的地方,针对这些地方进行更新。使得最终页面只有部分发生变化,而不是整体重新渲染。

全选按钮

image-20230216101321413

这个全选按钮和之前的checkbox不能绑定同一个数组,而是使用新的数组。

给全选按钮添加@change事件,点击全选按钮之后,让数组添加所有数据,因为数组的数据是跟页面绑定的,所以只需修改数组即可,前端页面也会发生变化:

image-20230216101621436

再给每个商品的选择按钮添加一个点击事件,用于判断是否需要将全选按钮勾选上:

<!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>

点击事件对应的函数:

image-20230216102027685

商品添加删除

商品加减功能,给按钮绑定点击事件:

image-20230216102208732

@click="item.number++":点击事件后面可以写简单的逻辑代码。

商品的添加不能超过库存:
页面加载好时,就已经获取到商品的库存值,如果添加的商品超过库存,那么就禁止商品添加。

减少商品的时候,会出现负数的问题:

image-20230216102625429

需要添加函数限制一下。将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请求去数据库将购物车的数据删除)

库存校验

添加库存数据:

image-20230216103158232

代码:


# 部分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

示例:

image-20230215183451970

由于数据做了双向绑定,每次只要输入数据,页面都会发生变化(其实页面是在更新的),这样是非常消耗资源的。

而我们通常只想要用户输入的数据,所以这个时候就可以使用v-model.lazy:

image-20230215183806618

此时当input输入框失去焦点的时候,页面才会发生变化:

image-20230215183850381

v-model.number

当数字开头时,会对输入的数字进行双向绑定,增删数字会即时的渲染在页面上;如果后续输入字母,不会对字母渲染:

image-20230215184814679

当字母开头时,就相当于就是普通的input框数据和页面双向绑定了:

image-20230215184844291

v-model.trim

当input输入框带空格时,v-model.trim可以删除首尾的空格,同时对输入数据进行双向绑定:

image-20230215184719869

代码:

<!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是同级的,并且他的名字是固定的,比如:

image-20230215191357472

一个八个,这些钩子函数会依次去执行。

页面一刷新,钩子函数就开始执行:

image-20230215191738598

此时已经挂载完成了,也就是执行到Vue生命周期的第二个过程。

当我们进行了变量的修改,此时页面会更新,也就会执行钩子函数:

image-20230215192318142

如果再次修改,这两个钩子函数还会执行。

当页面被销毁,我们就看不到控制台的输出了,但是页面销毁会执行剩余的两个钩子函数。

全局组件

全局组件如何定义?
使用Vue.component('组件名',{配置项})

image-20230215202007852

Vue.component的第二个参数就相当于Vue()el配置项。
注意:data的写法有些不同,在这个data函数的返回值的对象里写组件相关的数据。

为什么组件的数据使用data()函数存放?

组件可以使用多次,每个组件用自己的数据,每个组件都是用的函数返回的数据(每次都返回一个新的对象)。如果不是函数,是对象,相当于每次调用组件都使用的是同一份数据(同一个对象)。

将8个钩子函数放入全局组件:

image-20230215202131737

template怎么写?

image-20230215202337932

template里写html代码,组件内data内的数据,可以使用插值语法渲染在组件内。

如何将组件放在我们原来的html页面?

image-20230215202602396

相当于自定义了一个标签<child></child>,会把template的内容渲染到child标签的位置。

组件template内的名字和组件外的名字是可以重复的,不会互相影响。

实现组件的显示和消失(在根组件写代码):

image-20230215203330526

组件有自己的样式、自己的事件。

根组件和子组件:

image-20230216160019918

组件如何更新?
在组件的methods部分写事件相关函数。
数据更新时,会执行组件的更新相关的两个钩子函数。

image-20230215203838592

但使用根组件让子组件销毁,会执行子组件的销毁相关钩子函数:

image-20230215203954234

在组件created方法向后端发送ajax

为什么要在created?

# 8 学习生命周期重点掌握的
	-1 组件向后端发送请求,获取数据,应该放在 created 写,此时data已经有数据了
    -2 destroyed做一些资源清理性的工作

子组件有自己的数据,数据是从后端加载过来的,子组件是什么时候向后端发送请求获取数据的?是在生命周期的哪个位置?

应该在created函数执行时。data数据已经获取了,挂载还没有进行,所以el还不存在。

this是当前子组件:

image-20230215204353021

image-20230215205050479

查看前端:

image-20230215204811952

  • beforeCreate时,data和el都没有.
  • created时,data有了,但是由于还未挂载所以el没有

image-20230215204832181

  • beforeMount:此时data有了,el还未挂载
  • mounted: 终于el挂载到template

注意:前端向后端发送ajax请求放在created函数里,不要放在beforeCreate此时前端数据不存在。ajax请求在mounted也可以,如果前端加载很慢的话,前端可能会先显示一个默认值,然后再显示从后端接受到的数据。数据变化之后,页面会刷新。

destroyed资源清理

destroyed做一些资源清理性的工作:

如果在组件的created中执行了一些任务,那么当组件销毁了,可能这些任务还没销毁,这种情况就不太合适。所以就需要使用destroyedbeforeDestory做一些资源清理的工作。

组件销毁时,销毁定时器:

image-20230216154224297

代码:

<!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之后执行

image-20230215193817536

setInterval定时任务:每隔3s执行一次

image-20230215193920004

在线聊天室案例

# 实现实时聊天效果(在线聊天室)
	-轮询:定时器+ajax   http:http版本区别
    -长轮询:定时器+ajax  http
    -websocket协议:服务端主动推送消息
    -https://zhuanlan.zhihu.com/p/371500343
    
# 扩展
	-http websocket都是基于tcp 
	-http版本区别 1.1 1.9 2.0 

什么场景下用定时任务?

扫二维码登录微信时。比如初次扫码的时候,会有一个请求是pending状态:

image-20230216154638676

浏览器在等待你确认登录时,会一直向服务端发送请求:image-20230215194326122

使用手机扫二维码之后,手机会向微信的服务器发送登录相关的请求,但是浏览器不知道你发送了这个请求,所以浏览器需要一刻不停的向后端发送请求(轮询),以验证当前用户是否扫码。如果发现用户扫了码,浏览器这个定时任务的请求,就会把用户头像数据带回来,显示在页面上。

也就是说,数据不是服务器主动发送给客户端的,而是客户端一直发送定时任务,主动去拿的。(类似于拿快递,服务器是寄存点)(这都是因为http协议的特性:无状态、无连接)

比如: A ---> B A向B发送消息
实际上是: A --发送信息--> 服务器
B --查询信息--> 服务器

示例:
页面一直发送定时任务,查询是否有新增的消息。如果有新增的消息,将消息带回来,进行页面更新。

image-20230215200443530

秒杀案例

什么场景下使用延时任务?

image-20230215200708140

先提交秒杀请求,每隔3s,查询是否秒到。

首先,在组件中开启一个定时器(存放在data()):

image-20230215201145393

全局组件:

image-20230215201434779

组件销毁了之后,定时器还是在运行,这显然不合适,所以需要在组件的destroyed() 钩子函数中将定时器销毁:

image-20230215211830854

与后端交互

image-20230215214502655

使用jquery的ajax与后端交互

需求:点击按钮,数据加载,页面更新。

image-20230215212526875

Vue:

image-20230215212118958

点击事件函数:

image-20230215212734559

使用jquery封装的ajax方法。

创建flask服务端:

image-20230215212831822

只要有请求访问根路径,就会返回数据。

跨域问题

# 跨越问题
	-浏览器的原因,只要向不是地址栏中的 [域:地址和端口]发送请求,拿的数据,浏览器就给拦截了
       
# 处理跨域问题
	-后端代码处理----》只需要在响应头中加入允许即可  

前端点击按钮,会向后端发送请求,获取数据,但是发送了跨域请求报错:

image-20230215213012962

解决跨域问题需要修改后端代码,需要在响应头里加一句话:

image-20230215213109660

示例:

image-20230215213150749

前端渲染数据:

image-20230215213440993

注意从后端获取到的数据是json字符串,需要通过JSON.parse转换成对象。

fetch和axios发送请求

image-20230215214821007

jquery不仅封装了ajax,还有很多的dom操作,而axio只能发请求。
axio使用promsie风格,promsie风格是es6的新语法。

promsie风格是为了解决如下嵌套发送请求的情况,优化代码:image-20230216151019446

fetch

# fetch 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应
	-新的发送ajax 接口
    -用起来比较方便
    -支持promise写法[最新的异步写法]
    -解决了原生的XMLHttpRequest兼容性的问题
    -不是所有浏览器都支持
    -主流现在是用axios[第三方]发送请求
# XMLHttpRequest: 原生js提供的
	-比较老,不同浏览器需要做一些兼容性的处理,写起来比较麻烦
    -jq基于它做了封装
    
'''大公司会基于 javascript XMLhttprequest 或者 fetch 进行封装来使用'''

fetch是js原生的,所以可以直接使用:

image-20230215215105857

第一个then会返回一个数据,传给第二个then

查看前端:

image-20230215215135864

渲染数据:

image-20230215215210385

axios

#  axios是在Vue上的第三方的模块
#  Axios 是一个基于 promise 的 HTTP 库,还是基于XMLHttpRequest封装的

官网:起步 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

使用cdn引入axios:

image-20230215215557868

只要引入了,就可以在Vue组件里使用!

示例:

image-20230215215704271

查看前端:

image-20230215215750177

后端返回的数据在res.data中。

修改数据:

image-20230215215846280

代码:


<!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>

显示电影案例

需求:后端加载电影相关数据,前端显示。

数据获取:

image-20230215220216348

把数据放置在本地文件。

后端代码:

image-20230215220441403

在前端created方法内,向后端获取数据:

image-20230215220638710

循环展示电影:json.cn

image-20230215220920181

如果后端返回的数据格式嵌套的层级比较多,前端需要一层一层的点出来:

image-20230216115707083

前端还需要判断一下状态码是否为100,然后再将后端获取到的数据进行赋值。

有的公司觉得后端使用json格式返回不安全,比如接口返回出了患者的身份证号。需要对数据进行加密。后端在中间件中解密获取数据,再加密返回给前端。但是这种加密十分影响接口的效率,通常只对重要的接口使用。

示例:image-20230216121642160

进行了数据的加密:

image-20230216121710800

代码:

<!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组件

# 组件化开发的好处:重用代码
# 组件分类
	-全局组件:在任意组件中都可以使用
    -局部组件:只能在当前组件中使用

示例:

image-20230216160043363

组件化之后可复用代码:

image-20230216160106093

组件的分类:全局组件和局部组件。

全局组件在任意组件中都可以使用。
在组件中也可以使用全局组件:

image-20230216160615078

局部组件只能在当前组件中使用。

定义局部组件:

image-20230216160846128

使用components定义局部组件。

局部组件只能在Vue对象监控的代码内使用:

image-20230216161151252

可以将组件的代码放在变量里:

image-20230216161252010

这样在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>
posted @ 2023-02-16 21:52  passion2021  阅读(327)  评论(0编辑  收藏  举报