第二十七章:vue

一:VUE介绍

1.前端发展介绍

# HTML(5)、CSS(3)、JavaScript(ES5、ES6、ES13):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 -> 在浏览器中查看
    # ECMA标准
    # JavaScript=ECMA+bom+dom
    
# Ajax 的出现 -> 后台发送异步请求,Render+Ajax混合
# 单用 Ajax(加载数据,DOM渲染页面):前后端分离的雏形
# Angular 框架的出现(1个JS框架):出现了“前端工程化”的概念(前端也是1个工程、1个项目)
# React、Vue 框架:当下最火的2个前端框架(Vue:国人喜欢用,React:外国人喜欢用)
# 移动开发(Android+IOS) + Web(Web+微信小程序+支付宝小程序) + 桌面开发(Windows桌面):前端 -> 大前端
# 一套代码在各个平台运行(大前端):谷歌 Flutter(Dart 语言:和 Java 很像)可以运行在IOS、Android、PC端
# 在 Vue 框架的基础性上 uni-app:一套编码,编到 10 个平台
# Vue:2.x  3.x
	-ts:typescript
	-less
    -官网:https://cn.vuejs.org/
    -     https://v2.cn.vuejs.org/
    
# 在不久的将来 ,前端框架可能会一统天下

# 主流:vue、react、uni-app
	-typescript

2.Vue的快速使用

# Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架
	-可以一点一点地使用它,只用一部分,也可以整个工程都使用它
    
# 开发 vue,有一款编辑器
	-vscode:微软 轻量级编辑器,免费
    -vim:装插件,搞得花里胡哨,开发代码
    -Jetbrains:IDEA(java),Pycharm,Goland,phpStrom,webstorm   收费 吃内存(java开发)
    -AndroidStadio:免费,谷歌买了Jetbrains授权+ADT
    	-sun:java---》后来被甲骨文收购了--->oracle jdk   openjdk  毕昇jdk
        
    -Pycharm + 插件  开发vue 使用起来跟webstorm
    	-把vue的源代码下载到本地
        
        
 # M-V-VM思想
	- MTV :django
    - MVC :后端框架一般基于这种架构
    - MVVM:前端框架 vue
    - MVP:移动端
    
# 组件化开发、单页面开发
	-组件化开发:有自己独立的html,css,js
    -单页面应用(SPA)
      -只有一个html页面
       
# vue 2 基础没有大差距,会讲 vue 3
# 下载源码放到项目中了
<!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>
    <p>名字:{{name}}</p>
    <p>年龄:{{age}}</p>

</div>


</body>
<script>
    // 引入vue后,会有Vue的构造函数,传入 配置项 对象
    // 页面中id为app的div就被vue托管了,在div中就可以写vue的语法,指令
    var vm = new Vue({
        el: "#app",
        data: {
            name: 'lqz',
            age: 19
        },
    })
</script>

</html>

二:基础语法与概念

1.插值语法

# 被 vue 托管的标签中可以写 {{}}   中可以写:变量,js简单的表达式,函数
<!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>
    <p>名字:{{name}}</p>
    <p>年龄:{{age}}</p>
    <p>爱好:{{hobby}}</p>
    <p>第0个爱好:{{hobby[0]}}</p>
    <p>wife:{{wife}}</p>
    <p>wife的名字:{{wife['name']}}</p>
    <p>wife的年龄:{{wife.age}}</p>
    <p>标签:{{a}}</p>
    <h1>vue插值渲染简单表达式</h1>
    <p>{{10 > 9 ? '大于' : '小于'}}</p>
    <p>{{age+1}}</p>

    <h1>渲染函数,后面讲</h1>

</div>


</body>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            name: 'lqz',
            age: 19,
            hobby: ['篮球', '足球', '乒乓球'],
            wife: {name: '刘亦菲', age: 38},
            a: '<a href="http://www.baidu.com">点我</a>'
        },
    })


    // 三目运算符----》三元表达式
    // var a = 10 > 9 ? '大于' : '小于'
</script>

</html>

2.文本指令

# 写在[任意]标签上 以  v-xx  开头的,都是vue的指令
# 文本指令
	v-text:把变量渲染到标签中,如果之前有数据,覆盖掉
    v-html:如果是标签字符串,会把标签渲染到标签内
    v-show:控制标签的显示与隐藏,但是它是通过style的display控制的:style="display: none;"
    v-if:控制标签的显示与隐藏,但是它是通过dom的增加和删除
<!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-text</h1>
    <p v-text="name">彭于晏</p>
    <h1>文本指令 v-html</h1>
    <span v-html="a"></span>
    <h1>v-show</h1>
    <img v-show='b' src="https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg" alt="" width="300px" height="300px">

    <h1>v-if</h1>
    <div v-if="b1">
        <a href="">点我看美女</a>
        <h3>美女</h3>
    </div>

</div>


</body>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            name: 'lqz',
            a: '<a href="http://www.baidu.com">点我</a>',
            b:true,
            b1:true
        },
    })

</script>

</html> 

3.事件指令 v-on

# 放在标签上:v-on:事件名='函数'
# 事件名可以写:click,dbclick, input标签:change,blur,input
# 补充:es6 对象写法
    var name = 'lqz'
    var age = 19
    // var obj={name:name,age:age}
    var obj = {name, age, handleClick() {}}
    
# v-on:事件名='函数'  简写成  @事件名='函数'
<!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>

    <button v-on:click="handleShow">点我显示消失</button>
    <div v-if='show'>
        <img src="https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg" alt="" width="300px"
             height="300px">

    </div>
    <h1>事件指令函数带参数</h1>
    <button @click="handleClick">函数需要参数,但是没传,默认会把event事件传入</button>
    <br>
    <button @click="handleClick1('lqz')">函数需要参数,但是传值了</button>
    <br>
    <button @click="handleClick2('lqz')">函数需要3参数,但是传了1个</button>
    <br>
    <button @click="handleClick3($event,'lqz')">函数需要2参数,一个事件,一个字符串</button>
</div>


</body>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            show: true,
        },
        methods: {
            // handleShow: function () {
            //     // this 就是vm对象 vm.show
            //     this.show = !this.show
            // },
            //es6的语法,对象写法,以后都这样写
            handleShow() {
                // this 就是vm对象 vm.show
                this.show = !this.show
            },
            // 需要一个参数,但是没有传,会把事件传入
            handleClick(a) {
                console.log(a)
                alert('帅哥')
            },
            // 需要1个,传了一个,但是需要传数字,字符串,布尔,或变量
            handleClick1(name) {
                console.log(name)
            },
            // 需要3个参数,传了一个,没问题,后面俩都是 undefined
            handleClick2(a, b, c) {
                console.log(a)
                console.log(b)
                console.log(c)

            },
            handleClick3(event,name){
                console.log(event)
                console.log(name)

            }


        }
    })


    // es5中
    // var obj={'name':'lqz','age':19}
    // var obj={name:'lqz',age:19}
    // es6中
    var name = 'lqz'
    var age = 19
    // var obj={name:name,age:age}
    var obj = {
        name, age, handleClick() {
        }
    }


</script>

</html>

4.属性指令 v-bind

# 写在标签上的 name,class,href,src,id.....属性
# v-bind:属性名='变量'
# 简写成:
v-bind:属性名='变量'         :属性名="变量"
<!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>
    <button @click="handleClick">点我看美女</button>
    <br>
    <img v-bind:src="url" alt="" width="300px" height="300px">
    <hr>
    <button @click="handleChange">点我换美女</button>
    <br>
    <img :src="img_url" alt="" width="300px" height="300px">
    <hr>
    <img :src="img_url2" alt="" width="300px" height="300px">
    <hr>

    <div :id="id_div">
        我是div
    </div>
</div>


</body>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            url: 'https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg',
            img_url: 'https://p.qqan.com/up/2022-8/202283919254843.jpg',
            img_url2: 'https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg',
            imgList: ['https://img2.woyaogexing.com/2022/10/23/af963193e9fb67ee!400x400.jpg',
                'https://img2.woyaogexing.com/2022/10/22/95afdd5cd39d556d!400x400.jpg',
                'https://img2.woyaogexing.com/2022/10/21/f06c65142fe50c19!400x400.jpg',
                'https://img2.woyaogexing.com/2022/10/20/c0135f7e74050a74!400x400.jpg',
                'https://img2.woyaogexing.com/2022/10/20/5972f09505ac681d!400x400.jpg'],
            id_div:'xxx'
        },
        methods: {
            handleClick() {
                this.url = 'https://p.qqan.com/up/2022-8/202283919254843.jpg'

            },
            handleChange() {
                // floor:只取整数部分
                //Math.random() 生成0--1直接的数字,小数
                var i = Math.floor(Math.random() * this.imgList.length)
                console.log(i)
                this.img_url = this.imgList[i]
            }
        },
        mounted() {
            // 页面加载完,开启一个定时器,每隔3s干函数内容,函数里面在变化变量
            setInterval(() => {
                var i = Math.floor(Math.random() * this.imgList.length)
                this.img_url2 = this.imgList[i]
            }, 1000)
        }
    })


</script>

</html>

5.vue生命周期

# var vm=new Vue实例()
	-1 实例创建,数据放到实例中
    -2 挂在模板:el ---》div
    -3 改页面,改变量,都会相互影响 update
    -4 销毁实例
    
# 4 个过程,对应八个函数,依次执行(到某个过程就会执行某个函数)
	beforeCreate	创建Vue实例之前调用,data,el 都没有
    created	        创建Vue实例成功后调用(可以在此处发送异步请求后端数据),data 有了,el 没有的
    beforeMount	    渲染DOM之前调用 ,data有了,el 没有
    mounted	        渲染DOM之后调用
    beforeUpdate	重新渲染之前调用(数据更新等操作时,控制DOM重新渲染)
    updated	        重新渲染完成之后调用
    beforeDestroy	销毁之前调用
    destroyed	    销毁之后调用

# 5 钩子函数(hook)  AOP的体现:面向切面编程--》装饰器实现aop

# 6 vm实例:看不到它销毁 组件vc

# 7 组件:组件化开发

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

# 9 小案例:组件创建,开启定时器,不停的打印 hello,在 destroyed 中对定时器进行销毁
	-补充:js 定时任务和延时任务   
    # 延时任务
    setTimeout(()=>{
       console.log('3s后执行我')
    },3000)
    #定时任务
   setInterval(()=>{
     console.log('hello')
   },3000)

# 10 什么场景下用定时任务?
	1 实时跟后端交互  基于 http+ 定时任务 (websocket 协议:服务端主动推送消息,手机 app 的消息推送)
    2 秒杀场景:先提交秒杀请求,每隔 3s,查询是否秒到
   
<!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>

6.箭头函数

ES6 的箭头函数写法 ---》函数中套函数,this 指向有问题,有了箭头函数,箭头函数没有自己的 this,用的都是上一级的 this

<script>
    // 1 无参数,无返回值箭头函数
    // var f = () => {
    //     console.log('函数')
    // }
    
    // 2 有一个参数,没有返回值的箭头函数  括号可以去掉,可以加
    // var f = item => {
    //     console.log(item)
    // }
    
    // 3 有多个参数,没有返回值的箭头函数  括号不能去掉
    // var f = (item, key) => {
    //     console.log(item)
    // }

    // 4 有一个参数,一个返回值
    // var f =  (item)=> {
    //     return item + 'lqz'
    // }

    // var f = item => {
    //     return item + 'lqz'
    // }
	
    // 5 唯一参数 唯一返回值,终极版
    var f = item => item + 'lqz'
    var res = f('lqz')
    console.log(res)
</script>

7.迭代和索引循环

# python 中只有基于迭代的循环,没有基于索引的循环
# js,java,go中有基于迭代和索引的两种
# 补充:js 中 for 循环
	1 for(i=0; i<checkGroup.length; i++)     # 基于索引的循环
    2 for (i in checkGroup)                # 基于迭代的循环
    3 for (i of checkGroup)                # es6中 循环
    4 数组内置方法.forEach()
    5 jquery  $.each 循环
    
# 代码演示
    // 1 方式一:js的基于索引的循环
    // for (var i = 0; i< goodList.length; i++) {
    //     console.log(goodList[i])
    // }
    // 2 方式二:基于迭代的循环
    // for (i in goodList){
    //     console.log(goodList[i])
    // }

    // 3 方式三:of 循环,基于迭代的
    //   for (i of goodList){
    //     console.log(i)
    // }
    // 4 方式四:数组的循环方法
    // goodList.forEach(item => {
    //     console.log('---', item)
    // })

    // 5 jquery:引入
    // $.each(goodList, (i, v) => {
    //     console.log(v)
    // })

三:基础演示

1.class和style绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求。

因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。

不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 classstyle 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

1.1 绑定HTML Class

1.1.1 对象语法

1、:class 存在与否将取决于数据 property isActive 的 true/false。

2、:class中可以设置多条数据

3、v-bind:class 指令也可以与普通的 class attribute 共存。当有如下模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <style>
        .active {
            background-color: red;
            height: 200px;
            width: 200px;
        }

        .test {
            font-size: 30px;
        }

        .font_color {
            color: aquamarine;
        }
    </style>
</head>
<body>
<div id="app">
    <div :class="{ active: isActive, font_color: isFontColor }" class="test">
        这是一个 DIV
    </div>
</div>
</body>
<script>
    var watchExampleVM = new Vue({
        el: '#app',
        data: {
            isActive: true,
            isFontColor: false,
        }
    })
</script>
</html>

我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <style>
        .active {
            background-color: red;
            height: 200px;
            width: 200px;
        }

        .test {
            font-size: 30px;
        }

        .font_color {
            color: aquamarine;
        }
    </style>
</head>
<body>
<div id="app">
    <div :class="classObj" class="test">
        这是一个 DIV
    </div>
</div>
</body>
<script>
    var watchExampleVM = new Vue({
        el: '#app',
        data: {
            isActive: true,
            isFontColor: false,
            err: 'null'
        },
        computed:{
            classObj: function () {
                return {
                    active: this.isActive && !this.err
                }
            }
        }
    })
</script>
</html>

1.1.2 数组语法【推荐】

我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:

<div :class="['active', 'font_color']" class="test">
    这是一个 DIV
</div>

<style>
    .active {
        background-color: red;
        height: 200px;
        width: 200px;
    }

    .test {
        font-size: 30px;
    }

    .font_color {
        color: aquamarine;
    }
</style>

如果你也想根据条件切换列表中的 class,可以用三元表达式:

<div :class="[isActive ? 'active': '', isFontColor ? 'font_color': '']" class="test">
    这是一个 DIV
</div>
data: {
    isActive: true,
    isFontColor: false,
    err: 'null'
},

在数组语法中也可以使用对象语法

<div :class="[{active: isActive}, isFontColor ? 'font_color': '']" class="test">
    这是一个 DIV
</div>
data: {
    isActive: true,
    isFontColor: false,
    err: 'null'
},

1.1.3 用在组件上

Null

1.2 绑定内联样式

1.2.1 对象语法

v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 来命名:

PS:就用驼峰式

1)直观式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div :style="{backgroundColor: isblackColor, height: isheight + 'px', width: iswidth}" >
        这是一个 DIV
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            isblackColor: 'red',
            isheight: '200',
            iswidth: '200px',
            isFontColor: false,
            err: 'null'
        },
    })
</script>
</html>
2)绑定样式对象【推荐】

直接绑定到一个样式对象通常更好,这会让模板更清晰:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div :style="styleObj" class="test">
        这是一个 DIV
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            styleObj:{
                height: '200px',
                width: '100px',
                color: 'red',
                backgroundColor: '#fb7299'
            }
        },
    })
</script>
</html>

1.2.2 数组语法

v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div :style="[styleObj, styleObj2]" class="test">
        这是一个 DIV
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            styleObj:{
                height: '200px',
                width: '100px',
                color: 'red',
                backgroundColor: '#fb7299'
            },
            styleObj2:{
                lineHeight:'200px'
            }
        },
    })
</script>
</html>

2.条件渲染

2.1 v-if

2.1.1 显示与隐藏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    {{score}}
    <div v-if=score>
        <li>12</li>
        <li>12</li>
        <li>12</li>
        <li>12</li>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            score: Math.random() > 0.5 ? true : false
        }
    })
</script>
</html>

2.1.2 判断值

# 写在标签上,控制标签的显示与不显示
v-if='布尔值/运算完是布尔值'
v-else-if='布尔值/运算完是布尔值'
v-else
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <h2 v-if="score > 0.85"> 优秀:{{score}}</h2>
        <h2 v-else-if = "score < 0.85 && score >= 0.75">良好:{{score}}</h2>
        <h2 v-else-if = "score < 0.75 && score >= 0.6">及格:{{score}}</h2>
        <h2 v-else>不及格:{{score}}</h2>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            score: Math.random()

        },
        created:function () {
            console.log(this.score)
        }
    })
</script>
</html>

2.2 v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法与 v-if 大致一样:

<div id="app">
    {{score}}
    <div v-show=score>
        <li>12</li>
        <li>12</li>
        <li>12</li>
        <li>12</li>
    </div>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            score: Math.random() > 0.5 ? true : false
        }
    })
</script>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

PS:注意,v-show 不支持 <template> 元素,也不支持 v-else

2.3 v-if VS v-show

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

2.4 v-if 与 v-for 一起使用

不推荐同时使用 v-ifv-for。请查阅风格指南以获取更多信息。

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。请查阅列表渲染指南以获取详细信息。

3.列表渲染

3.1 可循环变量

# v-for 可以循环数组,数字,字符串,对象
	v-for="key in obj"
    	-如果是数组:key就是数组的一个个元素
        -如果是数字:key就是从1开始的一个个数字
        -如果是字符串:key就是一个个字符
        -如果是对象:key就是一个个value的值
	v-for="(key,value) in obj" 
		-如果是数组:key就是数组的一个个元素,value就是索引
        -如果是数字:key就是从1开始的一个个数字,value就是索引
        -如果是字符串:key就是一个个字符,value就是索引
        -如果是对象:key就是一个个value的值,value就是一个个key
        
        
        
 # 每次循环的标签上,一般都会带一个属性:  :key='值必须唯一'
	-key值得解释:为了加速虚拟dom的替换

3.1.1 列表

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <ul v-for="item in list" :key = item.id>
            <li>{{item}}</li>
        </ul>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            list: ['蹴鞠', '围棋', '弹棋', '投壶', '射箭', '斗兽', '赛马', '赛车', '秋千', '拔河', '竞渡'],
        }
    })
</script>
</html>

3.1.2 对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <ul v-for="(item, index) in dit" :key = item.id>
            <li>{{ index }} - {{ item }}</li>
        </ul>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            dit: {
                name: 'ysg',
                age: '16',
                sex: '男'
            }
        }
    })
</script>
</html>

3.1.3 数字

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <ul v-for="item in num" :key = item.id>
            <li>{{item}}</li>
        </ul>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            num: 8
        }
    })
</script>
</html>

3.2 数组更新检测

3.2.1 变更方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

你可以打开控制台,然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({ message: 'Baz' })

3.2.2 替换数组

变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如 filter()concat()slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

3.2.3 注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。深入响应式原理中有相关的讨论。

3.2.4 示例:

<!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>
    <button @click="handleClick">点我追加女生</button>
    |
    <button @click="handleClick1">点我追加一批女生</button>|
    <button @click="handleClick4">点我修改数组页面变化</button>|
    <p v-for="girl in girls">{{girl}}</p>

    <h1>对象的检测与更新</h1>
    <button @click="handleClick2">点我追加身高</button>
    |
    <button @click="handleClick3">点我追加身高--解决</button>
    |
    <p v-for="(value,key) in obj">{{key}}---{{value}}</p>


</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            girls: ['刘亦菲', '迪丽热巴', '杨超越', '刘诗诗'],
            obj: {'name': 'lqz', age: 19}
        },
        methods: {
            handleClick() {
                this.girls.push('美女1号')
            },
            handleClick1() {
                var a = this.girls.concat(['美女99号', '美女88号', '美女77号'])
                console.log(a)
            },
            handleClick2() {
                this.obj.height = '180'  // 监控不到变化
                console.log(this.obj)
            },
            handleClick3() {
               Vue.set(this.obj,'height',180)  // 监控到变化了
            },
            handleClick4(){
                Vue.set(this.girls,0,'sdasdfas')
            }
        }
    })


</script>
</html>

4.事件处理

为什么在 HTML 中监听事件?

你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:

1、扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。

2、因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。

3、当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

4.1 事件绑定

1、在 methods 对象中定义方法

2、this 在方法里指向当前 Vue 实例

3、有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <!--  有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法  -->
        <button @click="handleClick($event)">显示运动</button>
        <ul v-for="(item, index) in list" :key=item.id v-if=isif>
            <li>{{ index }} - {{ item }}</li>
        </ul>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            isif: false,
            list: ['蹴鞠', '围棋', '弹棋', '投壶', '射箭', '斗兽', '赛马', '赛车', '秋千', '拔河', '竞渡']
        },
        // 在 `methods` 对象中定义方法
        methods: {
            handleClick: function(event){
                // `this` 在方法里指向当前 Vue 实例
                this.isif = !this.isif
                console.log(event)
                console.log(this.isif)
        }
    }

    })
</script>
</html>

4.2 事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop 只处理自己的事件,不向父控件冒泡
  • .prevent 阻止a链接的跳转
  • .capture
  • .self 只处理自己的事件,子控件冒泡的事件不处理
  • .once 事件只会触发一次(适用于抽奖页面)
  • .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

PS:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。

因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

1)版本 2.1.4 新增

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

2)版本 2.3.0 新增

Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

这个 .passive 修饰符尤其能够提升移动端的性能。

passive 主要用在移动端的 scroll 事件,来提高浏览器响应速度,提升用户体验。

因为 passive=true 等于提前告诉了浏览器,touchstart 和 touchmove 不会阻止默认事件,手刚开始触摸,浏览器就可以立刻给与响应;否则,手触摸屏幕了,但要等待 touchstart 和 touchmove 的结果,多了这一步,响应时间就长了,用户体验也就差了。

PS:不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你想阻止事件的默认行为。

3)示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>事件修饰符---不添加修饰符</h3>
    <ul @click="handleUl">ul
        <li @click="handleLiA">AAAAA</li>
        <li @click="handleLiB">BBBBB</li>
    </ul>
    <h3>事件修饰符---stop---阻止事件冒泡</h3>
    <ul @click="handleUl">ul
        <li @click.stop="handleLiA">AAAAA</li>
        <li @click="handleLiB">BBBBB</li>
    </ul>
    <h3>事件修饰符---self---阻止事件冒泡</h3>
    <ul @click.self="handleUl">ul
        <li @click="handleLiA">AAAAA</li>
        <li @click="handleLiB">BBBBB</li>
    </ul>
    <h3>事件修饰符---prevent---阻止 a 链接的跳转</h3>
    <a href="https://www.baidu.com">百度</a>
    <a href="https://www.baidu.com" @click.prevent="handleLiC">阻止向百度链接跳转</a>
    <h3>事件修饰符---once---执行一次</h3>
    <button @click="handleLiD">不阻止</button>
    <button @click.once="handleLiE">EEEEE</button>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        // 在 `methods` 对象中定义方法
        methods: {
            handleUl(){
                console.log('ul')
            },
            handleLiA(){
                console.log('AAAAA')
            },
            handleLiB(){
                console.log('BBBBB')
            },
            handleLiC(){
                console.log('a')
            },
            handleLiD(){
                console.log('DDDDD')
            },
            handleLiE(){
                console.log('EEEEE')
            }
        }
    })
</script>
</html>

4.3 按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。

4.3.1 按键码

keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。

使用 keyCode attribute 也是允许的:

<input v-on:keyup.13="submit">

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名

// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

4.3.2 示例

<!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>按键修饰符---enter</h1>
    <input type="text" @keyup.enter="hankleEnter" v-model="info" placeholder="请输入搜索内容">  --- {{info}}
    <h1>按键修饰符---esc</h1>
    <input type="text" @keyup.space="hankleEnter2" v-model="info2" placeholder="请输入搜索内容">  --- {{info2}}
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            info: '',
            info2: ''
        },
        methods: {
            hankleEnter(val){
                console.log(val.code);
                if(val.code == 'Enter'){
                    console.log('回车键', val)
                }
            },
            hankleEnter2(val){
                console.log(val.code);
                if(val.code == 'Space'){
                    console.log('空格', val)
                }
            }
        }
    })
</script>
</html>

4.4 系统修饰键

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

  • .ctrl
  • .alt
  • .shift
  • .meta

注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。

例如:

<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

PS:请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCodekeyup.17

4.4.1 .exact 修饰符

2.5.0 新增

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

4.4.2 示例

<!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>按键修饰符---enter</h1>
    <button v-on:click.ctrl.exact="onCtrlClick">A</button>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        methods: {
            onCtrlClick(val){
                console.log('Ctrl + 点击');
            }
        }
    })
</script>
</html>

4.5 鼠标按钮修饰符

2.2.0 新增

  • .left
  • .right
  • .middle

这些修饰符会限制处理函数仅响应特定的鼠标按钮。

4.6 input事件

# 事件绑定 v-on:事件名='函数'---》@事件名='函数'
# input 也有很多事件
	-blur:失去焦点触发
    -change:发生变化触发
    -input:输入触发
<!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>input的事件处理</h1>
    <h2>blur</h2>
    <p><input type="text" v-model="name1" @blur="handleBlur"> ---->{{name1}}</p>
    <h2>change</h2>
    <p><input type="text" v-model="name2" @change="handleChange"> ---->{{name2}}</p>
    <h2>input</h2>
    <p><input type="text" v-model="name3" @input="handleInput"> ---->{{name3}}</p>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            name1: '',
            name2: '',
            name3: ''
        },
        methods: {
            handleBlur() {
                console.log('失去焦点了,触发了', this.name1)
            },
            handleChange() {
                console.log('发生变化,触发了', this.name2)
            },
            handleInput() {
                console.log('输入了内容,触发了', this.name3)
            }
        }
    })
</script>
</html>

4.7 过滤案例

<!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="search" placeholder="请输入要搜索的内容" @input="handleSearch"></p>
    <ul>
        <li v-for="item in newdataList">{{item}}</li>
    </ul>


</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            search: '',
            dataList: [
                'a',
                'at',
                'atom',
                'be',
                'beyond',
                'cs',
                'csrf',
                'd',
                'dddd',
            ],
            newdataList:[
                'a',
                'at',
                'atom',
                'be',
                'beyond',
                'cs',
                'csrf',
                'd',
                'dddd',
            ],
        },
        methods: {
            handleSearch() {
                console.log('搜索的内容是:', this.search)
                // var _this=this
                // 复杂写法
                // this.dataList=this.dataList.filter(item=>{
                //     console.log(this)
                //     // 判断this.search是否在item中,如果在保留,return true,如果不在返回false
                //     if (item.indexOf(this.search)>=0){
                //         return true
                //     }else {
                //         return  false
                //     }
                // })

                // 简单写法
                this.newdataList = this.dataList.filter(item => item.indexOf(this.search) >= 0)
            }
        }


    })


    // 1 补充:数组过滤方法,内置的
    // var l = ['a', 'at', 'atom', 'be', 'beyond', 'cs',
    //     'csrf',
    //     'd',
    //     'dddd',
    // ]
    // // filter数组内置的,需要传一个匿名函数,接受一个参数,会循环的从数组中取出值,传入匿名函数,执行
    // // 匿名函数返回true或false,如果返回true,该值保留,如果返回false该值丢弃
    // l = l.filter(function (item) {
    //     console.log('进来一个值:',item)
    //     return false
    // })
    // console.log(l)


    //2 判断子字符串是否在字符串中
    // var s='tttatom'
    // var a ='a'
    // console.log(s.indexOf(a)>=0)

</script>
</html>

5.表单绑定

5.1 基础用法

5.1.1 双向数据绑定

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

# v-model:数据双向绑定
	-使用 属性指令绑定 :value='变量'  是数据的单向绑定
    - v-model="name" :数据双向绑定
    
# PS:v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 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="password" v-model="password"></p>
    <button @click="handleSubmit">提交</button>{{err}}
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            name: '',
            password: '',
            err:''
        },
        methods: {
            handleSubmit() {
                console.log(this.name, this.password)
                this.err='用户名密码错误'
            }
        }
    })
</script>
</html>

5.1.2 文本与多行文本

<!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>
    <input type="text" v-model="text"> --- {{text}}
    
    <h1>表单绑定---多行文本</h1>
    <textarea v-model="msg" cols="30" rows="10"></textarea>
    <p>显示多行文本</p>
    <p style="white-space: pre-line;">{{msg}}</p>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data:{
            text:'',
            msg:'',
        }
    })
</script>
</html>

5.1.3 复选框与单选按钮

<!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>
    <input type="checkbox" v-model="checkbox"> --- {{checkbox}}

    <h1>表单绑定---多个复选框</h1>
    <input type="checkbox" v-model="checkboxList" value="跳水"> 跳水
    <input type="checkbox" v-model="checkboxList" value="跳高"> 跳高
    <input type="checkbox" v-model="checkboxList" value="跳伞"> 跳伞

    <h1>表单绑定---单选按钮</h1>
    <input type="radio" v-model="radio" value="one"> 第一个
    <input type="radio" v-model="radio" value="two"> 第二个
    <p>单选显示:{{radio}}</p>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data:{
            checkbox:'',
            checkboxList:[],
            radio:''
        }
    })
</script>
</html>

5.1.4 选择框

选择框单选

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。

在 iOS 中,这会使用户无法选择第一个选项,因为这样的情况下,iOS 不会触发 change 事件。

因此,更推荐提供一个值为空的禁用选项 <option value="" disabled>请选择</option>

<!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>表单绑定---选择框单选 1</h1>
    <select v-model="select">
        <option value="" disabled>请选择</option>
        <option>A</option>
        <option>B</option>
        <option value="C">C</option>
    </select>
    <p>选择框单选 1:{{select}}</p>

    <h1>表单绑定---选择框单选 2</h1>
    <p><input type="checkbox" v-model="isRemember"> 记住密码 --- 使用 bool 值</p>
    <p>选择框单选 2:{{isRemember}}</p>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data:{
            select:'',
            isRemember: true
        }
    })
</script>
</html>

选择框多选

<!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>
    <select v-model="selectList" multiple style="width: 50px">
        <option>A</option>
        <option>B</option>
        <option value="C">C</option>
    </select>
    <p>选择框多选:{{selectList}}</p>


    <h1>表单绑定---选择框多选 2</h1>
    <select v-model="selectList2" multiple style="width: 60px">
        <option v-for="lie in infoList" :value="lie.value">{{lie.name}}</option>
    </select>
    <p>选择框单选:{{selectList2}}</p>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            selectList: [],
            
            infoList: [
                {name: 'Tom', value: 'AA'},
                {name: 'kay', value: 'BB'},
                {name: 'roy', value: 'CC'},
            ],
            selectList2: [],
        }
    })
</script>
</html>

5.2 修饰符

lazy:等待input框的数据绑定时区焦点之后再变化
number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留
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">
    <h3>修饰符--- lazy</h3>
    <input type="text" v-model.lazy="lazy"> --- {{lazy}}
    <h3>修饰符--- number</h3>
    <input type="text" v-model.number="number"> --- {{number}}
    <h3>修饰符--- trim</h3>
    <input type="text" v-model.trim="trim"> --- {{trim}}
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data:{
            lazy:'',
            number: '',
            trim: '',
        }
    })
</script>
</html>

5.2.1 .lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件 之后 进行同步

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

5.2.2 .number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

5.2.3 .trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

6.计算属性与监听器

6.1计算属性

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。

然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        <h3>计算属性</h3>
        <p>{{reversedMessage}}</p>
        <p>{{newTime}}</p>
    </div>
</body>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            num:123
        },
        computed:{
            reversedMessage:function () {
                console.log('reversedMessage 执行了一次')
                return 5+9*6+3-96+100+this.num
            },
            newTime:function () {
                return Date.now()
            }
        }
    })
</script>

</html>

6.2.监听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器

这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。

当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

# 监听一个属性的变化,只要它发生变化,就执行一个函数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <span> <button @click="type='人文'">人文</button>|<button @click="type='社科'">社科</button>|<button
            @click="type='地理'">地理</button></span>
    <br>
    {{type}}
</div>
</body>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            type: '人文',
        },
        watch: {
            type(val) {
                console.log('修改后变成了,', val)
                console.log('向后端加载数据了')
            }
        }
    })
</script>
</html>

6.3 计算属性VS监听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性

当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        {{ fullName }}
    </div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',    // 名字
            lastName: 'Bar',    // 姓氏
            fullName: 'Foo Bar' // 全名
        },
        watch:{
            firstName:function (val) {
                this.fullName = val + ' ' + this.lastName
            },
            lastName: function (val) {
                this.fullName = this.firstName + ' ' + val
            }
        }
    })
</script>
</html>

上面代码是命令式且重复的。将它与计算属性的版本进行比较:好得多了,不是吗?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        {{ fullName }}
    </div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',    // 名字
            lastName: 'Bar'      // 姓氏
        },
        computed:{
            fullName:function () {
                return this.firstName + ' ' + this.lastName
            }
        }
    })
</script>
</html>

6.4 计算属性重写过滤案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>查询过滤</h3>
    <input type="text" v-model="sousuo">
    <ul>
        <li v-for="str in newdataList">{{str}}</li>
    </ul>
</div>

</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            sousuo: '',
            dataList: [
                'a',
                'at',
                'atom',
                'be',
                'beyond',
                'cs',
                'csrf',
                'd',
                'dddd'
            ]
        },
        computed:{
            newdataList(){
                // 计算属性一定要return,因为计算属性的值就是咱们return的值
                return this.dataList.filter(item => item.indexOf(this.sousuo) >= 0)
            }
        }
    })
</script>
</html>

四:组件

1.组件基础

1.1 组件的组织

# 扩展 HTML 元素,封装可重用的代码,目的是复用
	-例如:有一个轮播,可以在很多页面中使用,一个轮播有 js,css,html
	-组件把 js,css,html 放到一起,有逻辑,有样式,有 html
    
# 定义组件 ()
	-全局组件:全局可以使用,可以用在任意其它组件中
    -局部组件:局部组件只能在定义的位置(组件中)使用

通常一个应用会以一棵嵌套的组件树的形式来组织:

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

Vue.component('my-component-name', {
  // ... options ...
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

1.2 创建组件

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-one>

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

data 必须是一个函数

当我们定义这个 <button-one> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data(){
    return{
        count: 0
    }
}

如果 Vue 没有这条规则,点击一个按钮就可能会影响组件的复用

创建的组件如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="app">
    <h3>组件</h3>
    <button-one></button-one>
    <!-- 组件的复用 -->
    <button-one></button-one>
    <button-one></button-one>
</div>

</body>
<script>
    Vue.component('button-one',{
        // data 必须是一个函数
        data(){
            return{
                count: 0
            }
        },
        // 创建组件, 并绑定事件
        template: `
            <button @click="count++" > 点击 +1 事件  --- {{this.count}} </button>
        `
    });


    var vm = new Vue({
        el: '#app'
    })
</script>
</html>

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

根元素

组件模板应仅包含一个根元素,即组件中都应该有一个像 <div></div> 标签包裹住整个组件的所有内容

1.2.1 全局组件

到目前为止,我们只用过 Vue.component 来创建组件

Vue.component('my-component-name', {
  // ... 选项 ...
})

这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。比如:

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })

在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。

<div id="app">
  <component-a></component-a>
  <component-b></component-b>
  <component-c></component-c>
</div>

1.2.2 局部组件

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

然后在 components 选项中定义你想要使用的组件:

new Vue({
  el: '#app',
  components: {
      ComponentA,
      ComponentB,
      ComponentC
  }
})
var foo={
    template: `
        <div>
            <h1>局部组件---{{ age }}</h1>
        </div>`,
    // data 必须是一个函数,且有 return 值
    data() {
        return {
            age: 19
        }
    }
}

var vm = new Vue({
    ...
    components: {
        foo
    }
})

1.2.3 其它文件中的组件

例如,在一个假设的 ComponentB.jsComponentB.vue 文件中引入其它文件中的组件,需要在局部注册之前导入每个你想使用的组件:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

2.组件间通信

组件间数据不共享,因此需要进行数据传递

父传子:自定义属性

子传父:自定义事件

2.1 父传子:props

父组件传值给子组件:通过自定义属性的方式

场景:子组件中存在值 name ,父组件中存在值 age ,现需将父组件中的 age 传入子组件中使用

2.1.1 使用流程

1、创建父组件与子组件,并设置相应的值

2、父组件中使用 components 注册子组件

3、子组件标签中自定义父组件中的属性 <child :age="age"></child>

4、子组件中使用 props 接收数据。

props 的两个接收类型

# 字符串数组 类型
	props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
# 对象 类型
    props: {
      title: String,
      likes: Number,
      isPublished: Boolean,
      commentIds: Array,
      author: Object,
      callback: Function,
      contactsPromise: Promise // or any other constructor
    }

2.1.2 代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>父传子</h3>
    // 通过自定义属性 ---> 不能用驼峰,不要跟子组件中变量冲突
    <child :age="age"></child>

</div>
</body>
<script>
	// 父中有age,child 只有name,没有age,现在把父中的 age 传到 child 中显示
    var child = {
        template: `
            <div>
                <p>姓名:{{name}}</p>
                <p>年龄:{{age}}</p>
            </div>
        `,
        data(){
            return{
                name:'ysg'
            }
        },
        // props: {
        //     age: Number
        // },
        props: ['age']

    };
    
    var vm = new Vue({
        el: '#app',
        data: {
            age: 16
        },
        // 注册组件
        components: {
            child
        }
    })
</script>
</html>

2.1.3 Prop 验证

我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。

为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    
      // 多个可能的类型
    propB: [String, Number],
    
      // 必填的字符串
    propC: {
      type: String,
      required: true
    },
      
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
      
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
      
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].includes(value)
      }
    }
  }
})

代码示例

// 子组件

<template>
  <div class="index">
    这里是子组件,显示父组件信息————{{ info }}
  </div>
</template>

<script>
export default {
  name:'index',
  props: {
    info: {
      type: String, //类型
      required: true, //必要性
      default: '老王' //默认值
    }
  }
}
</script>



// 父组件

<template>
  <div class="home">
    这里是父组件
    <button @click="SendInfo">发送Info</button>
    <zzj :info="name"></zzj>
  </div>
</template>

<script>
import zzj from '@/components'

export default {
    name: "home",
    data() {
      return{
        name: ''
      }
    },
    methods: {
      SendInfo() {
        this.name = 'null'
      }
    },
    components: {
      zzj
    }
}
</script>

2.2 子传父

子组件传值给父组件:通过自定义事件的方式

场景:父组件中等待子组件值进行装饰,子组件中存在输入框,通过点击事件将输入框中的值传入父组件

2.2.1 使用流程

1、创建父组件与子组件,并设置相应的值

2、父组件中使用 components 注册子组件

3、在子组件的函数通过 this.$emit('myevent', this.childval) 设置自定义事件名称 myevent

4、子组件标签中引用自定义事件 @myevent<child @myevent="father"></child>

5、在父组件中设置接收函数 father ,接收到值后装饰视图

2.2.2 代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>子传父</h3>
    父组件等待子组件的值:{{fatherval}}
    <p>
    <child @myevent="father"></child>
</div>
</body>
<script>
    var child={
        template:`
           <div>
            子组件的值:<input type="text" v-model="childval">
            <input type="text" v-model="childval2">
            <button @click='childsend'>发送</button>
           </div>
        `,
        data(){
            return {
                childval: '',
                childval2: ''
            }
        },
        methods:{
            childsend(){
                console.log(this.childval);
                this.$emit('myevent', [this.childval, this.childval2])
            }
        }
    };

    var vm = new Vue({
        el:'#app',
        data: {
            fatherval: ''
        },
        methods:{
            father(val){
                this.fatherval = val
            }
        },
        components:{
            child
        }
    })
</script>
</html>

2.3 ref属性

ref 属性,可以更方便的实现父子通信,无需考虑父子组件的层级关系,即父组件与孙组件传值时就会特别麻烦

场景:父组件获取子组件中的 年龄属性,父组件发送 input.value 属性给子组件

2.3.1 使用流程

# 普通标签
# 1、ref 属性放在普通标签上,拿到标签的 dom 对象
# 2、直接修改原生 dom 对象的 value 属性,就可以修改 input 的值了
	this.inputinfo = '普通标签数据修改';
	this.$refs.myinput.value = this.inputinfo

# 组件对象
# 1、ref 属性放在组件上,拿到的是组件对象
# 2、就可以使用组件对象的属性和方法,使用句点符
# 3、this.$refs.mychild.属性,this.$refs.mychild.方法
	console.log(this.$refs) 查看对象中有几个值
	this.age = this.$refs.mychild.age;

重点:以后就不需要关注是子传父还是父传子了,直接通过对象取值赋值即可,而且可以主动调用子组件中的函数

2.3.2 代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>ref 属性</h3>
    <button @click="handleClick">点击获取子组件的年龄</button> ----》{{age}}
    <br>
    <br>
    <input type="text" ref="myinput" v-model="inputinfo">
    <div ref="mydiv">我是div</div>
    <hr>
    <child ref="mychild"></child>
    <hr>
</div>
</body>
<script>
    var child={
        template:`
            <div>
                <p>获取父组件中 input 框中的内容</p>
                <p>父组件内容:{{val}}</p>
            </div>
        `,
        data(){
            return{
                val:'',
                age:35
            }
        }
    };

    var vm = new Vue({
        el:'#app',
        data:{
            age:'',
            inputinfo:''
        },
        methods:{
            handleClick(){
                this.age = this.$refs.mychild.age;
                this.$refs.mychild.val = this.inputinfo
            }
        },
        components:{
            child
        },
        mounted() {
            this.inputinfo = 'lqz is handsome';
            this.$refs.myinput.value = this.inputinfo
        }
    })
</script>
</html>

3.插槽分发

一般情况下,编写完一个组件之后,组件的内容都是写死的,当要加数据时,只能去组件中修改内容,扩展性很差。

幸好,Vue 自定义的 <slot> 元素让这变得非常简单:

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

如你所见,定义 alert-box 组件时,我们只要在需要的地方加入插槽就行了 <slot></slot> ——就这么简单!

PS:编译作用域

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

3.1 匿名插槽

后备内容:<p>插槽无内容时显示:无内容的插槽</p>

提供内容:<p>这是定义插槽的内容</p>

插槽中可定义默认显示的内容,在无 提供内容 时显示 后备内容

当一个组件中存在多个 <slot></slot> 时,同一条 提供内容 会存在于多个 slot

使用插槽

# 组件为:waring_div

<waring_div>
    <p>这是定义插槽的内容1</p>
    <p>这是定义插槽的内容2</p>
</waring_div>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>匿名插槽</h3>
    <waring_div>
        <p>这是定义插槽的内容1</p>
        <p>这是定义插槽的内容2</p>
    </waring_div>
</div>
</body>
<script>
    Vue.component('waring_div', {
        template: `
        <div>
            <p>插槽内容开始</p>
            <strong>这是一个错误提示</strong>
            <slot>
            <p>插槽无内容时显示:无内容的插槽</p>
            </slot>
            <p>----------</p>
            <slot></slot>
            <p>插槽内容结束</p>
        </div>
        `
    })

    var vm = new Vue({
        el:'#app',
        data:{

        }
    })
</script>
</html>

3.2 具名插槽

v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #

例如 v-slot:header 可以被重写为 #header

<div id="app">
    <h3>具名插槽</h3>
    <info_div>
        <div slot=header>
            <p>头部内容</p>
        </div>
        <p>-----中间匿名函数-----</p>
        <div slot=foot>
            <p>头部内容</p>
        </div>
    </info_div>
</div>
Vue.component('info_div',{
    template:`
    <div>
        <p>头部插槽开始</p>
        <slot name="header"></slot>
        <p>头部插槽结束</p>

        <slot></slot>

		<p>尾部插槽开始</p>
        <slot name="foot"></slot>
        <p>尾部插槽结束</p>
    </div>
`
})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>具名插槽</h3>
    <info_div>
        <div slot=header>
            <p>头部内容</p>
        </div>
        <p>-----中间匿名函数-----</p>
        <div slot=foot>
            <p>头部内容</p>
        </div>
    </info_div>
</div>
</body>
<script>
    Vue.component('info_div',{
       template:`
        <div>
            <p>头部插槽开始</p>
            <slot name="header"></slot>
            <p>头部插槽结束</p>
            <slot></slot>
            <p>尾部插槽开始</p>
            <slot name="foot"></slot>
            <p>尾部插槽结束</p>
        </div>
       `
    })

    var vm = new Vue({
        el:'#app',
        data:{

        }
    })
</script>
</html>

4.动态组件

4.1 动态组件component标签

component 的用法 <component :is="val"></component>

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

4.2 keep-alive

我们希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来

为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<keep-alive>
    <component :is="val"></component>
</keep-alive>

4.3 代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
    <h3>动态组件</h3>
    <button @click="handleClick('home')">首页</button>
    <button @click="handleClick('login')">登录</button>
    <button @click="handleClick('sgin')">注册</button>
    <keep-alive>
        <component :is="val"></component>
    </keep-alive>
</div>
</body>
<script>
    var home = {
        template: `
            <div>
                <p>这里是首页</p>
                <input type="text"> <button>搜索</button>
            </div>
        `
    };
    var login = {
        template: `
            <div>
                <p>这里是登录</p>
            </div>
        `
    };
    var sgin = {
        template: `
            <div>
                <p>这里是注册</p>
            </div>
        `
    };

    var vm = new Vue({
        el: '#app',
        data: {
            val: 'login'
        },
        methods: {
            handleClick(type) {
                this.val = type;
                console.log(this.val)
            }
        },
        // 注册组件
        components: {
            home, login, sgin
        }
    })
</script>
</html>

5.scoped

# 父组件的样式,在子组件中会生效,加入 scoped 让该样式只在当前组件中生效

<style scoped>
h1 {
  background-color: chartreuse;
}
</style>

五:前后端交互 --- axios

1 Axios 的快速使用

1.1 axios 的安装

# 利用 npm/cnpm安装
npm install axios
cnpm install axios

# vue 脚手架项目中使用
# 以 cnpm 为例, i 表示 install,-S 表示自动加入 package.json 中 
cnpm i axios -S

# 引用 cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

1.2 创建 axios 实例

const instance = axios.create({
    baseURL: 'http://127.0.0.1:8000/api/',
    timeout: 1000,
    headers: {'Authorization': this.token}
})

instance.get('car_model/').then(res => {
    console.log(res.data['results'])
    this.$refs.child.results = res.data['results']
})

1.2.1 发送 post 请求

// 发送 POST 请求
axios.post('http://127.0.0.1:8000/api/login/', {
    username: this.username,
    password: this.password
}).then(res => {
    if (res.data['code'] === 0) {
        console.log(res.data)
    } else {
        alert('账号或用户名错误')
    }
})

1.2.2 发送 get 请求

axios.get('http://127.0.0.1:8000/api/cart/').then(response => {
    console.log('cart', response.data)
})

2.请求配置参数

这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 GET 方法。

{
  // `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // 默认值

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
  // 你可以修改请求头。
  transformRequest: [function (data, headers) {
    // 对发送的 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对接收的 data 进行任意转换处理

    return data;
  }],

  // 自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是与请求一起发送的 URL 参数
  // 必须是一个简单对象或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer`是可选方法,主要用于序列化`params`
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 是作为请求体被发送的数据
  // 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
  // 在没有设置 `transformRequest` 时,则必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属: FormData, File, Blob
  // - Node 专属: Stream, Buffer
  data: {
    firstName: 'Fred'
  },
  
  // 发送请求体数据的可选语法
  // 请求方式 post
  // 只有 value 会被发送,key 则不会
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout` 指定请求超时的毫秒数。
  // 如果请求时间超过 `timeout` 的值,则请求会被中断
  timeout: 1000, // 默认值是 `0` (永不超时)

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // default

  // `adapter` 允许自定义处理请求,这使测试更加容易。
  // 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
  adapter: function (config) {
    /* ... */
  },

  // `auth` HTTP Basic Auth
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 表示浏览器将要响应的数据类型
  // 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
  // 浏览器专属:'blob'
  responseType: 'json', // 默认值

  // `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
  // 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
  // Note: Ignored for `responseType` of 'stream' or client-side requests
  responseEncoding: 'utf8', // 默认值

  // `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
  xsrfCookieName: 'XSRF-TOKEN', // 默认值

  // `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
  xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值

  // `onUploadProgress` 允许为上传处理进度事件
  // 浏览器专属
  onUploadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  // 浏览器专属
  onDownloadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
  maxContentLength: 2000,

  // `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
  maxBodyLength: 2000,

  // `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
  // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
  // 则promise 将会 resolved,否则是 rejected。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值
  },

  // `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
  // 如果设置为0,则不会进行重定向
  maxRedirects: 5, // 默认值

  // `socketPath` 定义了在node.js中使用的UNIX套接字。
  // e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
  // 只能指定 `socketPath` 或 `proxy` 。
  // 若都指定,这使用 `socketPath` 。
  socketPath: null, // default

  // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
  // and https requests, respectively, in node.js. This allows options to be added like
  // `keepAlive` that are not enabled by default.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy` 定义了代理服务器的主机名,端口和协议。
  // 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
  // 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
  // `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
  // 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
  // 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // see https://axios-http.com/zh/docs/cancellation
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress` indicates whether or not the response body should be decompressed 
  // automatically. If set to `true` will also remove the 'content-encoding' header 
  // from the responses objects of all decompressed responses
  // - Node only (XHR cannot turn off decompression)
  decompress: true // 默认值

}

3.代码示例

<!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>
    <script src="./js/jquery-2.0.0.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>axios与后端交互</h1>
    <button @click="handleLoad2">点击加载数据</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:8000/user/",
                    type: 'get',
                    success: data => {
                        data = JSON.parse(data); // data 是字符串类型,需要转成对象类型
                        this.name = data.name;
                        this.age = data.age;

                    }
                })
            },
            handleLoad2(){
                axios.get('http://127.0.0.1:8000/user/').then(res=>{
                    this.name = res.data.name;
                    this.age = res.data.age;
                })
            }
        }
    })
</script>

</html>

六:可复用性 & 组合

1.混入mixin

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

// index.js
// 设置一个混入对象
var myMixin = {
    data(){
      return{
          age:65
      }
    },
    mounted() {
        this.printName()
    },
    methods:{
        printName(){
            alert(this.name)
        }
    }
}

// 匿名导出混入对象
export default {myMixin}

1.1 局部引用

// xxx.vue

<template>
  <div class="home">
    <h3>混入</h3>
    <!--  使用混入参数  -->
    <button @click="printName">显示名字</button>
    <p>打印年龄:{{ age }}</p>
  </div>
</template>

<script>
// 引用混入对象
import mix from '@/mymixin'

export default {
  name: 'home',
  data() {
    return {
      name: 'ysging'
    }
  },
  // 配置混入对象
  mixins: [mix.myMixin],
}
</script>

1.2 全局引用

在 vue 项目中的 main.js 中引入

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

// 引用全局混入对象
import mix from '@/mymixin'

Vue.mixin(mix.myMixin)
// 多个混入需要引用多次
// Vue.mixin(mix.myMixin2)
// Vue.mixin(mix.myMixin3)

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')



2.插件

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
1、添加全局方法或者 property。如:vue-custom-element
2、添加全局资源:指令/过滤器/过渡等。如 vue-touch
3、通过全局混入来添加一些组件选项。如 vue-router
4、添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
5、一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

2.1 使用步骤

1 新建包 plugins,新建 index.js
    import Vue from "vue";
    import axios from "axios";
    export default {
        install(vue) {
            console.log('执行了插件', vue)
            # 可以做的事
            #  1 了解,自定义指令(不了解没关系)
            #  2 定义全局变量,以后在任何组件中都可以使用到,借助于Vue.prototype往里放 ,以后所有组件只要this.$ajax  就是axios对象
            #  3 使用全局混入
            #  4 自定义全局组件
        }
    }

2 在main.js 中配置
    # 使用自定义插件
    import plugin from '@/plugins'
    Vue.use(plugin)
    

2.2 代码示例

定义插件

import Vue from "vue";
// 定义插件
export default {
    install(vue){
        console.log('执行了插件', vue)
        // 2 定义全局变量,以后在任何组件中都可以使用到,借助于 Vue.prototype 往里放 ,以后所有组件只要 this.$ajax  就是axios对象
        Vue.prototype.$name = 'ysg'
        Vue.prototype.$add = (a, b)=>{
            return a + b
        }
    }
}

引用插件

// 在 main.js 文件中引用自定义插件
import myplugin from '@/plugins'
Vue.use(myplugin)

使用插件

<template>
  <div class="home">
    <h3>插件的使用</h3>
    <p>打印插件中的姓名:{{$name}}</p>
    <button @click="handleAdd">使用插件的函数计算值</button> ---> {{addval}}
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return{
      addval:''
    }
  },
  methods:{
    handleAdd(){
      this.addval = this.$add(15, 19)
    }
  }
}
</script>

七:路由

1.vuex

2.vue Router

第三方路由插件,单页面应用 ---> 实现在一个 index.html 中有页面跳转效果的插件

# 路由控制
    -<router-link>   跳转用
    -<router-view/>  替换页面组件用

3.localStorage系列

3.1 概念与使用

# 都是在浏览器存储数据的--》存数据有什么用?
	-登录成功 token存在本地
    -不登录加入购物车功能,迪卡侬存在了localStorage中
    -组件间通信----》 跨组件

this.userInfo = {"name":"ysg", "age":"25"}
   
# localStorage
	-永久存储,除非清空缓存,手动删除,代码删除
    -localStorage.setItem('userinfo', JSON.stringify(this.userInfo))
    -localStorage.getItem('userinfo')
    -localStorage.clear()  // 清空全部
    -localStorage.removeItem('userinfo') 
# sessionStorage
	-关闭浏览器,自动清理
    -sessionStorage.setItem('userinfo', JSON.stringify(this.userInfo))
    -sessionStorage.getItem('userinfo')
    -sessionStorage.clear()  // 清空全部
    -sessionStorage.removeItem('userinfo') 
# cookie
	- 安装:cnpm i vue-cookie -S
	
    -有过期时间,到过期时间自动清理
    -借助于第三方 vue-cookies
    -cookies.set('userinfo', JSON.stringify(this.userInfo))
    -cookies.get('userinfo')
    -cookies.delete('userinfo')
    

3.2 代码示例

<template>
  <div class="home">

    <h3>local</h3>
    <button @click="localSave">存储local</button>
    <button @click="localGet">获取local</button>
    <button @click="localDelete">删除local</button>

    <h3>session</h3>
    <button @click="sessionSave">存储session</button>
    <button @click="sessionGet">获取session</button>
    <button @click="sessionDelete">删除session</button>

    <h3>cookies</h3>
    <button @click="cookiesSave">存储cookies</button>
    <button @click="cookiesGet">获取cookies</button>
    <button @click="cookiesDelete">删除cookies</button>

  </div>
</template>

<script>
import cookies from 'vue-cookie'

export default {
  name: 'home',
  data() {
    return {
      userInfo: {"name": "ysging", "age": 15}
    }
  },
  methods:{
    // local
    localSave(){
      localStorage.setItem('userinfo', JSON.stringify(this.userInfo))
    },
    localGet(){
      var info = localStorage.getItem('userinfo')
      var str_info = JSON.parse(info)
      console.log(str_info)
    },
    localDelete(){
      // localStorage.clear()  // 清空
      localStorage.removeItem('userinfo') // 删除指定值
    },

	// session
    sessionSave(){
      sessionStorage.setItem('userinfo', JSON.stringify(this.userInfo))
    },
    sessionGet(){
      var info = sessionStorage.getItem('userinfo')
      var str_info = JSON.parse(info)
      console.log(str_info)
    },
    sessionDelete(){
      // localStorage.clear()  // 清空
      sessionStorage.removeItem('userinfo') // 删除指定值
    },

	// vue-cookies
    cookiesSave(){
      cookies.set('userinfo', JSON.stringify(this.userInfo))
    },
    cookiesGet(){
      var info = cookies.get('userinfo')
      console.log(info)
    },
    cookiesDelete(){
      cookies.delete('userinfo') // 删除指定值
    },

  }
}
</script>

八:Vue框架使用

1.vue-cli 安装

# vue的脚手架:快速帮我们创建出vue的项目
# vue2 和 vue3 
	-vue-cli可以创建 vue2 和 vue3 的项目 webpack构建工具
    -Vite:新一代构建工具,只能创建vue3 效率非常高
    -vue3上,推荐使用 ts 写 js
    
# ide 的选择(vscode,webstorm:jetbrains公司的,跟pycharm一家的,使用习惯一样)
	-选择使用pycharm+vue插件 开发vue项目
    -使用pycharm打开vue项目
    
# 1.先安装nodejs 后端语言---》语法就是js的语法
	-js运行在浏览器中,浏览器中有它的解释器环境
    -不能运行在操作系统之上,把chrom浏览器的v8引擎,把它安装在操作系统上
    	-c语言写了内置库:文件操作,网络操作
        
    -官网:https://nodejs.org/zh-cn/download/ ,下载,一路下一步
    -安装完会释放两个命令(在环境变量中,任意路径都能敲这俩命令)
    	-node      python3
        -npm        pip
        -cnpm      等同于pip ,只是下模块,直接取淘宝镜像站下载,速度快
 
    
 # 2.npm 下载时候,去国外,速度慢,使用国内镜像
	-淘宝做了一个 cnpm 可执行文件,用来替换 npm,以后所有使用 npm 的地方都换成 cnpm 即可
    -安装 cnpm 
    npm install -g cnpm --registry=https://registry.npm.taobao.org
 
 # 3.安装 vue-cli ,通过脚手架创建 vue 项目 (django--->django项目--->django-admin)
 	cnpm install -g @vue/cli
    -只要装成功,又会多出一个可执行文件 vue   
 
 # 4.创建vue项目
	vue create myfirstvue # 速度很慢,可以 ctrl+c 停止 执行 npm cache clean --force
    # 很慢的原因
    	-从 github 拉一个空项目
    	-按照该项目所有的依赖,npm 装
 # 5.运行 vue 项目
	-方式一:在命令行中敲:npm run serve
    -方式二:在pycharm中点击绿色箭头运行




2.vue项目目录介绍

myfirstvue                    # 项目名字
    node_modules              # 非常多第三方模块,项目复制给别人时【上传 git 要忽略掉】,这个文件夹删掉,很多小文件,项目的依赖,项目要运行,没有它不行 如果没有只需要执行 cnpm install,根据 package.json 的依赖包,按装好依赖
    public                    # 文件夹
        -favicon.ico           # 网站小图标
        -index.html             # spa 单页面应用,以后整个 vue 项目都是用这一个 html,但不用动它
    src                       # 文件夹--以后咱们都动这里面的
    	-assets                # 静态资源,js,css,图片类似于 static 文件夹
    		logo.png          # 静态资源的图片
    	-components           # 组件:小组件 xx.vue,用在别的大(页面组件)组件中
    		-HelloWorld.vue   # 默认了一个 hello world 组件
    	-router                    # 装了 vue-router 自动生成的,如果不装就没有
    		index.js              # vue-router 的配置
    	-store                # 装了 vuex 自动生成的,如果不装就没有
    		index.js          # vuex 的配置
    	-views                # 页面组件
            AboutView.vue     # 关于 页面组件
            HomeView.vue      # 主页 页面组件
    	-App.vue              # 根组件
    	-main.js              # 整个项目启动入口
    .gitignore                # git 的忽略文件
    babel.config.js           # babel 的配置
    jsconfig.json			  # 配置文件,不用动
    package.json              # 安装了第三方模块,它自动增加 重要:类似于 python 项目的 requirements.txt 当前项目所有依赖
    package-lock.json         # 锁定文件,忽略掉 package.json 中写了依赖的版本,这个文件锁定所有版本
    README.md                 # 读我,项目的介绍
    vue.config.js             # vue 项目的配置文件

3.es6的导入导出语法

如果要导入,必须先导出

3.1 默认导出【常用】

// 默认导出
var name = 'ysg'

function addnum(a, b) {
    return a + b
}

export default {name, addnum}
// 导入并使用
import ysg from '@/ysg/ysg'
console.log(ysg.name)
console.log(ysg.addnum(12,34))

3.2 命名导出

// 命名导出
export const yname=name
export const yaddnum=addnum
export const yage=16

// 命名导出, 不必全部引用
import {yname, yage} from "@/ysg/ysg";
console.log(yname)
console.log(yage)

4.vue项目开发规范

# 以后写的组件,都是单页面组件  使用 xx.vue 以后写一个组件就是一个 xx.vue,页面组件和小组件
# 以后一个组件就是一个 xx.vue,内部都有三部分
	-<template></template>  # html 内容写在里面
    -<script></script>      # 写 js 内容
    -<style></style>        # 写 css 样式
# main.js 是整个入口
1 把App.vue 根组件导入了
2 使用 new Vue({render: h => h(App)}).$mount('#app') 把 App.vue 组件中得数据和模板,插入到了 index.html 的 id 为 app 的 div 中
3 以后,只要在每个组件的 export default {} 写之前学过的所有 js 的东西
4 以后,只要在每个组件的 template,写模板,插值语法,指令
5 以后,只要在每个组件的 style,写样式

5.vue项目前后端打通

5.1 代码示例

# App.vue

<template>
    <div id="app">
        <ul v-for="book in bookList">
            <li>{{book.name}}</li>
            <li>{{book.price}}</li>
        </ul>
        <h3>登录功能</h3>
        用户名:<input type="text" v-model="username">
        密码:<input type="text" v-model="password">
        <button @click="handleclick">登录</button>
        {{err}}
    </div>
</template>

<script>
    import axios from 'axios'

    export default {
        name: 'App',
        data() {
            return {
                bookList: [],
                username: '',
                password: '',
                err: '',
            }
        },
        created() {
            axios.get('http://127.0.0.1:8000/api/book/').then(res => {
                // console.log(res.data)
                this.bookList = res.data
            })
        },
        methods: {
            handleclick() {
                axios.post('http://127.0.0.1:8000/api/login/',
                {
                    'username':this.username,
                    'password':this.password,
                }).then(res=>{
                  this.err = res.data
                })
            }
        }
    }

</script>

5.2 解决 Django 跨域问题

cors 解决跨域

cors:跨域资源共享,后端技术,核心就是在响应头中加入数据,允许浏览器接受数据
CORS需要浏览器和服务器同时支持,IE浏览器不能低于IE10

简单请求与非简单请求

# CORS基本流程
    浏览器将CORS请求分成两类:
        -简单请求(simple request)
        -非简单请求(not-so-simple request)
    
# 简单请求:
    浏览器发出CORS简单请求,只需要在头信息之中增加一个Access-Control-Allow-Origin字段
    
# 非简单请求
    浏览器发出CORS非简单请求,会在正式通信之前,先发送一个options请求,称为”预检”请求。
    浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,如果运行,再发真正的请求
    
# 什么是简单请求,什么是非简单请求
	-满足下面两种情况,就是简单请求
    	-1 请求方法是以下三种方法之一:
            HEAD
            GET
            POST
        -2 HTTP的请求头信息不超出以下几种字段:
            Accept
            Accept-Language
            Content-Language
            Last-Event-ID
            Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

django 配置

1.安装:pip install django-cors-headers
2.配置 settings.py 文件

INSTALLED_APPS = [
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware', # 在此项前配置
]

# 跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
# 允许所有域
CORS_ORIGIN_ALLOW_ALL = True
# 允许的请求头
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
# 允许的请求头
CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)

九:Vue3

vue项目的版本,新项目使用vue3,有部分老项目使用vue2

1.vue3 的变化

# vue3 的变化
    1.性能的提升
    	-打包大小减少 41%
    	-初次渲染快 55%, 更新渲染快 133%
    	-内存减少 54%
    2.源码的升级
    	使用 Proxy 代替 defineProperty 实现响应式
    	重写虚拟DOM的实现和 Tree-Shaking
    3.拥抱TypeScript
    	Vue3 可以更好的支持 TypeScript
    4.新的特性
    	Composition API(组合API)
        setup 配置
        ref 与 reactive
        watch 与 watchEffect
        provide 与 inject
        新的内置组件
        Fragment
        Teleport
        Suspense
        其他改变...

        新的生命周期钩子
        data 选项应始终被声明为一个函数
        移除 keyCode 支持作为 v-on 的修饰符
        
# 组合式API和配置项API
	vue2 :配置项API
         new Vue({
             el:'#app',
             data:{}
         })
        
    vue3: 组合式API
        let name='lqz'
        let add=()=>{ 
        }

2.vue3创建项目

2.1 使用vue-cli

# 打开 cmd 
vue create vuecli3

2.2 vite

前端构建工具,最大的优势就是速度快
官网:https://cn.vuejs.org/guide/scaling-up/tooling.html#project-scaffolding

为什么这么快?
创建项目快 ----> 不安装第三方依赖
执行项目,热更新 ----> 按需编译

# 使用步骤:
1.创建项目npm init vue@latest
    -按需选择,vueRouter
    - cd到项目中执行cnpm install 把依赖装好
    -运行:npm run dev

    -Pinia:用来替换Vuex的,新一代的状态管理器

3.基础函数

3.1 setup函数

<script> 中的 setup 属性表示凡在此内容里定义的属性、方法均可以在 <template> 中使用,且无需 return

lang=ts 表示以 TypeScript 类型进行编写,简称 ts

PS:ts 完全兼容 js

<script setup lang="ts">

</script>

但下面仍需简单使用下 setup() 函数,了解其 vue3 低版本的使用方法

在 setup 函数中,定义变量、函数,一定要 return{ ... },然后在 <template> 中才能使用

<template>
  <div>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <button @click="handleAge">点击年龄 +1</button>
  </div>
</template>


<script>
  export default {
      setup(){
        let name='亦双弓'
        let age='25'
        let handleAge =() => {
          age++
          console.log(age)
        }
        return {name, age, handleAge}
    }
  }
</script>

但因此失去了响应式,因此需要使用 ref 恢复其响应式

3.3 响应式

ref 定义的数据:操作数据需要 .value,读取数据时模板中直接读取不需要 .value。
reactive 定义的数据:操作数据与读取数据:均不需要 .value
reactive 渲染的数据:可以直接引用

3.3.1 ref

以后定义变量,如果想有响应式就用 ref 包裹起来,再修改变量,需要用 变量名.value 修改

vue3 中不在自动引入,需要用到时 按需引用 方法

# 配置项 api(vue2) 和组合式 api(vue3) 可以混写,不建议
	-在前在data中定义的变量
    -在setup中定义的变量
    
    -总结:
    	在 setup 中定义的变量和函数,在之前配置项 api 中可以直接使用 this.变量,函数调用即可
    	但是在原来配置项中定义的变量,函数,在 setup 中无法使用
<template>
  <div>
    <p>年龄:{{age}}</p>
    <button @click="handleAge">点击年龄 +1</button>
  </div>
</template>


<script>
// vue3 中不在自动引入,需要用到时按需引用方法
import {ref} from 'vue'
  export default {
      setup(){
        // ref 渲染需要指定的响应式参数,可以减少监听成本,提高运行效率
        let age=ref(25)
        let handleAge =() => {
          age.value++
        }
        return {name, age, handleAge}
    }
  }
</script>

3.3.1 reactive

<template>
  <div>
    <p>姓名:{{userinfo.name}}</p>
    <p>年龄:{{userinfo.age}}</p>
    <button @click="handleAge">点击年龄 +1</button>
  </div>
</template>


<script>
// vue3 中不在自动引入,需要用到时按需引用方法
import {ref, reactive} from 'vue'
  export default {
      setup(){
        let userinfo = reactive({
          'name': 'ysg',
          'age': 25
        })
        // ref 渲染需要指定的响应式参数,可以减少监听成本,提高运行效率
        let age=ref(25)
        let handleAge =() => {
          userinfo.age++
        }
        return {userinfo, handleAge}
    }
  }
</script>

3.3.3 toRefs

以下例子中 toRefs 需要与 reactive 同时使用,否知会报错:

reactivity.esm-bundler.js:1114 toRefs() expects a reactive object but received a plain one.

<template> 中可以直接引用对象中的属性,示例如下:

<template>
  <div>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <button @click="handleAge">点击年龄 +1</button>
  </div>
</template>


<script>
// vue3 中不在自动引入,需要用到时按需引用方法
import {ref, reactive, toRefs} from 'vue'
  export default {
      setup(){
        let userinfo = reactive({
          'name': 'ysg',
          'age': 25
        })
        // ref 渲染需要指定的响应式参数,可以减少监听成本,提高运行效率
        let age=ref(25)
        let handleAge =() => {
          userinfo.age++
        }
        return {...toRefs(userinfo), handleAge}
    }
  }
</script>

4.计算属性和监听属性

4.1 计算属性

vue3 新语法

let fullName = computed(() =>{
	return userinfo.firstName + userinfo.lastName
})

计算属性取值和修改值

let fullName = computed({
    get() {
    	return userinfo.firstName + userinfo.lastName
    },
    set(value) {
    	userinfo.firstName = value.slice(0, 1)
    	userinfo.lastName = value.slice(1)
    }
})

示例

<template>
  <div>
    <input type="text" v-model="num1"> + <input type="text" v-model="num2"> =
    <button>{{ addNum }}</button>
    <br>
    姓:<input type="text" v-model="firstName">
    名:<input type="text" v-model="lastName">
    <p></p>
    姓名:<input type="text" v-model="fullName">
  </div>
</template>


<script>
// vue3 中不在自动引入,需要用到时按需引用方法
import {reactive, toRefs, computed} from 'vue'

export default {
  setup() {
    let number = reactive({
      num1: 0,
      num2: 0
    })
    // 新语法
    let addNum = computed(() => {
      return parseInt(number.num1) + parseInt(number.num2)
    })

    let userinfo = reactive({
      'firstName': '亦',
      'lastName': '双弓'
    })
    // 计算属性取值和修改值
    let fullName = computed({
      get() {
        return userinfo.firstName + userinfo.lastName
      },
      set(value) {
        userinfo.firstName = value.slice(0, 1)
        userinfo.lastName = value.slice(1)
      }
    })

    return {...toRefs(number), addNum, ...toRefs(userinfo), fullName}
  }
}
</script>

4.2 监听属性

语法

watch(fullName, (newName, oldName) =>{
      console.log('老的', oldName)
      console.log('新的', newName)
    })
<template>
  <div>
    姓名:<input type="text" v-model="fullName">
  </div>
</template>


<script>
// vue3 中不在自动引入,需要用到时按需引用方法
import {ref, watch} from 'vue'

export default {
  setup() {
    let fullName = ref('ysg')
    watch(fullName, (newName, oldName) =>{
      console.log('老的', oldName)
      console.log('新的', newName)
    })
    return {fullName}
  }
}
</script>

5.生命周期

由于 Vue3 中加入 setup 与组件引用的改变,因此生命周期有了新的改变

# vue2       和    vue3比较
beforeCreate===>beforeCreate
created=======>created
beforeMount ===>beforeMount
mounted=======>mounted
beforeUpdate===>beforeUpdate
updated =======>updated
beforeDestroy ==>beforeUnmount
destroyed =====>unmounted
    
    
    
# vue2 的写法不推荐了,现在统一写在 setup 中
    beforeCreate===>setup()
    created=======>setup()
    beforeMount ===>onBeforeMount
    mounted=======>onMounted
    beforeUpdate===>onBeforeUpdate
    updated =======>onUpdated
    beforeUnmount ==>onBeforeUnmount
    unmounted =====>onUnmounted
    
 # 以前写在 created 中的代码,现在直接写在 setup 开始即可
 let show = ref(false)
 axios.get().then(res=>{
      show.value=res.data.show
 })

6.Vue后台管理模板

公司内部项目,会让写后台管理,往上有很多 vue 开源的后台管理的框架,咱们只需要在其上面做二次开发

# Django-Vue-Admin 前端---》D2Admin      https://django-vue-admin.com/document/
# 若依:vue 前端  用来   vue-element-admin
# elamin 前端   drf 写了基于 rbac 权限管理的框架

# https://gitee.com/liuqingzheng/vue_admin

视图如:Django-Vue-Admin

posted @ 2023-02-10 19:55  亦双弓  阅读(109)  评论(0编辑  收藏  举报