Vue学习笔记(尚硅谷天禹老师)

编程之家收集整理的这篇文章主要介绍了Vue学习笔记(尚硅谷天禹老师)编程之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

Vue学习笔记(尚硅谷天禹老师讲解)1. Vue核心1.1. Vue简介1.1.1. 官网英文官网中文官网1.1.2. 介绍与描述动态构建用户界面的渐进式JavaScript框架作者:尤雨溪1.1.3. Vue的特点遵循MVVM模式编码简洁,体积小,运行效率高,适合移动/PC端开发它本身只关注UI,可以引入其它第三方库开发项目1.1.4.与其他JS框架的关联借鉴 Angular 的模板和数据绑定技术借鉴 React 的组件化和虚拟DOM

Vue学习笔记

文章目录

1. Vue核心

1.1. Vue简介

1.1.1. 官网

1.1.2. 介绍与描述

1.1.3. Vue的特点

@H_741_404@
  • 遵循MVVM模式

  • 编码简洁,体积小,运行效率高,适合移动/PC端开发

  • 它本身只关注UI,可以引入其它第三方库开发项目

  • @H_326_419@

    1.1.4.与其他Js框架的关联

    @H_741_404@
  • 借鉴 Angular 的模板数据绑定技术
  • 借鉴 React 的组件化虚拟DOM技术
  • @H_326_419@

    1.1.5. Vue周边库

    • vue-cli:vue脚手架
    • vue-resource
    • axios
    • vue-router:路由
    • vuex:状态管理
    • element-ui:基于vue的UI组件库(PC端)

    1.2. 初识Vue

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>初识vue</Title>
        <!-- 引入Vue -->
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <!-- 准备一个容器 -->
        <div ID="root">
            <h1>Hello!{{name}}!</h1>
        </div>
    
        <script>
            Vue.config.productionTip = false // 阻止vue在启动时生成生产提示
            new Vue({
                el:'#root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
                data:{ //data用于存储数据,数据共el所指定的容器去使用
                    name:'JOJO'
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    注意:

    @H_741_404@
  • 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
  • root容器里的代码依然符合HTML规范,只不过混入了一些特殊的Vue语法
  • root容器里的代码被称为Vue模板
  • Vue实例与容器是一一对应
  • 真实开发中只有一个Vue实例,并且会配合着组件一起使用
  • {{xxx}}中的xxx要写Js表达式,且xxx可以自动读取到data中的所有属性
  • 一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新
  • @H_326_419@

    1.3. 模板语法

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>vue模板语法</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h1>插值语法</h1>
            <h3>你好,{{name}}!</h3>
            <hr>
            <h1>指令语法</h1>
            <a v-bind:href="url">快去看新番!</a><br>
            <a :href="url">快去看新番!</a>
        </div>
    
        <script>
            Vue.config.productionTip = false 
            new Vue({
                el:'#root', 
                data:{ 
                    name:'JOJO',
                    url:'https://www.bilibili.com/'
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    Vue模板语法包括两大类:

    @H_741_404@
  • 插值语法:

  • 指令语法:

  • @H_326_419@

    1.4. 数据绑定

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>数据绑定</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            单向数据绑定:<input type="text" v-bind:value="name"><br>
            双向数据绑定:<input type="text" v-model:value="name">
        </div>
    
        <script>
            Vue.config.productionTip = false 
            new Vue({
                el:'#root', 
                data:{
                    name:'JOJO'
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    • Vue中有2种数据绑定的方式:

      @H_741_404@
    • 单向绑定(v-bind):数据只能从data流向页面
    • 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
    • @H_326_419@
    • 备注:

      @H_741_404@
    • 双向绑定一般都应用在表单类元素上(如:<input><select><textarea>等)
    • v-model:value可以简写为v-model,因为v-model认收集的就是value值
    • @H_326_419@

    1.5. el与data的两种写法

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>el与data的两种写法</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h1>Hello,{{name}}!</h1>
        </div>
    
        <script>
            Vue.config.productionTip = false 
            //el的两种写法:
            // const vm = new Vue({
            //     // el:'#root',//第一种写法
            //     data:{
            //         name:'JOJO'
            //     }
            // })
            // vm.$mount('#root')//第二种写法
    
            //data的两种写法:
            new Vue({
                el:'#root', 
                //data的第一种写法:对象式
                // data:{
                //     name:'JOJO'
                // }
                //data的第二种写法:函数
                data(){
                    return{
                        name:'JOJO'
                    }
                }
            })
        </script>
    </body>
    </HTML>
    

    总结:

    el有2种写法:

    @H_741_404@
  • 创建Vue实例对象的时候配置el属性
  • 创建Vue实例,随后再通过vm.$mount('#root')指定el的值
  • @H_326_419@

    data有2种写法:

    @H_741_404@
  • 对象式
  • 函数
  • @H_326_419@

    由Vue管理函数一定不要写箭头函数,否则this就不再是Vue实例了

    1.6. MVVM模型

    • MVVM模型:
      • M:模型(Model),data中的数据
      • V:视图(VIEw),模板代码
      • VM:视图模型(viewmodel),Vue实例
    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>mvvm</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h2>名称:{{name}}</h2>
            <h2>战队:{{rank}}</h2>
            <h2>测试:{{$options}}</h2>
        </div>
    
        <script>
            Vue.config.productionTip = false
            new Vue({
                el:'#root',
                data:{ 
                    name:'uzi',
                    rank:'RNG'
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    • data中所有的属性,最后都出现在了vm身上
    • vm身上所有的属性 及 Vue原型身上所有的属性,在Vue模板中都可以直接使用

    1.7. Vue中的数据代理

    总结:

    @H_741_404@
  • Vue中的数据代理通过vm对象来代理data对象中属性的操作(读/写)
  • Vue中数据代理的好处:更加方便的操作data中的数据
  • 基本原理:
  • @H_326_419@

    1.8. 事件处理

    1.8.1. 事件的基本用法

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>事件的基本用法</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h2>hello,{{name}}</h2>
            <@R_502_5554@ v-on:click="showInfo1">点我提示信息1</@R_502_5554@>
            <@R_502_5554@ @click="showInfo2($event,66)">点我提示信息2</@R_502_5554@>
        </div>
    
        <script>
            Vue.config.productionTip = false 
            new Vue({
                el:'#root', 
                data:{
                    name:'JOJO'
                },
                methods:{
                    showInfo1(event){
                        console.log(event)
                    },
                    showInfo2(evnet,num){
                        console.log(event,num)
                    }
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    @H_741_404@
  • 使用v-on:xxx@xxx绑定事件,其中xxx是事件名
  • 事件的回调需要配置在methods对象中,最终会在vm上
  • methods中配置的函数,==不要用箭头函数!==否则this就不是vm了
  • methods中配置的函数,都是被Vue所管理函数,this的指向是vm或组件实例对象
  • @click="demo@click="demo($event)"效果一致,但后者可以传参
  • @H_326_419@

    1.8.2. 事件修饰符

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>事件修饰符</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    		<style>
    			*{
    				margin-top: 20px;
    			}
    			.demo1{
    				height: 50px;
    				background-color: skyblue;
    			}
    			.Box1{
    				padding: 5px;
    				background-color: skyblue;
    			}
    			.Box2{
    				padding: 5px;
    				background-color: orange;
    			}
    			.List{
    				wIDth: 200px;
    				height: 200px;
    				background-color: peru;
    				overflow: auto;
    			}
    			li{
    				height: 100px;
    			}
    		</style>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>欢迎来到{{name}}学习</h2>
    			<!-- 阻止认事件 -->
    			<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
    
    			<!-- 阻止事件冒泡 -->
    			<div class="demo1" @click="showInfo">
    				<@R_502_5554@ @click.stop="showInfo">点我提示信息</@R_502_5554@>
    			</div>
    
    			<!-- 事件只触发一次 -->
    			<@R_502_5554@ @click.once="showInfo">点我提示信息</@R_502_5554@>
    
    			<!-- 使用事件的捕获模式 -->
    			<div class="Box1" @click.capture="showMsg(1)">
    				div1
    				<div class="Box2" @click="showMsg(2)">
    					div2
    				</div>
    			</div>
    
    			<!-- 只有event.target是当前操作的元素时才触发事件 -->
    			<div class="demo1" @click.self="showInfo">
    				<@R_502_5554@ @click="showInfo">点我提示信息</@R_502_5554@>
    			</div>
    
    			<!-- 事件的认行为立即执行,无需等待事件回调执行完毕 -->
    			<ul @wheel.passive="demo" class="List">
    				<li>1</li>
    				<li>2</li>
    				<li>3</li>
    				<li>4</li>
    			</ul>
    
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			data:{
    				name:'尚硅谷'
    			},
    			methods:{
    				showInfo(e){
    					alert('同学你好!')
    				},
    				showMsg(msg){
    					console.log(msg)
    				},
    				demo(){
    					for (let i = 0; i < 100000; i++) {
    						console.log('#')
    					}
    					console.log('累坏了')
    				}
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    Vue中的事件修饰符:

    @H_741_404@
  • prevent:阻止认事件(常用
  • stop:阻止事件冒泡(常用
  • once:事件只触发一次(常用
  • capture:使用事件的捕获模式
  • self:只有event.target是当前操作的元素时才触发事件
  • passive:事件的认行为立即执行,无需等待事件回调执行完毕
  • @H_326_419@

    修饰符可以连续写,比如可以这么用:@click.prevent.stop="showInfo"

    1.8.3. 键盘事件

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>键盘事件</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>欢迎来到{{name}}学习</h2>
    			<input type="text" placeholder="按下回车提示输入" @keydown.enter="showInfo">
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			methods: {
    				showInfo(e){
    					console.log(e.target.value)
    				}
    			},
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    键盘上的每个按键都有自己的名称和编码,例如:Enter(13)。而Vue还对一些常用按键起了别名方便使用

    Vue中常用的按键别名

    • 回车:enter
    • 删除:delete (捕获“删除”和“退格”键)
    • 退出:esc
    • 空格:space
    • 换行:tab (特殊,必须配合keydown去使用)
    • 上:up
    • 下:down
    • 左:left
    • 右:right

    注意:

    @H_741_404@
  • 系统修饰键(用法特殊):ctrl、alt、shift、Meta

    • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
    • 配合keydown使用:正常触发事件
  • 可以使用keyCode去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用

  • Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名

  • @H_326_419@

    1.9. 计算属性

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>计算属性</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            姓:<input type="text" v-model="firstname"><br><br>
            名:<input type="text" v-model="lastname"><br><br>
            姓名:<span>{{fullname}}</span>
        </div>
    
        <script>
            Vue.config.productionTip = false 
    
            new Vue({
                el:'#root', 
                data:{ 
                    firstname:'张',
                    lastname:'三'
                },
                computed:{
                    fullname:{
                        get(){
                            return this.firstname + '-' + this.lastname
                        },
                        set(value){
    						const arr = value.split('-')
    						this.firstname = arr[0]
    						this.lastname = arr[1]
                        }
                    }
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    new Vue({
        el:'#root',data:{ 
            firstname:'张',lastname:'三'
        },computed:{
        	fullname(){
    		    return this.firstname + '-' + this.lastname
        	}
        }
    })
    

    1.10. 监视属性

    1.10.1. 监视属性基本用法

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>监视属性</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h2>今天天气好{{info}}!</h2>
            <@R_502_5554@ @click="changeWeather">点击切换天气</@R_502_5554@>
        </div>
    
        <script>
            Vue.config.productionTip = false 
    
            new Vue({
                el:'#root', 
                data:{ 
                    isHot:true,
                },
                computed:{
                    info(){
                        return this.isHot ? '炎热' : '凉爽' 
                    }
                },
                methods:{
    				changeWeather(){
    					this.isHot = !this.isHot
    				}
    			},
                watch:{
                    isHot:{
                        immediate:true, //初始化时让handler调用一下
                        //handler什么时候调用?当isHot发生改变时
                        handler(newValue,oldValue){
    						console.log('isHot被修改了',newValue,oldValue)
    					}
                    }
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    监视属性watch:

    @H_741_404@
  • 当被监视的属性变化时,回调函数自动调用进行相关操作
  • 监视的属性必须存在,才能进行监视
  • 监视有两种写法: @H_741_404@
  • 创建Vue时传入watch配置
  • 通过vm.$watch监视
  • @H_326_419@ @H_326_419@
    vm.$watch('isHot',{
    	immediate:true,handler(newValue,oldValue){
    		console.log('isHot被修改了',newValue,oldValue)
    	}
    })
    

    1.10.2. 深度监视

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>深度监视</Title>
        <script src="../Js/vue.Js"></script>
    </head>
    <body>
        <div ID="root">
            <h3>a的值是:{{numbers.a}}</h3>
    		<@R_502_5554@ @click="numbers.a++">点我让a+1</@R_502_5554@>
    		<h3>b的值是:{{numbers.b}}</h3>
    		<@R_502_5554@ @click="numbers.b++">点我让b+1</@R_502_5554@>
        </div>
    
        <script>
            Vue.config.productionTip = false 
    
            new Vue({
                el:'#root',
                    numbers:{
                        a:1,
                        b:1,
                    }
                },
                watch:{
                    //监视多级结构中所有属性的变化
                    numbers:{
                        deep:true,
    					handler(){
    						console.log('numbers改变了')
    					}
                    }
                    //监视多级结构中某个属性的变化
    				/* 'numbers.a':{
    					handler(){
    						console.log('a被改变了')
    					}
    				} */
                }
            })
        </script>
    </body>
    </HTML>
    

    效果

    总结:

    • 深度监视:

      @H_741_404@
    • Vue中的watch认不监测对象内部值的改变(一层)
    • 在watch中配置deep:true可以监测对象内部值的改变(多层)
    • @H_326_419@
    • 备注:

      @H_741_404@
    • Vue自身可以监测对象内部值的改变,但Vue提供的watch认不可以
    • 使用watch时根据监视数据的具体结构,决定是否采用深度监视
    • @H_326_419@

    1.10.3. 监视属性简写

    如果监视属性除了handler没有其他配置项的话,可以进行简写。

    <script type="text/JavaScript">
    	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
    		
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true,
            },
            computed:{
                info(){
                    return this.isHot ? '炎热' : '凉爽'
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            watch:{
                //正常写法
                isHot:{
    				handler(newValue,oldValue){
    					console.log('isHot被修改了',oldValue)
    				}
    			}, 
                //简写
                isHot(newValue,oldValue){
    				console.log('isHot被修改了',oldValue,this)
    			}
            }
        })
    
        //正常写法
        vm.$watch('isHot',{
            handler(newValue,oldValue){
                console.log('isHot被修改了',oldValue)
            }
        })
        //简写
        vm.$watch('isHot',function(newValue,oldValue){
            console.log('isHot被修改了',this)
        })
    </script>
    

    1.10.4. 监听属性 VS 计算属性

    使用计算属性

    new Vue({
        el:'#root',computed:{
        	fullname(){
    		    return this.firstname + '-' + this.lastname
        	}
        }
    })
    

    使用监听属性

    new Vue({
    	el:'#root',data:{
    		firstname:'张',lastname:'三',fullname:'张-三'
    	},watch:{
    		firstname(val){
    			setTimeout(()=>{
    				this.fullname = val + '-' + this.lastname
    			},1000);
    		},lastname(val){
    			this.fullname = this.firstname + '-' + val
    		}
    	}
    })
    

    总结:

    1.11. 绑定样式

    <style>
        .basic{
            wIDth: 400px;
            height: 100px;
            border: 1px solID black;
        }
        .happy{
            border: 4px solID red;;
            background-color: rgba(255, 255, 0, 0.644);
            background: linear-gradIEnt(30deg,yellow,pink,orange,yellow);
        }
        .sad{
            border: 4px dashed rgb(2, 197, 2);
            background-color: gray;
        }
        .normal{
            background-color: skyblue;
        }
    
        .atguigu1{
            background-color: yellowgreen;
        }
        .atguigu2{
            Font-size: 30px;
            text-shadow:2px 2px 10px red;
        }
        .atguigu3{
            border-radius: 20px;
        }
    </style>
    
    <div ID="root">
        <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
        <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
    
        <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
        <div class="basic" :class="classArr">{{name}}</div> <br/><br/>
    
        <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
        <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
    
        <!-- 绑定style样式--对象写法 -->
        <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
    
        <!-- 绑定style样式--数组写法 -->
        <div class="basic" :style="styleArr">{{name}}</div>
    </div>
    
    <script type="text/JavaScript">
    	Vue.config.productionTip = false
    		
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                mood:'normal',
                classArr:['atguigu1','atguigu2','atguigu3'],
                classObj:{
                    atguigu1:false,
                    atguigu2:false,
                styleObj:{
                    FontSize: '40px',
                    color:'red',
                styleObj2:{
                    backgroundcolor:'orange'
                },
                styleArr:[
                    {
                        FontSize: '40px',
                        color:'blue',
                    },
                    {
                        backgroundcolor:'gray'
                    }
                ]
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal']
                    const index = Math.floor(Math.random()*3)
                    this.mood = arr[index]
                }
            },
        })
    </script>
    

    效果

    image-20210717181454771

    总结:

    @H_741_404@
  • class样式

    • 写法:class="xxx",xxx可以是字符串、对象、数组

    • 字符串写法适用于:类名不确定,要动态获取

    • 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定

    • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用

  • style样式

    • :style="{FontSize: xxx}"其中xxx是动态值
    • :style="[a,b]"其中a、b是样式对象
  • @H_326_419@

    1.12. 条件渲染

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>条件渲染</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>当前的n值是:{{n}}</h2>
    			<@R_502_5554@ @click="n++">点我n+1</@R_502_5554@>
    
    			<h2 v-show="true">Hello,{{name}}!</h2>
    
    			<div v-if="n === 1">Angular</div>
    			<div v-else-if="n === 2">React</div>
    			<div v-else>Vue</div>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		const vm = new Vue({
    			el:'#root',
    			data:{
    				name:'jojo',
    				n:0
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    @H_741_404@
  • v-if:

    • 写法:

      @H_741_404@
    • v-if="表达式"
    • v-else-if="表达式"
    • v-else
    • @H_326_419@
    • 适用于:切换频率较低的场景

    • 特点:不展示的DOM元素直接被移除

    • 注意:v-if可以和v-else-ifv-else一起使用,但要求结构不能被打断

  • v-show:

    • 写法:v-show="表达式"
    • 适用于:切换频率较高的场景
    • 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
  • @H_326_419@

    使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取

    1.13. 列表渲染

    1.13.1. 基本列表

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>基本列表</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>人员列表(遍历数组)</h2>
    			<ul>
    				<li v-for="(p,index) in persons" :key="index">
    					{{p.name}}-{{p.age}}
    				</li>
    			</ul>
    
    			<h2>汽车信息(遍历对象)</h2>
    			<ul>
    				<li v-for="(value,k) in car" :key="k">
    					{{k}}-{{value}}
    				</li>
    			</ul>
    
    			<h2>遍历字符串</h2>
    			<ul>
    				<li v-for="(char,index) in str" :key="index">
    					{{char}}-{{index}}
    				</li>
    			</ul>
    			
    			<h2>遍历指定次数</h2>
    			<ul>
    				<li v-for="(number,index) in 5" :key="index">
    					{{index}}-{{number}}
    				</li>
    			</ul>
    		</div>
    
    		<script type="text/JavaScript">
    			Vue.config.productionTip = false
    			
    			new Vue({
    				el:'#root',
    				data:{
    					persons:[
    						{ID:'001',name:'张三',age:18},
    						{ID:'002',name:'李四',age:19},
    						{ID:'003',name:'王五',age:20}
    					],
    					car:{
    						name:'奥迪A8',
    						price:'70万',
    						color:'黑色'
    					},
    					str:'hello'
    				}
    			})
    		</script>
        </body>
    </HTML>
    

    效果

    总结:

    v-for指令:

    @H_741_404@
  • 用于展示列表数据
  • 语法:<li v-for="(item,index) in xxx" :key="yyy">,其中key可以是index,也可以是遍历对象的唯一标识
  • 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
  • @H_326_419@

    1.13.2. key的作用与原理

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>key的原理</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>人员列表</h2>
    			<@R_502_5554@ @click.once="add">添加老刘</@R_502_5554@>
    			<ul>
    				<li v-for="(p,index) in persons" :key="index">
    					{{p.name}} - {{p.age}}
    					<input type="text">
    				</li>
    			</ul>
    		</div>
    
    		<script type="text/JavaScript">
    			Vue.config.productionTip = false
    			
    			new Vue({
    				el:'#root',age:20}
    					]
    				},
    				methods: {
    					add(){
    						const p = {ID:'004',name:'老刘',age:40}
    						this.persons.unshift(p)
    					}
    				},
    			})
    		</script>
    </HTML>
    

    效果

    原理:

    image-20210718113900024

    image-20210718114304512

    面试题:react、vue中的key有什么作用?(key的内部原理)

    @H_741_404@
  • 虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  • 对比规则:

    @H_741_404@
  • 旧虚拟DOM中找到了与新虚拟DOM相同的key:

    @H_741_404@
  • 若虚拟DOM中内容没变,直接使用之前的真实DOM
  • 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
  • @H_326_419@
  • 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面

  • @H_326_419@
  • 用index作为key可能会引发的问题

    @H_741_404@
  • 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果问题,但效率低
  • 若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
  • @H_326_419@
  • 开发中如何选择key?

    @H_741_404@
  • 最好使用每条数据的唯一标识作为key,比如ID手机号、身份证号、学号等唯一值
  • 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题
  • @H_326_419@ @H_326_419@

    1.13.3. 列表过滤

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>列表过滤</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>人员列表</h2>
    			<input type="text" placeholder="请输入名字" v-model="keyWord">
    			<ul>
    				<li v-for="(p,index) of filPersons" :key="index">
    					{{p.name}}-{{p.age}}-{{p.sex}}
    				</li>
    			</ul>
    		</div>
    
    		<script type="text/JavaScript">
    			Vue.config.productionTip = false
    
    			new Vue({
    				el:'#root',
    				data:{
    					keyWord:'',
    					persons:[
    						{ID:'001',name:'马冬梅',age:19,sex:'女'},name:'周冬雨',age:20,name:'周杰伦',age:21,sex:'男'},
    						{ID:'004',name:'温兆伦',age:22,sex:'男'}
    					]
    				},
    				computed:{
    					filPersons(){
    						return this.persons.filter((p)=>{
    							return p.name.indexOf(this.keyWord) !== -1
    						})
    					}
    				}
    			})
    		</script>
    	</body>
    </HTML>
    

    效果

    1.13.4. 列表排序

    <body>
        <div ID="root">
            <h2>人员列表</h2>
            <input type="text" placeholder="请输入名字" v-model="keyWord">
            <@R_502_5554@ @click="sortType = 2">年龄升序</@R_502_5554@>
    		<@R_502_5554@ @click="sortType = 1">年龄降序</@R_502_5554@>
    		<@R_502_5554@ @click="sortType = 0">原顺序</@R_502_5554@>
            <ul>
                <li v-for="(p,index) of filPersons" :key="p.ID">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
        
        <script>
            new Vue({
                el:'#root',
                data:{
                    persons:[
                        {ID:'001',age:30,
    				    {ID:'002',age:45,
    				    {ID:'003',
    				    {ID:'004',sex:'男'}
                    ],
                    keyWord:'',
                    sortType:0,//0代表原顺序,1代表升序,3代表降序
                },
                computed:{
                    filPersons(){
                        const arr = this.persons.filter((p)=>{
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                        if(this.sortType){
                            arr.sort((p1, p2)=>{
                                return this.sortType ===1 ? p2.age-p1.age : p1.age-p2.age
                            })
                        }
                        return arr
                    }
                }
            })
        </script>
    </body>
    

    效果

    1.13.5. Vue数据监视

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>Vue数据监视</Title>
    		<style>
    			@R_502_5554@{
    				margin-top: 10px;
    			}
    		</style>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h1>学生信息</h1>
    			<@R_502_5554@ @click="student.age++">年龄+1岁</@R_502_5554@><br/>
    			<@R_502_5554@ @click="addSex">添加性别属性认值:男</@R_502_5554@> <br/>
    			<@R_502_5554@ @click="addFrIEnd">在列表首位添加一个朋友</@R_502_5554@> <br/>
    			<@R_502_5554@ @click="updateFirstFrIEndname">修改一个朋友的名字为:张三</@R_502_5554@><br/>
    			<@R_502_5554@ @click="addHobby">添加一个爱好</@R_502_5554@> <br/>
    			<@R_502_5554@ @click="updateHobby">修改一个爱好为:开车</@R_502_5554@><br/>
    			<@R_502_5554@ @click="removeSmoke">过滤掉爱好中的抽烟</@R_502_5554@> <br/>
    			<h3>姓名:{{student.name}}</h3>
    			<h3>年龄:{{student.age}}</h3>
    			<h3 v-if="student.sex">性别:{{student.sex}}</h3>
    			<h3>爱好:</h3>
    			<ul>
    				<li v-for="(h,index) in student.hobby" :key="index">
    					{{h}}
    				</li>
    			</ul>
    			<h3>朋友们:</h3>
    			<ul>
    				<li v-for="(f,index) in student.frIEnds" :key="index">
    					{{f.name}}--{{f.age}}
    				</li>
    			</ul>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
    
    		const vm = new Vue({
    			el:'#root',
    			data:{
    				student:{
    					name:'tom',
    					age:18,
    					hobby:['抽烟','喝酒','烫头'],
    					frIEnds:[
    						{name:'jerry',age:35},
    						{name:'tony',age:36}
    					]
    				}
    			},
    			methods: {
    				addSex(){
    					//Vue.set(this.student,'sex','男')
    					this.$set(this.student,'sex','男')
    				},
    				addFrIEnd(){
    					this.student.frIEnds.unshift({name:'jack',age:70})
    				},
    				updateFirstFrIEndname(){
    					this.student.frIEnds[0].name = '张三'
    				},
    				addHobby(){
    					this.student.hobby.push('学习')
    				},
    				updateHobby(){
    					this.student.hobby.splice(0,1,'开车')
    				},
    				removeSmoke(){
    					this.student.hobby = this.student.hobby.filter((h)=>{
    						return h !== '抽烟'
    					})
    				}
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    Vue监视数据的原理:

    @H_741_404@
  • vue会监视data中所有层次的数据

  • 如何监测对象中的数据?

    通过setter实现监视,且要在new Vue时就传入要监测的数据

    @H_741_404@
  • 对象中后追加的属性,Vue认不做响应式处理
  • 如需给后添加属性做响应式,请使用如下API
    • Vue.set(target,propertyname/index,value)
    • vm.$set(target,value)
  • @H_326_419@
  • 如何监测数组中的数据?

    通过包裹数组更新元素的方法实现,本质就是做了两件事:

    @H_741_404@
  • 调用原生对应的方法对数组进行更新
  • 重新解析模板,进而更新页面
  • @H_326_419@
  • 在Vue修改数组中的某个元素一定要用如下方法

    @H_741_404@
  • 使用这些APIpush()pop()shift()unshift()splice()sort()reverse()
  • Vue.set()vm.$set()
  • @H_326_419@ @H_326_419@

    特别注意Vue.set()vm.$set() 不能给vm 或 vm的根数据对象(data等) 添加属性

    1.14. 收集表单数据

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>收集表单数据</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<form @submit.prevent="demo">
    				账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
    				密码:<input type="password" v-model="userInfo.password"> <br/><br/>
    				年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
    				性别:
    				男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
    				爱好:
    				学习<input type="checkBox" v-model="userInfo.hobby" value="study">
    				打游戏<input type="checkBox" v-model="userInfo.hobby" value="game">
    				吃饭<input type="checkBox" v-model="userInfo.hobby" value="eat">
    				<br/><br/>
    				所属校区:
    				<select v-model="userInfo.city">
    					<option value="">选择校区</option>
    					<option value="beijing">北京</option>
    					<option value="shanghai">上海</option>
    					<option value="shenzhen">深圳</option>
    					<option value="wuhan">武汉</option>
    				</select>
    				<br/><br/>
    				其他信息:
    				<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
    				<input type="checkBox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">用户协议》</a>
    				<@R_502_5554@>提交</@R_502_5554@>
    			</form>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			data:{
    				userInfo:{
    					account:'',
    					password:'',
    					age:0,
    					sex:'female',
    					hobby:[],
    					city:'beijing',
    					other:'',
    					agree:''
    				}
    			},
    			methods: {
    				demo(){
    					console.log(JsON.stringify(this.userInfo))
    				}
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    收集表单数据:

    • 若:<input type="text"/>,则v-model收集的是value值,用户输入的内容就是value值
    • 若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value属性
    • 若:<input type="checkBox"/>
      • 没有配置value属性,那么收集的是checked属性(勾选 or 未勾选,是布尔值)
      • 配置了value属性
        • v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
        • v-model的初始值是数组,那么收集的就是value组成的数组

    v-model的三个修饰符:

    @H_741_404@
  • lazy:失去焦点后再收集数据
  • number:输入字符串转为有效的数字
  • trim:输入首尾空格过滤
  • @H_326_419@

    1.15. 过滤器

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>过滤器</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    		<script src="https://cdn.bootcdn.net/AJAX/libs/dayJs/1.10.6/dayJs.min.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2>时间</h2>
                <h3>当前时间戳:{{time}}</h3>
                <h3>转换后时间:{{time | timeFormater()}}</h3>
    			<h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3>
    			<h3>截取年月日:{{time | timeFormater() | mySlice}}</h3>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    		//全局过滤器
    		Vue.filter('mySlice',function(value){
    			return value.slice(0,11)
    		})
    		new Vue({
                el:'#root',
                data:{
                    time:1626750147900,
    			//局部过滤器
                filters:{
                    timeFormater(value, str="YYYY年MM月DD日 HH:mm:ss"){
                        return dayJs(value).format(str)
                    }
                }
            })
    	</script>
    </HTML>
    

    效果

    总结:

    过滤器:

    • 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

    • 语法:

      @H_741_404@
    • 注册过滤器:Vue.filter(name,callback)new Vue{filters:{}}

    • 使用过滤器:{{ xxx | 过滤器名}}v-bind:属性 = "xxx | 过滤器名"

    • @H_326_419@
    • 备注:

      @H_741_404@
    • 过滤器可以接收额外参数,多个过滤器也可以串联
    • 并没有改变原本的数据,而是产生新的对应的数据
    • @H_326_419@

    1.16. 内置指令

    1.16.1. v-text指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>v-text指令</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<div>你好,{{name}}</div>
    			<div v-text="name"></div>
    			<div v-text="str"></div>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false 
    		
    		new Vue({
    			el:'#root',
    			data:{
    				name:'JOJO',
    				str:'<h3>你好啊!</h3>'
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    • 之前学过的指令:

      • v-bind:单向绑定解析表达式,可简写为:
      • v-model:双向数据绑定
      • v-for:遍历数组 / 对象 / 字符串
      • v-on:绑定事件监听,可简写为@
      • v-if条件渲染(动态控制节点是否存存在)
      • v-else条件渲染(动态控制节点是否存存在)
      • v-show条件渲染 (动态控制节点是否展示)
    • v-text指令:

      @H_741_404@
    • 作用:向其所在的节点中渲染文本内容

    • 与插值语法的区别v-text替换掉节点中的内容{{xx}}则不会。

    • @H_326_419@

    1.16.2. v-HTML指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>v-HTML指令</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<div>Hello,{{name}}</div>
    			<div v-HTML="str"></div>
    			<div v-HTML="str2"></div>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
    
    		new Vue({
    			el:'#root',
    				str:'<h3>你好啊!</h3>',
    				str2:'<a href=JavaScript:location.href="http://www.baIDu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    v-HTML指令:

    @H_741_404@
  • 作用:向指定节点中渲染包含HTML结构的内容

  • 与插值语法的区别

    @H_741_404@
  • v-HTML会替换掉节点中所有的内容{{xx}}则不会
  • v-HTML可以识别HTML结构
  • @H_326_419@
  • 严重注意:v-HTML安全问题!!!

    @H_741_404@
  • 网站上动态渲染任意HTML非常危险的,容易导致XSS攻击
  • 一定要在可信的内容使用v-HTML永远不要用在用户提交的内容上!!!
  • @H_326_419@ @H_326_419@

    1.16.3. v-cloak指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>v-cloak指令</Title>
    		<style>
    			[v-cloak]{
    				display:none;
    			}
    		</style>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 v-cloak>{{name}}</h2>
    		</div>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</body>
    	
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    		
    		new Vue({
    			el:'#root',
    			data:{
    				name:'尚硅谷'
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    v-cloak指令(没有值):

    @H_741_404@
  • 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
  • 使用CSS配合v-cloak可以解决网速慢时页面展示出{{xxx}}问题
  • @H_326_419@

    1.16.4. v-once指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>v-once指令</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 v-once>n初始化的值是:{{n}}</h2>
                <h2>n现在的值是:{{n}}</h2>
                <@R_502_5554@ @click="n++">点我n+1</@R_502_5554@>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false 
    		
    		new Vue({
    			el:'#root',
    			data:{
    				n:1
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    v-once指令:

    @H_741_404@
  • v-once所在节点在初次动态渲染后,就视为静态内容

  • 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

  • @H_326_419@

    1.16.5. v-pre指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>v-pre指令</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 v-pre>Vue其实很简单</h2>
    			<h2>当前的n值是:{{n}}</h2>
    			<@R_502_5554@ @click="n++">点我n+1</@R_502_5554@>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			data:{
    				n:1
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    v-pre指令:

    @H_741_404@
  • 跳过其所在节点的编译过程
  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
  • @H_326_419@

    1.17. 自定义指令

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>自定义指令</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
        <!-- 
    		需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
    		需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素获取焦点。
    	-->
    	<body>
    		<div ID="root">
    			<h2>当前的n值是:<span v-text="n"></span> </h2>
    			<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
    			<@R_502_5554@ @click="n++">点我n+1</@R_502_5554@>
    			<hr/>
    			<input type="text" v-fbind:value="n">
    		</div>
    	</body>
    	
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			data:{
    				n:1
    			},
    			directives:{
                    //big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析
    				big(element,binding){
    					console.log('big',this) //注意此处的this是window
    					element.innerText = binding.value * 10
    				},
    				fbind:{
    					//指令与元素成功绑定时(一上来)
    					bind(element,binding){
    						element.value = binding.value
    					},
    					//指令所在元素被插入页面
    					inserted(element,binding){
    						element.focus()
    					},
    					//指令所在的模板被重新解析
    					update(element,binding){
    						element.value = binding.value
    					}
    				}
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    @H_741_404@
  • 自定义指令定义语法:

    @H_741_404@
  • 局部指令:

    @H_741_404@
  •  new Vue({															
     	directives:{指令名:配置对象}   
     }) 		
    
  •  new Vue({															
     	directives:{指令名:回调函数}   
     }) 	
    
  • @H_326_419@
  • 全局指令:

    @H_741_404@
  • Vue.directive(指令名,配置对象)
  • Vue.directive(指令名,回调函数)
  • @H_326_419@

    例如:

    Vue.directive('fbind',{
    	//指令与元素成功绑定时(一上来)
    	bind(element,binding){
    		element.value = binding.value
    	},
        //指令所在元素被插入页面
        inserted(element,binding){
        	element.focus()
        },
        //指令所在的模板被重新解析
        update(element,binding){
        	element.value = binding.value
        }
    })
    
    @H_326_419@
  • 配置对象中常用的3个回调函数

    @H_741_404@
  • bind(element,binding):指令与元素成功绑定时调用
  • inserted(element,binding):指令所在元素被插入页面调用
  • update(element,binding):指令所在模板结构被重新解析调用
  • @H_326_419@
  • 备注:

    @H_741_404@
  • 指令定义时不加“v-”,但使用时要加“v-”

  • 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名

    new Vue({
    	el:'#root',
    	data:{
    		n:1
    	},
    	directives:{
    		'big-number'(element,binding){
    			element.innerText = binding.value * 10
    		}
    	}
    })
    
  • @H_326_419@ @H_326_419@

    1.18. Vue生命周期

    1.18.1. 引出生命周期

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>引出生命周期</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 v-if="a">你好啊</h2>
    			<h2 :style="{opacity}">欢迎学习Vue</h2>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false 
    
    		 new Vue({
    			el:'#root',
    			data:{
    				a:false,
    				opacity:1
    			},
    			mounted(){
    				console.log('mounted',this)
    				setInterval(() => {
    					this.opacity -= 0.01
    					if(this.opacity <= 0) this.opacity = 1
    				},16)
    			},
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    生命周期:

    @H_741_404@
  • 又名:生命周期回调函数、生命周期函数、生命周期钩子
  • 是什么:Vue在关键时刻帮我们调用的一些特殊名称函数
  • 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
  • 生命周期函数中的this指向是vm组件实例对象
  • @H_326_419@

    1.18.2. 分析生命周期

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>分析生命周期</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 v-text="n"></h2>
    			<h2>当前的n值是:{{n}}</h2>
    			<@R_502_5554@ @click="add">点我n+1</@R_502_5554@>
    			<@R_502_5554@ @click="bye">点我销毁vm</@R_502_5554@>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		new Vue({
    			el:'#root',
    			// template:`
    			// 	<div>
    			// 		<h2>当前的n值是:{{n}}</h2>
    			// 		<@R_502_5554@ @click="add">点我n+1</@R_502_5554@>
    			// 	</div>
    			// `,
    			methods: {
    				add(){
    					console.log('add')
    					this.n++
    				},
    				bye(){
    					console.log('bye')
    					this.$destroy()
    				}
    			},
    			watch:{
    				n(){
    					console.log('n变了')
    				}
    			},
    			beforeCreate() {
    				console.log('beforeCreate')
    			},
    			created() {
    				console.log('created')
    			},
    			beforeMount() {
    				console.log('beforeMount')
    			},
    			mounted() {
    				console.log('mounted')
    			},
    			beforeUpdate() {
    				console.log('beforeUpdate')
    			},
    			updated() {
    				console.log('updated')
    			},
    			beforeDestroy() {
    				console.log('beforeDestroy')
    			},
    			destroyed() {
    				console.log('destroyed')
    			},
    		})
    	</script>
    </HTML>
    

    效果

    1.18.3. 总结生命周期

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>引出生命周期</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h2 :style="{opacity}">欢迎学习Vue</h2>
    			<@R_502_5554@ @click="opacity = 1">透明设置为1</@R_502_5554@>
    			<@R_502_5554@ @click="stop">点我停止变换</@R_502_5554@>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false 
    
    		 new Vue({
    			el:'#root',
    			data:{
    				opacity:1
    			},
    			methods: {
    				stop(){
    					this.$destroy()
    				}
    			},this)
    				this.timer = setInterval(() => {
    					console.log('setInterval')
    					this.opacity -= 0.01
    					if(this.opacity <= 0) this.opacity = 1
    				},
    			beforeDestroy() {
    				clearInterval(this.timer)
    				console.log('vm即将驾鹤西游了')
    			},
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    常用的生命周期钩子:

    @H_741_404@
  • mounted:发送AJAX请求、启动定时器、绑定自定义事件、订阅消息等初始化操作

  • beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等收尾工作

  • @H_326_419@

    关于销毁Vue实例:

    @H_741_404@
  • 销毁后借助Vue开发者工具看不到任何信息

  • 销毁后自定义事件会失效,但原生DOM事件依然有效

  • 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了

  • @H_326_419@

    2. Vue组件化编程

    2.1. 模块与组件、模块化与组件化

    image-20210723115936262

    image-20210723120028543

    2.1.1. 模块

    @H_741_404@
  • 理解:向外提供特定功能Js 程序,一般就是一个 Js 文件
  • 为什么:Js 文件很多很复杂
  • 作用:复用 Js,简化 Js 的编写,提高 Js 运行效率
  • @H_326_419@

    2.1.2. 组件

    @H_741_404@
  • 定义:用来实现局部功能代码资源的集合(HTML/CSS/Js/image…)
  • 为什么:一个界面的功能很复杂
  • 作用:复用编码,简化项目编码,提高运行效率
  • @H_326_419@

    2.1.3. 模块化

    当应用中的 Js 都以模块来编写的,那这个应用就是一个模块化的应用

    2.1.4. 组件化

    当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用

    2.2. 非单文件组件

    2.2.1. 基本使用

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>基本使用</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h1>{{msg}}</h1>
    			<hr>
    			<!-- 第三步:编写组件标签 -->
    			<school></school>
    			<hr>
    			<!-- 第三步:编写组件标签 -->
    			<student></student>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    
    		//第一步:创建school组件
    		const school = Vue.extend({
                //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
    			template:`
    				<div class="demo">
    					<h2>学校名称:{{schoolname}}</h2>
    					<h2>学校地址:{{address}}</h2>	
    				</div>
    			`,
    			data(){
    				return {
    					schoolname:'尚硅谷',
    					address:'北京昌平'
    				}
    			}
    		})
    
    		//第一步:创建student组件
    		const student = Vue.extend({
    			template:`
    				<div>
    					<h2>学生姓名:{{studentname}}</h2>
    					<h2>学生年龄:{{age}}</h2>
    				</div>
    			`,
    			data(){
    				return {
    					studentname:'JOJO',
    					age:20
    				}
    			}
    		})
    		
    		//创建vm
    		new Vue({
    			el:'#root',
    			data:{
    				msg:'你好,JOJO!'
    			},
    			//第二步:注册组件(局部注册
    			components:{
    				school,
    				student
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    • Vue中使用组件的三大步骤:

      @H_741_404@
    • 定义组件(创建组件)
    • 注册组件
    • 使用组件(写组件标签)
    • @H_326_419@
    • 如何定义一个组件?

      使用Vue.extend(options)创建,其中optionsnew Vue(options)时传入的options几乎一样,但也有点区别

      @H_741_404@
    • el不要写,为什么?

      最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器

    • data必须写成函数,为什么?

      避免组件被复用时,数据存在引用关系

    • @H_326_419@
    • 如何注册组件?

      @H_741_404@
    • 局部注册new Vue的时候传入components选项
    • 全局注册Vue.component('组件名',组件)
    • @H_326_419@
    • 编写组件标签<school></school>

    2.2.2. 组件注意事项

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>组件注意事项</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<h1>{{msg}}</h1>
    			<school></school>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    		
    		const school = Vue.extend({
    			name:'atguigu',
    			template:`
    				<div>
    					<h2>学校名称:{{name}}</h2>	
    					<h2>学校地址:{{address}}</h2>	
    				</div>
    			`,
    			data(){
    				return {
    					name:'尚硅谷',
    					address:'北京'
    				}
    			}
    		})
    
    		new Vue({
    			el:'#root',
    			data:{
    				msg:'欢迎学习Vue!'
    			},
    			components:{
    				school
    			}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    • 关于组件名:

      • 一个单词组成:

        • 第一种写法(首字母小写):school
        • 第二种写法(首字母大写):School
      • 多个单词组成:

        • 第一种写法(kebab-case命名):my-school
        • 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持
      • 备注:

        • 组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
        • 可以使用name配置项指定组件在开发者工具中呈现的名字
    • 关于组件标签

      • 第一种写法:<school></school>
      • 第二种写法:<school/>
      • 备注:不使用脚手架时,<school/>会导致后续组件不能渲染
    • 一个简写方式:const school = Vue.extend(options)可简写为:const school = options

    2.2.3. 组件的嵌套

    组件的嵌套

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>组件的嵌套</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    		
    		//定义student组件
    		const student = Vue.extend({
    			template:`
    				<div>
    					<h2>学生名称:{{name}}</h2>	
    					<h2>学生年龄:{{age}}</h2>	
    				</div>
    			`,
    			data(){
    				return {
    					name:'JOJO',
    					age:20
    				}
    			}
    		})
    
    		//定义school组件
    		const school = Vue.extend({
    			template:`
    				<div>
    					<h2>学校名称:{{name}}</h2>	
    					<h2>学校地址:{{address}}</h2>	
    					<student></student>
    				</div>
    			`,
    			components:{
    				student
    			},
    					address:'北京'
    				}
    			}
    		})
    
    		//定义hello组件
    		const hello = Vue.extend({
    			template:`
    				<h1>{{msg}}</h1>
    			`,
    			data(){
    				return {
    					msg:"欢迎学习尚硅谷Vue教程!"
    				}
    			}
    		})
    
    		//定义app组件
    		const app = Vue.extend({
    			template:`
    				<div>
    					<hello></hello>
    					<school></school>
    				</div>
    			`,
    			components:{
    				school,
    				hello
    			}
    		})
    
    		//创建vm
    		new Vue({
    			template:`
    				<app></app>
    			`,
    			el:'#root',
    			components:{
    				app
    			}
    		})
    	</script>
    </HTML>
    

    效果

    2.2.4. VueComponent

    关于VueComponent:

    @H_741_404@
  • school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成

  • 我们只需要写<school/><school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)

  • 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!

  • 关于this指向:

    @H_741_404@
  • 组件配置中:data函数methods中的函数watch中的函数computed中的函数 它们的this均是VueComponent实例对象
  • new Vue(options)配置中:data函数methods中的函数watch中的函数computed中的函数 它们的this均是Vue实例对象
  • @H_326_419@
  • VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)

    Vue的实例对象,以后简称vm

    只有在本笔记中VueComponent的实例对象才简称为vc

  • @H_326_419@

    2.2.5. 一个重要的内置关系

    <!DOCTYPE HTML>
    <HTML>
    	<head>
    		<Meta charset="UTF-8" />
    		<Title>一个重要的内置关系</Title>
    		<script type="text/JavaScript" src="../Js/vue.Js"></script>
    	</head>
    	<body>
    		<div ID="root">
    			<school></school>
    		</div>
    	</body>
    
    	<script type="text/JavaScript">
    		Vue.config.productionTip = false
    		Vue.prototype.x = 99
    
    		const school = Vue.extend({
    			name:'school',
    			template:`
    				<div>
    					<h2>学校名称:{{name}}</h2>	
    					<h2>学校地址:{{address}}</h2>	
    					<@R_502_5554@ @click="showX">点我输出x</@R_502_5554@>
    				</div>
    			`,
    					address:'北京'
    				}
    			},
    			methods: {
    				showX(){
    					console.log(this.x)
    				}
    			},
    		})
    
    		const vm = new Vue({
    			el:'#root',
    			data:{
    				msg:'你好'
    			},
    			components:{school}
    		})
    	</script>
    </HTML>
    

    效果

    总结:

    image-20210730113353890

    @H_741_404@
  • 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
  • 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性方法
  • @H_326_419@

    2.3. 单文件组件

    • School.vue:

      <template>
          <div ID='Demo'>
              <h2>学校名称:{{name}}</h2>
              <h2>学校地址:{{address}}</h2>
              <@R_502_5554@ @click="showname">点我提示学校名</@R_502_5554@>
          </div>
      </template>
      
      <script>
          export default {
              name:'School',data() {
                  return {
                      name:'尚硅谷',address:'北京'
                  }
              },methods: {
                  showname(){
                      alert(this.name)
                  }
              },}
      </script>
      
      <style>
          #Demo{
              background: orange;
          }
      </style>
      
    • Student.vue:

      <template>
          <div>
              <h2>学生姓名:{{name}}</h2>
              <h2>学生年龄:{{age}}</h2>
          </div>
      </template>
      
      <script>
          export default {
              name:'Student',data() {
                  return {
                      name:'JOJO',age:20
                  }
              },}
      </script>
      
    • App.vue:

      <template>
          <div>
              <School></School>
              <Student></Student>
          </div>
      </template>
      
      <script>
          import School from './School.vue'
          import Student from './Student.vue'
      
          export default {
              name:'App',components:{
                  School,Student
              }
          }
      </script>
      
    • main.Js:

      import App from './App.vue'
      
      new Vue({
          template:`<App></App>`,
          el:'#root',
          components:{App}
      })
      
    • index.HTML

      <!DOCTYPE HTML>
      <HTML lang="en">
      <head>
          <Meta charset="UTF-8">
          <Meta http-equiv="X-UA-Compatible" content="IE=edge">
          <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
          <Title>文件组件练习</Title>
      </head>
      <body>
          <div ID="root"></div>
          <script src="../../Js/vue.Js"></script>
          <script src="./main.Js"></script>
      </body>
      </HTML>
      

    3. 使用Vue Cli脚手架

    3.1. 初始化脚手架

    3.1.1. 说明

    @H_741_404@
  • Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
  • 最新的版本是 4.x
  • 文档:Vue CLI
  • @H_326_419@

    3.1.2. 具体步骤

    @H_741_404@
  • 如果下载缓慢请配置 npm 淘宝镜像:npm config set registry http://registry.npm.taobao.org
  • 全局安装@vue/clinpm install -g @vue/cli
  • 切换到你要创建项目的目录然后使用命令创建项目:vue create xxxx
  • 选择使用vue的版本
  • 启动项目:npm run serve
  • 暂停项目:Ctrl+C
  • @H_326_419@

    Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,请执行:

    vue inspect > output.Js

    3.1.3. 分析脚手架结构

    脚手架文件结构:

    .文件目录
    ├── node_modules 
    ├── public
    │   ├── favicon.ico: 页签图标
    │   └── index.HTML: 主页面
    ├── src
    │   ├── assets: 存放静态资源
    │   │   └── logo.png
    │   │── component: 存放组件
    │   │   └── HelloWorld.vue
    │   │── App.vue: 汇总所有组件
    │   └── main.Js: 入口文件
    ├── .gitignore: git版本管制忽略的配置
    ├── babel.config.Js: babel的配置文件
    ├── package.Json: 应用包配置文件 
    ├── README.md: 应用描述文件
    └── package-lock.Json: 包版本控制文件
    

    src/components/School.vue:

    <template>
        <div ID='Demo'>
            <h2>学校名称:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>
            <@R_502_5554@ @click="showname">点我提示学校名</@R_502_5554@>
        </div>
    </template>
    
    <script>
        export default {
            name:'School',methods: {
                showname() {
                    alert(this.name)
                }
            },}
    </script>
    
    <style>
        #Demo{
            background: orange;
        }
    </style>
    

    src/components/Student.vue:

    <template>
        <div>
            <h2>学生姓名:{{name}}</h2>
            <h2>学生年龄:{{age}}</h2>
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',}
    </script>
    

    src/App.vue:

    <template>
        <div>
            <School></School>
            <Student></Student>
        </div>
    </template>
    
    <script>
        import School from './components/School.vue'
        import Student from './components/Student.vue'
    
        export default {
            name:'App',Student
            }
        }
    </script>
    

    src/main.Js:

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
        el:'#app',
        render: h => h(App),
    })
    

    public/index.HTML:

    <!DOCTYPE HTML>
    <HTML lang="">
        <head>
            <Meta charset="UTF-8">
            <!-- 针对IE浏览器的特殊配置,含义是让IE浏览器以最高渲染级别渲染页面 -->
            <Meta http-equiv="X-UA-Compatible" content="IE=edge">
            <!-- 开启移动端的理想端口 -->
            <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
            <!-- 配置页签图标 -->
            <link rel="icon" href="<%= BASE_URL %>favicon.ico">
            <!-- 配置网页标题 -->
            <Title><%= HTMLWebpackPlugin.options.Title %></Title>
        </head>
        <body>
            <!-- 容器 -->
            <div ID="app"></div>
        </body>
    </HTML>
    

    效果

    3.1.4. render函数

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
        el:'#app',
        // 简写形式
    	render: h => h(App),
        // 完整形式
    	// render(createElement){
    	//     return createElement(App)
    	// }
    })
    

    总结:

    关于不同版本的函数

    @H_741_404@
  • vue.Jsvue.runtime.xxx.Js区别

    @H_741_404@
  • vue.Js 是完整版的 Vue,包含:核心功能+模板解析
  • vue.runtime.xxx.Js 是运行版的 Vue,只包含核心功能,没有模板解析
  • @H_326_419@
  • 因为 vue.runtime.xxx.Js 没有模板解析器,所以不能使用 template 配置项,需要使用 render函数接收到的createElement 函数去指定具体内容

  • @H_326_419@

    3.1.5. 修改认配置

    module.exports = {
        pages: {
            index: {
                // 入口
                entry: 'src/index/main.Js'
            }
        },
      // 关闭语法检查
      lineOnSave:false
    }
    

    3.2. ref属性

    <template>
        <div>
            <h1 ref="Title">{{msg}}</h1>
            <School ref="sch"/>
            <@R_502_5554@ @click="show" ref="btn">点我输出ref</@R_502_5554@>
        </div>
    </template>
    
    <script>
        import School from './components/School.vue'
        export default {
            name:'App',components: { School },data() {
                return {
                    msg:'欢迎学习Vue!'
                }
            },methods:{
                show(){
                    console.log(this.$refs.Title)
                    console.log(this.$refs.sch)
                    console.log(this.$refs.btn)
                }
            }
        }
    </script>
    

    效果

    总结:

    ref属性

    @H_741_404@
  • 被用来给元素或子组件注册引用信息ID的替代者)
  • 应用在HTML标签获取的是真实DOM元素,应用在组件标签获取的是组件实例对象(vc)
  • 使用方式: @H_741_404@
  • 打标识:<h1 ref="xxx"></h1><School ref="xxx"></School>
  • 获取this.$refs.xxx
  • @H_326_419@ @H_326_419@

    3.3. props配置项

    src/App.vue:

    <template>
        <div>
            <Student name="JOJO" sex="男酮" :age="20" />
        </div>
    </template>
    
    <script>
        import Student from './components/Student.vue'
        export default {
            name:'App',components: { Student },}
    </script>
    

    src/components/Student.vue:

    <template>
        <div>
            <h1>{{msg}}</h1>
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>
            <h2>学生年龄:{{age}}</h2>     
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',data() {
                return {
                    msg:"我是一名来自枝江大学的男酮,嘿嘿,我的金轮~~",}
            },// 简单声明接收
    		// props:['name','age','sex']
    
            // 接收的同时对数据进行类型限制
    		/* props:{
    			name:String,age:Number,sex:String
    		} */
    
            // 接收的同时对数据进行类型限制 + 指定认值 + 限制必要性
    		props:{
    			name:{
    				type:String,required:true,},age:{
    				type:Number,default:99
    			},sex:{
    				type:String,required:true
    			}
    		}
        }
    </script>
    

    效果

    总结:

    props配置项:

    @H_741_404@
  • 功能:让组件接收外部传过来的数据

  • 传递数据:<Demo name="xxx"/>

  • 接收数据:

    @H_741_404@
  • 第一种方式(只接收):props:['name']

  • 第二种方式(限制数据类型):props:{name:String}

  • 第三种方式(限制类型、限制必要性、指定认值):

    props:{
        name:{
        	type:String, //类型
            required:true, //必要性
            default:'JOJO' //认值
        }
    }
    
  • @H_326_419@ @H_326_419@

    props是只读的,Vue底层会监测你对props的修改,如果进行修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后修改data中的数据

    3.4. mixin混入

    局部混入:

    src/mixin.Js:

    export const mixin = {
        methods: {
            showname() {
                alert(this.name)
            }
        },
        mounted() {
            console.log("你好呀~")
        }
    }
    

    src/components/School.vue

    <template>
        <div>
            <h2 @click="showname">学校姓名:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>   
        </div>
    </template>
    
    <script>
        //引入混入
        import {mixin} from '../mixin'
        
        export default {
            name:'School',mixins:[mixin]
        }
    </script>
    

    src/components/Student.vue:

    <template>
        <div>
            <h2 @click="showname">学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>   
        </div>
    </template>
    
    <script>
        //引入混入
        import {mixin} from '../mixin'
        
        export default {
            name:'Student',sex:'男'
                }
            },mixins:[mixin]
        }
    </script>
    

    src/App.vue:

    <template>
        <div>
            <School/>
            <hr/>
            <Student/>
        </div>
    </template>
    
    <script>
        import Student from './components/Student.vue'
        import School from './components/School.vue'
    
        export default {
            name:'App',components: { Student,School },}
    </script>
    

    效果


    全局混入:

    src/main.Js:

    import Vue from 'vue'
    import App from './App.vue'
    import {mixin} from './mixin'
    
    Vue.config.productionTip = false
    Vue.mixin(mixin)
    
    new Vue({
        el:"#app",
        render: h => h(App)
    })
    

    效果


    总结:

    mixin(混入):

    @H_741_404@
  • 功能:可以把多个组件共用的配置提取一个混入对象

  • 使用方式:

    @H_741_404@
  • 定义混入:

    const mixin = {
        data(){....},
        methods:{....}
        ....
    }
    
  • 使用混入:

    @H_741_404@
  • 全局混入:Vue.mixin(xxx)
  • 局部混入:mixins:['xxx']
  • @H_326_419@ @H_326_419@
  • 备注:

    @H_741_404@
  • 组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先

    var mixin = {
    	data: function () {
    		return {
        		message: 'hello',
                foo: 'abc'
        	}
      	}
    }
    
    new Vue({
      	mixins: [mixin],
      	data () {
        	return {
          		message: 'goodbye',
                bar: 'def'
        	}
        },
      	created () {
        	console.log(this.$data)
        	// => { message: "goodbye",foo: "abc",bar: "def" }
      	}
    })
    
  • 同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用

    var mixin = {
      	created () {
        	console.log('混入对象的钩子被调用')
      	}
    }
    
    new Vue({
      	mixins: [mixin],
      	created () {
        	console.log('组件钩子被调用')
      	}
    })
    
    // => "混入对象的钩子被调用"
    // => "组件钩子被调用"
    
  • @H_326_419@ @H_326_419@

    3.5. plugin插件

    src/plugin.Js:

    export default {
    	install(Vue,x,y,z){
    		console.log(x,z)
    		//全局过滤器
    		Vue.filter('mySlice',4)
    		})
    
    		//定义混入
    		Vue.mixin({
    			data() {
    				return {
    					x:100,
    					y:200
    				}
    			},
    		})
    
    		//给Vue原型上添加一个方法(vm和vc就都能用了)
    		Vue.prototype.hello = ()=>{alert('你好啊')}
    	}
    }
    

    src/main.Js:

    import Vue from 'vue'
    import App from './App.vue'
    import plugin from './plugin'
    
    Vue.config.productionTip = false
    Vue.use(plugin,2,3)
    
    new Vue({
        el:"#app",
        render: h => h(App)
    })
    

    src/components/School.vue:

    <template>
        <div>
            <h2>学校姓名:{{name | mySlice}}</h2>
            <h2>学校地址:{{address}}</h2>   
        </div>
    </template>
    
    <script>
        export default {
            name:'School',data() {
                return {
                    name:'尚硅谷atguigu',address:'北京'
                }
            }
        }
    </script>
    

    src/components/Student.vue:

    <template>
        <div>
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2> 
            <@R_502_5554@ @click="test">点我测试hello方法</@R_502_5554@>  
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',methods:{
                test() {
                    this.hello()
                }
            }
        }
    </script>
    

    效果

    @H_444_20301@

    总结:

    插件

    @H_741_404@
  • 功能:用于增强Vue

  • 本质:包含install方法一个对象,install的第一个参数是Vue,第二个以后的参数插件使用者传递的数据

  • 定义插件

    plugin.install = function (Vue, options) {
            // 1. 添加全局过滤器
            Vue.filter(....)
        
            // 2. 添加全局指令
            Vue.directive(....)
        
            // 3. 配置全局混入
            Vue.mixin(....)
        
            // 4. 添加实例方法
            Vue.prototype.$myMethod = function () {...}
            Vue.prototype.$myProperty = xxxx
        }
    
  • 使用插件Vue.use(plugin)

  • @H_326_419@

    3.6. scoped样式

    src/components/School.vue:

    <template>
        <div class="demo">
            <h2>学校姓名:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>   
        </div>
    </template>
    
    <script>
        export default {
            name:'School',address:'北京'
                }
            }
        }
    </script>
    
    <style scoped>
        .demo{
            background-color: blueviolet;
        }
    </style>
    

    src/components/Student.vue

    <template>
        <div class="demo">
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2> 
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',sex:'男'
                }
            }
        }
    </script>
    
    <style scoped>
        .demo{
            background-color: chartreuse;
        }
    </style>
    

    src/App.vue:

    <template>
        <div>
            <School/>
            <Student/>
        </div>
    </template>
    
    <script>
        import Student from './components/Student.vue'
        import School from './components/School.vue'
    
        export default {
            name:'App',}
    </script>
    

    效果

    总结:

    scoped样式

    @H_741_404@
  • 作用:让样式在局部生效,防止冲突
  • 写法:<style scoped>
  • @H_326_419@

    scoped样式一般不会在App.vue使用

    3.7. Todo-List案例

    src/components/Myheader.vue:

    <template>
        <div class="todo-header">
            <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keydown.enter="add" v-model="Title"/>
        </div>
    </template>
    
    <script>
        import {nanoID} from 'nanoID'
        export default {
            name:'Myheader',data() {
                return {
                    Title:''
                }
            },methods:{
                add(){
                    if(!this.Title.trim()) return
                    const todoObj = {ID:nanoID(),Title:this.Title,done:false}
                    this.addTodo(todoObj)
                    this.Title = ''
                }
            },props:['addTodo']
        }
    </script>
    
    <style scoped>
        .todo-header input {
            wIDth: 560px;
            height: 28px;
            Font-size: 14px;
            border: 1px solID #ccc;
            border-radius: 4px;
            padding: 4px 7px;
        }
    
        .todo-header input:focus {
            outline: none;
            border-color: rgba(82,168,236,0.8);
            Box-shadow: inset 0 1px 1px rgba(0,0.075),0 0 8px rgba(82,0.6);
        }
    </style>
    

    src/components/MyItem.vue:

    <template>
        <li>
            <label>
                <input type="checkBox" :checked="todo.done" @click="handleCheck(todo.ID)"/>
                <span>{{todo.Title}}</span>
            </label>
            <@R_502_5554@ class="btn btn-danger" @click="handleDelete(todo.ID,todo.Title)">删除</@R_502_5554@>
        </li>
    </template>
    
    <script>
        export default {
            name:'MyItem',props:['todo','checkTodo','deletetodo'],methods:{
                handleCheck(ID){
                    this.checkTodo(ID)
                },handleDelete(ID,Title){
                    if(confirm("确定删除任务:"+Title+"吗?")){
                        this.deletetodo(ID)
                    }
                }
            }
        }
    </script>
    
    <style scoped>
        li {
            List-style: none;
            height: 36px;
            line-height: 36px;
            padding: 0 5px;
            border-bottom: 1px solID #ddd;
        }
    
        li label {
            float: left;
            cursor: pointer;
        }
    
        li label li input {
            vertical-align: mIDdle;
            margin-right: 6px;
            position: relative;
            top: -1px;
        }
    
        li @R_502_5554@ {
            float: right;
            display: none;
            margin-top: 3px;
        }
    
        li:before {
            content: initial;
        }
    
        li:last-child {
            border-bottom: none;
        }
    
        li:hover {
            background-color: #eee;
        }
    
        li:hover @R_502_5554@{
            display: block;
        }
    </style>
    

    src/components/MyList.vue:

    <template>
        <ul class="todo-main">
            <MyItem 
                v-for="todo in todos" 
                :key="todo.ID" 
                :todo="todo" 
                :checkTodo="checkTodo"
                :deletetodo="deletetodo"
            />
        </ul>
    </template>
    
    <script>
        import MyItem from './MyItem.vue'
    
        export default {
            name:'MyList',components:{MyItem},props:['todos','deletetodo']
        }
    </script>
    
    <style scoped>
        .todo-main {
            margin-left: 0px;
            border: 1px solID #ddd;
            border-radius: 2px;
            padding: 0px;
        }
    
        .todo-empty {
            height: 40px;
            line-height: 40px;
            border: 1px solID #ddd;
            border-radius: 2px;
            padding-left: 5px;
            margin-top: 10px;
        }
    </style>
    

    src/components/MyFooter.vue:

    <template>
        <div class="todo-footer" v-show="total">
            <label>
                <input type="checkBox" v-model="isAll"/>
            </label>
            <span>
                <span>已完成{{donetotal}}</span> / 全部{{total}}
            </span>
            <@R_502_5554@ class="btn btn-danger" @click="clearall">清除已完成任务</@R_502_5554@>
        </div>
    </template>
    
    <script>
        export default {
            name:'MyFooter','checkAllTodo','clearallTodo'],computed:{
                donetotal(){
                    return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
                },total(){
                    return this.todos.length
                },isAll:{
                    get(){
                        return this.total === this.donetotal && this.total > 0
                    },set(value){
                        this.checkAllTodo(value)
                    }
                }
            },methods:{
                clearall(){
                    this.clearallTodo()
                }
            }
        }
    </script>
    
    <style scoped>
        .todo-footer {
            height: 40px;
            line-height: 40px;
            padding-left: 6px;
            margin-top: 5px;
            }
    
        .todo-footer label {
            display: inline-block;
            margin-right: 20px;
            cursor: pointer;
        }
    
        .todo-footer label input {
            position: relative;
            top: -1px;
            vertical-align: mIDdle;
            margin-right: 5px;
        }
    
        .todo-footer @R_502_5554@ {
            float: right;
            margin-top: 5px;
        }
    </style>
    

    src/App.vue:

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader :addTodo="addTodo"/>
                <MyList :todos="todos" :checkTodo="checkTodo" :deletetodo="deletetodo"/>
                <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
        export default {
            name:'App',components: { Myheader,MyList,MyFooter },data() {
                return {
                    todos:[
                        {ID:'001',Title:'抽烟',done:false},{ID:'002',Title:'喝酒',{ID:'003',Title:'烫头',]
                }
            },methods:{
                //添加一个todo
                addTodo(todoObj){
                    this.todos.unshift(todoObj)
                },//勾选or取消勾选一个todo
                checkTodo(ID){
                    this.todos.forEach((todo)=>{
                        if(todo.ID === ID) todo.done = !todo.done
                    })
                },//删除一个todo
                deletetodo(ID){
                    this.todos = this.todos.filter(todo => todo.ID !== ID)
                },//全选or取消勾选
                checkAllTodo(done){
                    this.todos.forEach(todo => todo.done = done)
                },//删除已完成的todo
                clearallTodo(){
                    this.todos = this.todos.filter(todo => !todo.done)
                }
            }
        }
    </script>
    
    <style>
        body {
        	background: #fff;
        }
    
        .btn {
            display: inline-block;
            padding: 4px 12px;
            margin-bottom: 0;
            Font-size: 14px;
            line-height: 20px;
            text-align: center;
            vertical-align: mIDdle;
            cursor: pointer;
            Box-shadow: inset 0 1px 0 rgba(255,255,0.2),0 1px 2px rgba(0,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #da4f49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn:focus {
        	outline: none;
        }
    
        .todo-container {
            wIDth: 600px;
            margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    效果

    总结:

    • 组件化编码流程:

      @H_741_404@
    • 拆分静态组件:组件要按照功能点拆分,命名不要与HTML元素冲突
    • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用: @H_741_404@
    • 一个组件在用:放在组件自身即可
    • 一些组件在用:放在他们共同的父组件上(状态提升
    • @H_326_419@
    • 实现交互:从绑定事件开始
    • @H_326_419@
    • props适用于:

      @H_741_404@
    • 父组件 ==> 子组件 通信
    • 子组件 ==> 父组件 通信(要求父组件先给子组件一个函数
    • @H_326_419@
    • 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改

    • props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做

    3.8. WebStorage

    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>localstorage</Title>
    </head>
    <body>
        <h2>localstorage</h2>
        <@R_502_5554@ onclick="saveDate()">点我保存数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="readDate()">点我读数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="deleteDate()">点我删除数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="deleteallDate()">点我清空数据</@R_502_5554@><br/>
    
        <script>
            let person = {name:"JOJO",age:20}
    
            function saveDate(){
                localstorage.setItem('msg','localstorage')
                localstorage.setItem('person',JsON.stringify(person))
            }
            function readDate(){
                console.log(localstorage.getItem('msg'))
                const person = localstorage.getItem('person')
                console.log(JsON.parse(person))
            }
            function deleteDate(){
                localstorage.removeItem('msg')
                localstorage.removeItem('person')
            }
            function deleteallDate(){
                localstorage.clear()
            }
        </script>
    </body>
    </HTML>
    
    <!DOCTYPE HTML>
    <HTML lang="en">
    <head>
        <Meta charset="UTF-8">
        <Meta http-equiv="X-UA-Compatible" content="IE=edge">
        <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
        <Title>sessionStorage</Title>
    </head>
    <body>
        <h2>sessionStorage</h2>
        <@R_502_5554@ onclick="saveDate()">点我保存数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="readDate()">点我读数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="deleteDate()">点我删除数据</@R_502_5554@><br/>
        <@R_502_5554@ onclick="deleteallDate()">点我清空数据</@R_502_5554@><br/>
    
        <script>
            let person = {name:"JOJO",age:20}
    
            function saveDate(){
                sessionStorage.setItem('msg','sessionStorage')
                sessionStorage.setItem('person',JsON.stringify(person))
            }
            function readDate(){
                console.log(sessionStorage.getItem('msg'))
                const person = sessionStorage.getItem('person')
                console.log(JsON.parse(person))
            }
            function deleteDate(){
                sessionStorage.removeItem('msg')
                sessionStorage.removeItem('person')
            }
            function deleteallDate(){
                sessionStorage.clear()
            }
        </script>
    </body>
    </HTML>
    

    总结:

    @H_741_404@
  • 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

  • 浏览器端通过Window.sessionStorageWindow.localstorage属性来实现本地存储机制

  • 相关API

    @H_741_404@
  • xxxStorage.setItem('key','value'):该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
  • xxxStorage.getItem('key'):该方法接受一个键名作为参数,返回键名对应的值
  • xxxStorage.removeItem('key'):该方法接受一个键名作为参数,并把该键名从存储中删除
  • xxxStorage.clear():该方法会清空存储中的所有数据
  • @H_326_419@
  • 备注:

    @H_741_404@
  • SessionStorage存储的内容会随着浏览器窗口关闭而消失
  • localstorage存储的内容,需要手动清除才会消失
  • xxxStorage.getItem(xxx)如果 xxx 对应的 value 获取不到,那么getItem()的返回值是null
  • JsON.parse(null)的结果依然是null
  • @H_326_419@ @H_326_419@

    使用本地存储优化Todo-List

    src/App.vue:

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader :addTodo="addTodo"/>
                <MyList :todos="todos" :checkTodo="checkTodo" :deletetodo="deletetodo"/>
                <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
        export default {
            name:'App',data() {
                return {
                    //若localstorage中存有'todos'则从localstorage中取出,否则初始为空数组
                    todos:JsON.parse(localstorage.getItem('todos')) || []
                }
            },//删除已完成的todo
                clearallTodo(){
                    this.todos = this.todos.filter(todo => !todo.done)
                }
            },watch:{
                todos:{
                    //由于todos是对象数组,所以必须开启深度监视才能发现数组中对象的变化
                    deep:true,handler(value){
                        localstorage.setItem('todos',JsON.stringify(value))
                    }
                }
            }
        }
    </script>
    
    <style>
        body {
        	background: #fff;
        }
    
        .btn {
            display: inline-block;
            padding: 4px 12px;
            margin-bottom: 0;
            Font-size: 14px;
            line-height: 20px;
            text-align: center;
            vertical-align: mIDdle;
            cursor: pointer;
            Box-shadow: inset 0 1px 0 rgba(255,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #da4f49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn:focus {
        	outline: none;
        }
    
        .todo-container {
            wIDth: 600px;
            margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    3.9. 自定义事件

    3.9.1. 绑定

    src/App.vue:

    <template>
        <div class="app">
            <!-- 通过父组件给子组件传递函数类型的props实现子给父传递数据 -->
            <School :getSchoolname="getSchoolname"/>
    
            <!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第一种写法,使用@或v-on) -->
            <!-- <Student @jojo="getStudentname"/> -->
    
            <!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第二种写法,使用ref) -->
    		<Student ref="student"/>
        </div>
    </template>
    
    <script>
        import Student from './components/Student.vue'
        import School from './components/School.vue'
    
        export default {
            name:'App',methods:{
                getSchoolname(name){
                    console.log("已收到学校的名称:"+name)
                },getStudentname(name){
                    console.log("已收到学生的姓名:"+name)      
                }
            },mounted(){
                this.$refs.student.$on('jojo',this.getStudentname)
            }
        }
    </script>
    
    
    <style scoped>
    	.app{
    		background-color: gray;
    		padding: 5px;
    	}
    </style>
    

    src/components/Student.vue:

    <template>
        <div class="student">
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>
            <@R_502_5554@ @click="sendStudentname">点我传递学生姓名</@R_502_5554@> 
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',methods:{
                sendStudentname(){
                    this.$emit('jojo',this.name)
                }
            }
        }
    </script>
    
    <style scoped>
        .student{
            background-color: chartreuse;
            padding: 5px;
    		margin-top: 30px;
        }
    </style>
    

    效果

    3.9.2. 解绑

    src/App.vue:

    <template>
        <div class="app">
            <Student @jojo="getStudentname"/>
        </div>
    </template>
    
    <script>
        import Student from './components/Student.vue'
    
        export default {
            name:'App',methods:{
                getStudentname(name){
                    console.log("已收到学生的姓名:"+name)      
                }
            }
        }
    </script>
    
    <style scoped>
    	.app{
    		background-color: gray;
    		padding: 5px;
    	}
    </style>
    

    src/components/Student.vue:

    <template>
        <div class="student">
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>
            <@R_502_5554@ @click="sendStudentname">点我传递学生姓名</@R_502_5554@> 
            <@R_502_5554@ @click="unbind">解绑自定义事件</@R_502_5554@> 
        </div>
    </template>
    
    <script>
        export default {
            name:'Student',this.name)
                },unbind(){
                    // 解绑一个自定义事件
                    // this.$off('jojo')
                    // 解绑多个自定义事件
                    // this.$off(['jojo'])
                    // 解绑所有自定义事件
                    this.$off()
                }
            }
        }
    </script>
    
    <style scoped>
        .student{
            background-color: chartreuse;
            padding: 5px;
    		margin-top: 30px;
        }
    </style>
    

    效果


    总结:

    组件的自定义事件:

    @H_741_404@
  • 一种组件间通信的方式,适用于:==子组件 > 父组件

  • 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中

  • 绑定自定义事件:

    @H_741_404@
  • 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

  • 第二种方式,在父组件中:

    <Demo ref="demo"/>
    ...
    mounted(){
        this.$refs.demo.$on('atguigu',data)
    }
    
  • 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法

  • @H_326_419@
  • 触发自定义事件:this.$emit('atguigu',数据)

  • 解绑自定义事件:this.$off('atguigu')

  • 组件上也可以绑定原生DOM事件,需要使用native修饰符

  • 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中要么用箭头函数,否则this指向会出问题

  • @H_326_419@

    使用自定义事件优化Todo-List

    src/App.vue:

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader @addTodo="addTodo"/>
                <MyList :todos="todos" :checkTodo="checkTodo" :deletetodo="deletetodo"/>
                <MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
        export default {
            name:'App',data() {
                return {
                    todos:JsON.parse(localstorage.getItem('todos')) || []
                }
            },watch:{
                todos:{
                    deep:true,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #da4f49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn:focus {
       		outline: none;
        }
    
        .todo-container {
        	wIDth: 600px;
        	margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    src/components/Myheader.vue:

    <template>
        <div class="todo-header">
            <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keydown.enter="add" v-model="Title"/>
        </div>
    </template>
    
    <script>
        import {nanoID} from 'nanoID'
        export default {
            name:'Myheader',done:false}
                    this.$emit('addTodo',todoObj)
                    this.Title = ''
                }
            }
        }
    </script>
    
    <style scoped>
        /*header*/
        .todo-header input {
            wIDth: 560px;
            height: 28px;
            Font-size: 14px;
            border: 1px solID #ccc;
            border-radius: 4px;
            padding: 4px 7px;
        }
    
        .todo-header input:focus {
            outline: none;
            border-color: rgba(82,0.6);
        }
    </style>
    

    src/components/MyFooter:

    <template>
        <div class="todo-footer" v-show="total">
            <label>
                <input type="checkBox" v-model="isAll"/>
            </label>
            <span>
                <span>已完成{{donetotal}}</span> / 全部{{total}}
            </span>
            <@R_502_5554@ class="btn btn-danger" @click="clearall">清除已完成任务</@R_502_5554@>
        </div>
    </template>
    
    <script>
        export default {
            name:'MyFooter',props:['todos'],set(value){
                        this.$emit('checkAllTodo',value)
                    }
                }
            },methods:{
                clearall(){
                    this.$emit('clearallTodo')
                }
            }
        }
    </script>
    
    <style scoped>
        .todo-footer {
            height: 40px;
            line-height: 40px;
            padding-left: 6px;
            margin-top: 5px;
            }
    
        .todo-footer label {
            display: inline-block;
            margin-right: 20px;
            cursor: pointer;
        }
    
        .todo-footer label input {
            position: relative;
            top: -1px;
            vertical-align: mIDdle;
            margin-right: 5px;
        }
    
        .todo-footer @R_502_5554@ {
            float: right;
            margin-top: 5px;
        }
    </style>
    

    3.10. 全局事件总线

    全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:1. 所有的组件对象都必须能看见他 2. 这个对象必须能够使用$on$emit$off方法去绑定、触发和解绑事件

    src/main.Js:

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
    	el:'#app',
    	render: h => h(App),
    	beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线
    	}
    })
    

    src/App.vue:

    <template>
    	<div class="app">
    		<School/>
    		<Student/>
    	</div>
    </template>
    
    <script>
    	import Student from './components/Student'
    	import School from './components/School'
    
    	export default {
    		name:'App',components:{School,Student}
    	}
    </script>
    
    <style scoped>
    	.app{
    		background-color: gray;
    		padding: 5px;
    	}
    </style>
    

    src/components/School.vue:

    <template>
    	<div class="school">
    		<h2>学校名称:{{name}}</h2>
    		<h2>学校地址:{{address}}</h2>
    	</div>
    </template>
    
    <script>
    	export default {
    		name:'School',data() {
    			return {
    				name:'尚硅谷',address:'北京',}
    		},methods:{
    			demo(data) {
    				console.log('我是School组件,收到了数据:',data)
    			}
    		},mounted() {
    			this.$bus.$on('demo',this.demo)
    		},beforeDestroy() {
    			this.$bus.$off('demo')
    		},}
    </script>
    
    <style scoped>
    	.school{
    		background-color: skyblue;
    		padding: 5px;
    	}
    </style>
    

    src/components/Student.vue:

    <template>
    	<div class="student">
    		<h2>学生姓名:{{name}}</h2>
    		<h2>学生性别:{{sex}}</h2>
    		<@R_502_5554@ @click="sendStudentname">把学生名给School组件</@R_502_5554@>
    	</div>
    </template>
    
    <script>
    	export default {
    		name:'Student',data() {
    			return {
    				name:'张三',sex:'男'
    			}
    		},methods: {
    			sendStudentname(){
    				this.$bus.$emit('demo',this.name)
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	.student{
    		background-color: pink;
    		padding: 5px;
    		margin-top: 30px;
    	}
    </style>
    

    效果

    总结:

    全局事件总线(GlobalEventBus):

    @H_741_404@
  • 一种组件间通信的方式,适用于任意组件间通信

  • 安装全局事件总线:

    new Vue({
       	...
       	beforeCreate() {
       		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
       	},
        ...
    }) 
    
  • 使用事件总线:

    @H_741_404@
  • 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

    export default {
        methods(){
            demo(data){...}
        }
        ...
        mounted() {
            this.$bus.$on('xxx',this.demo)
        }
    }
    
  • 提供数据:this.$bus.$emit('xxx',data)

  • @H_326_419@
  • 最好beforeDestroy钩子中,用$off解绑当前组件所用到的事件

  • @H_326_419@

    使用自定义事件优化Todo-List

    src/mian.Js:

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
        el:"#app",
        beforeCreate() {
            Vue.prototype.$bus = this
        }
    })
    

    src/components/App.vue

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader @addTodo="addTodo"/>
                <MyList :todos="todos"/>
                <MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
        export default {
            name:'App',JsON.stringify(value))
                    }
                }
            },mounted(){
                this.$bus.$on('checkTodo',this.checkTodo)
                this.$bus.$on('deletetodo',this.deletetodo)
            },beforeDestroy(){
                this.$bus.$off(['checkTodo','deletetodo'])
            }
        }
    </script>
    
    <style>
        body {
            background: #fff;
        }
    
        .btn {
            display: inline-block;
            padding: 4px 12px;
            margin-bottom: 0;
            Font-size: 14px;
            line-height: 20px;
            text-align: center;
            vertical-align: mIDdle;
            cursor: pointer;
            Box-shadow: inset 0 1px 0 rgba(255,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #da4f49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn:focus {
            outline: none;
        }
    
        .todo-container {
            wIDth: 600px;
            margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    src/components/MyItem.vue:

    <template>
        <li>
            <label>
                <input type="checkBox" :checked="todo.done" @click="handleCheck(todo.ID)"/>
                <span>{{todo.Title}}</span>
            </label>
            <@R_502_5554@ class="btn btn-danger" @click="handleDelete(todo.ID,props:['todo'],methods:{
                handleCheck(ID){
                    this.$bus.$emit('checkTodo',ID)
                },Title){
                    if(confirm("确定删除任务:"+Title+"吗?")){
                        this.$bus.$emit('deletetodo',ID)
                    }
                }
            }
        }
    </script>
    
    <style scoped>
        li {
            List-style: none;
            height: 36px;
            line-height: 36px;
            padding: 0 5px;
            border-bottom: 1px solID #ddd;
        }
    
        li label {
            float: left;
            cursor: pointer;
        }
    
        li label li input {
            vertical-align: mIDdle;
            margin-right: 6px;
            position: relative;
            top: -1px;
        }
    
        li @R_502_5554@ {
            float: right;
            display: none;
            margin-top: 3px;
        }
    
        li:before {
            content: initial;
        }
    
        li:last-child {
            border-bottom: none;
        }
    
        li:hover {
            background-color: #eee;
        }
    
        li:hover @R_502_5554@{
            display: block;
        }
    </style>
    

    3.11. 消息的订阅发布

    src/components/School.vue:

    <template>
    	<div class="school">
    		<h2>学校名称:{{name}}</h2>
    		<h2>学校地址:{{address}}</h2>
    	</div>
    </template>
    
    <script>
    	import pubsub from 'pubsub-Js'
    
    	export default {
    		name:'School',methods:{
    			demo(msgname,data) {
    				console.log('我是School组件,收到了数据:',mounted() {
    			this.pubID = pubsub.subscribe('demo',this.demo) //订阅消息
    		},beforeDestroy() {
    			pubsub.unsubscribe(this.pubID) //取消订阅
    		}
    	}
    </script>
    
    <style scoped>
    	.school{
    		background-color: skyblue;
    		padding: 5px;
    	}
    </style>
    

    src/components/Student.vue:

    <template>
    	<div class="student">
    		<h2>学生姓名:{{name}}</h2>
    		<h2>学生性别:{{sex}}</h2>
    		<@R_502_5554@ @click="sendStudentname">把学生名给School组件</@R_502_5554@>
    	</div>
    </template>
    
    <script>
    	import pubsub from 'pubsub-Js'
    
    	export default {
    		name:'Student',data() {
    			return {
    				name:'JOJO',sex:'男',methods: {
    			sendStudentname(){
    				pubsub.publish('demo',this.name) //发布消息
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	.student{
    		background-color: pink;
    		padding: 5px;
    		margin-top: 30px;
    	}
    </style>
    

    效果

    总结:

    消息订阅发布(pubsub):

    @H_741_404@
  • 消息订阅发布是一种组件间通信的方式,适用于任意组件间通信

  • 使用步骤:

    @H_741_404@
  • 安装pubsub:npm i pubsub-Js

  • 引入:import pubsub from 'pubsub-Js'

  • 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

    export default {
        methods(){
            demo(data){...}
        }
        ...
        mounted() {
    		this.pID = pubsub.subscribe('xxx',this.demo)
        }
    }
    
  • 提供数据:pubsub.publish('xxx',data)

  • 最好beforeDestroy钩子中,使用pubsub.unsubscribe(pID)取消订阅

  • @H_326_419@ @H_326_419@

    使用消息的订阅发布优化Todo-List

    src/App.vue:

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader @addTodo="addTodo"/>
                <MyList :todos="todos"/>
                <MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import pubsub from 'pubsub-Js'
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
    
        export default {
            name:'App',//勾选or取消勾选一个todo
                checkTodo(_,ID){
                    this.todos.forEach((todo)=>{
                        if(todo.ID === ID) todo.done = !todo.done
                    })
                },mounted(){
                this.pubID = pubsub.subscribe('checkTodo',beforeDestroy(){
                pubsub.unsubscribe(this.pubID)
                this.$bus.$off('deletetodo')
            }
        }
    </script>
    
    <style>
        body {
            background: #fff;
        }
    
        .btn {
            display: inline-block;
            padding: 4px 12px;
            margin-bottom: 0;
            Font-size: 14px;
            line-height: 20px;
            text-align: center;
            vertical-align: mIDdle;
            cursor: pointer;
            Box-shadow: inset 0 1px 0 rgba(255,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #da4f49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn:focus {
            outline: none;
        }
    
        .todo-container {
            wIDth: 600px;
            margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    src/components/myItem.vue:

    <template>
        <li>
            <label>
                <input type="checkBox" :checked="todo.done" @click="handleCheck(todo.ID)"/>
                <span>{{todo.Title}}</span>
            </label>
            <@R_502_5554@ class="btn btn-danger" @click="handleDelete(todo.ID,todo.Title)">删除</@R_502_5554@>
        </li>
    </template>
    
    <script>
        import pubsub from 'pubsub-Js'
        export default {
            name:'MyItem',methods:{
                handleCheck(ID){                    
                    pubsub.publish('checkTodo',ID)
                    }
                }
            }
        }
    </script>
    
    <style scoped>
        li {
            List-style: none;
            height: 36px;
            line-height: 36px;
            padding: 0 5px;
            border-bottom: 1px solID #ddd;
        }
    
        li label {
            float: left;
            cursor: pointer;
        }
    
        li label li input {
            vertical-align: mIDdle;
            margin-right: 6px;
            position: relative;
            top: -1px;
        }
    
        li @R_502_5554@ {
            float: right;
            display: none;
            margin-top: 3px;
        }
    
        li:before {
            content: initial;
        }
    
        li:last-child {
            border-bottom: none;
        }
    
        li:hover {
            background-color: #eee;
        }
    
        li:hover @R_502_5554@{
            display: block;
        }
    </style>
    

    3.12. $nextTick

    $nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行

    使用$nextTick优化Todo-List

    src/App.vue:

    <template>
        <div ID="root">
            <div class="todo-container">
                <div class="todo-wrap">
                <Myheader @addTodo="addTodo"/>
                <MyList :todos="todos"/>
                <MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearallTodo="clearallTodo"/>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        import pubsub from 'pubsub-Js'
        import Myheader from './components/Myheader.vue'
        import MyList from './components/MyList.vue'
        import MyFooter from './components/MyFooter.vue'
    
    
        export default {
            name:'App',//更新一个todo
    			updatetodo(ID,Title){
    				this.todos.forEach((todo)=>{
    					if(todo.ID === ID) todo.Title = Title
    				})
    			},this.deletetodo)
                this.$bus.$on('updatetodo',this.updatetodo)
            },beforeDestroy(){
                pubsub.unsubscribe(this.pubID)
                this.$bus.$off('deletetodo')
                this.$bus.$off('updatetodo')
            }
        }
    </script>
    
    <style>
        body {
            background: #fff;
        }
    
        .btn {
            display: inline-block;
            padding: 4px 12px;
            margin-bottom: 0;
            Font-size: 14px;
            line-height: 20px;
            text-align: center;
            vertical-align: mIDdle;
            cursor: pointer;
            Box-shadow: inset 0 1px 0 rgba(255,0.05);
            border-radius: 4px;
        }
    
        .btn-danger {
            color: #fff;
            background-color: #e04e49;
            border: 1px solID #bd362f;
        }
    
        .btn-danger:hover {
            color: #fff;
            background-color: #bd362f;
        }
    
        .btn-info {
            color: #fff;
            background-color: rgb(50,129,233);
            border: 1px solID rgb(1,47,212);
            margin-right: 5px;
        }
    
        .btn-info:hover {
            color: #fff;
            background-color: rgb(1,212);
        }
    
        .btn:focus {
            outline: none;
        }
    
        .todo-container {
            wIDth: 600px;
            margin: 0 auto;
        }
        .todo-container .todo-wrap {
            padding: 10px;
            border: 1px solID #ddd;
            border-radius: 5px;
        }
    </style>
    

    src/components/MyItem.vue:

    <template>
        <li>
            <label>
                <input type="checkBox" :checked="todo.done" @click="handleCheck(todo.ID)"/>
                <span v-show="!todo.isEdit">{{todo.Title}}</span>
                <input type="text" v-show="todo.isEdit" :value="todo.Title" @blur="handleBlur(todo,$event)" ref="inputTitle">
            </label>
            <@R_502_5554@ class="btn btn-danger" @click="handleDelete(todo.ID,todo.Title)">删除</@R_502_5554@>
            <@R_502_5554@ class="btn btn-info" v-show="!todo.isEdit" @click="handleEdit(todo)">编辑</@R_502_5554@>
        </li>
    </template>
    
    <script>
        import pubsub from 'pubsub-Js'
        export default {
            name:'MyItem',ID)
                    }
                },handleEdit(todo){
                    // 如果todo自身有isEdit属性就将isEdit改成true
    				if(Object.prototype.hasOwnProperty.call(todo,'isEdit')){
    					todo.isEdit = true
    				}else{
                        // 如果没有就向todo添加一个响应式的isEdit属性并设为true
    					this.$set(todo,'isEdit',true)
    				}
                    // 当Vue重新编译模板后执行$nextTick()中的回调函数
                    this.$nextTick(function(){
                        // 使input获取焦点
                        this.$refs.inputTitle.focus()
                    })
    			},// 当input框失去焦点时更新
                handleBlur(todo,event){
                    todo.isEdit = false
    				if(!event.target.value.trim()) return alert('输入不能为空!')
    				this.$bus.$emit('updatetodo',todo.ID,event.target.value)
                }
            }
        }
    </script>
    
    <style scoped>
        li {
            List-style: none;
            height: 36px;
            line-height: 36px;
            padding: 0 5px;
            border-bottom: 1px solID #ddd;
        }
    
        li label {
            float: left;
            cursor: pointer;
        }
    
        li label li input {
            vertical-align: mIDdle;
            margin-right: 6px;
            position: relative;
            top: -1px;
        }
    
        li @R_502_5554@ {
            float: right;
            display: none;
            margin-top: 3px;
        }
    
        li:before {
            content: initial;
        }
    
        li:last-child {
            border-bottom: none;
        }
    
        li:hover {
            background-color: #eee;
        }
    
        li:hover @R_502_5554@{
            display: block;
        }
    </style>
    

    Todo-List最终效果

    总结:

    $nextTick:

    @H_741_404@
  • 语法:this.$nextTick(回调函数)
  • 作用:在下一次 DOM 更新结束后执行其指定的回调
  • 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
  • @H_326_419@

    3.13. 过渡与动画

    src/App.vue:

    <template>
        <div ID="root">
            <MyAnimation/>
            <MyTransition/>
            <MyTransitionGroup/>
            <ThirdPartAnimation/>
        </div>
    </template>
    
    <script>
        import MyAnimation from './components/MyAnimation.vue'
        import MyTransition from './components/MyTransition.vue'
        import MyTransitionGroup from './components/MyTransitionGroup.vue'
        import ThirdPartAnimation from './components/ThirdPartAnimation.vue'
    
        export default {
            name:'App',components: { MyAnimation,MyTransition,MyTransitionGroup,ThirdPartAnimation },}
    </script>
    

    src/components/MyAnimation:

    <template>
        <div>
    		<@R_502_5554@ @click="isShow = !isShow">显示/隐藏</@R_502_5554@>
    		<Transition name="jojo" appear>
    			<h1 v-show="isShow">你好啊!</h1>
    		</Transition>
    	</div>
    </template>
    
    <script>
    export default {
        name:'MyTitle',data() {
    		return {
    			isShow:true
    		}
    	}
    }
    </script>
    
    <style scoped>
    	h1{
    		background-color: orange;
    	}
    
    	.jojo-enter-active{
    		animation: jojo 0.5s linear;
    	}
    
    	.jojo-leave-active{
    		animation: jojo 0.5s linear reverse;
    	}
    
    	@keyframes jojo {
    		from{
    			transform: translateX(-100%);
    		}
    		to{
    			transform: translateX(0px);
    		}
    	}
    </style>
    

    src/components/MyTransition.vue:

    <template>
        <div>
    		<@R_502_5554@ @click="isShow = !isShow">显示/隐藏</@R_502_5554@>
    		<Transition name="jojo" appear>
    			<h1 v-show="isShow">你好啊!</h1>
    		</Transition>
    	</div>
    </template>
    
    <script>
    export default {
        name:'MyTitle',data() {
    		return {
    			isShow:true
    		}
    	}
    }
    </script>
    
    <style scoped>
    	h1{
    		background-color: orange;
    	}
    
    	.jojo-enter,.jojo-leave-to{
    		transform: translateX(-100%);
    	}
    
    	.jojo-enter-to,.jojo-leave{
    		transform: translateX(0);
    	}
    
    	.jojo-enter-active,.jojo-leave-active{
    		Transition: 0.5s linear;
    	}
    </style>
    

    src/components/MyTransitionGroup.vue:

    <template>
        <div>
    		<@R_502_5554@ @click="isShow = !isShow">显示/隐藏</@R_502_5554@>
    		<Transition-group name="jojo" appear>
    			<h1 v-show="isShow" key="1">你好啊!</h1>
    			<h1 v-show="!isShow" key="2">大笨蛋</h1>
    		</Transition-group>
    	</div>
    </template>
    
    <script>
    export default {
        name:'MyTitle',data() {
    		return {
    			isShow:true
    		}
    	}
    }
    </script>
    ·
    <style scoped>
    	h1{
    		background-color: orange;
    	}
    
    	.jojo-enter,.jojo-leave-active{
    		Transition: 0.5s linear;
    	}
    </style>
    

    src/components/ThirdPartAnimation:

    <template>
        <div>
    		<@R_502_5554@ @click="isShow = !isShow">显示/隐藏</@R_502_5554@>
    		<Transition-group 
    			appear
    			name="animate__animated animate__bounce"
    			enter-active-class="animate__backInUp" 
    			leave-active-class="animate__backOutUp"
    		>
    			<h1 v-show="isShow" key="1">你好啊!</h1>
    			<h1 v-show="!isShow" key="2">大笨蛋</h1>
    		</Transition-group>
    	</div>
    </template>
    
    <script>
    	import 'animate.CSS'
    	export default {
    		name:'MyTitle',data() {
    			return {
    				isShow:true
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	h1{
    		background-color: orange;
    	}
    
    </style>
    

    效果

    总结:

    Vue封装的过度与动画

    @H_741_404@
  • 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名
  • 图示:
  • @H_326_419@

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F7tX8Utw-1648820283412)(https://cn.vueJs.org/images/Transition.png)]

    1. 写法:

      @H_741_404@
    2. 准备样式

      • 元素进入的样式

        @H_741_404@
      • v-enter:进入的起点
      • v-enter-active:进入过程
      • v-enter-to:进入的终点
      • @H_326_419@
      • 元素离开的样式

        @H_741_404@
      • v-leave:离开的起点
      • v-leave-active:离开过程
      • v-leave-to:离开的终点
      • @H_326_419@
    3. 使用<Transition>包裹要过度的元素,并配置name属性

      <Transition name="hello">
      	<h1 v-show="isShow">你好啊!</h1>
      </Transition>
      
    4. 备注:若有多个元素需要过度,则需要使用<Transition-group>,且每个元素都要指定key

    5. @H_326_419@ @H_326_419@

      使用动画优化Todo-List

      src/components/MyList.vue:

      <template>
          <ul class="todo-main">
              <Transition-group name="todo" appear>
                  <MyItem v-for="todo in todos" :key="todo.ID" :todo="todo"/>
              </Transition-group>
          </ul>
      </template>
      
      <script>
          import MyItem from './MyItem.vue'
      
          export default {
              name:'MyList',props:['todos']
          }
      </script>
      
      <style scoped>
          .todo-main {
              margin-left: 0px;
              border: 1px solID #ddd;
              border-radius: 2px;
              padding: 0px;
          }
      
          .todo-empty {
              height: 40px;
              line-height: 40px;
              border: 1px solID #ddd;
              border-radius: 2px;
              padding-left: 5px;
              margin-top: 10px;
          }
      
          .todo-enter-active{
      		animation: todo 0.5s linear;
      	}
      
      	.todo-leave-active{
      		animation: todo 0.5s linear reverse;
      	}
      
      	@keyframes todo {
      		from{
      			transform: translateX(-100%);
      		}
      		to{
      			transform: translateX(0px);
      		}
      	}
      </style>
      

      4. Vue中的AJAX

      4.1. Vue脚手架配置代理

      本案例需要下载axios库:npm install axios

      vue.config.Js:

      module.exports = {
          pages: {
              index: {
                  entry: 'src/main.Js',
          },
          lintOnSave:false,
          // 开启代理服务器(方式一)
          // devServer: {
          //     proxy:'http://localhost:5000'
          // }
      
          //开启代理服务器(方式二)
      	devServer: {
              proxy: {
                  '/jojo': {
                      target: 'http://localhost:5000',
                      pathRewrite:{'^/jojo':''},
                      // ws: true,//用于支持websocket,认值为true
                      // changeOrigin: true //用于控制请求头中的host值,认值为true
                  },
                  '/atguigu': {
                      target: 'http://localhost:5001',
                      pathRewrite:{'^/atguigu':''},认值为true
                  }
              }
          }
      }
      

      src/App.vue:

      <template>
          <div ID="root">
              <@R_502_5554@ @click="getStudents">获取学生信息</@R_502_5554@><br/>
              <@R_502_5554@ @click="getCars">获取汽车信息</@R_502_5554@>
          </div>
      </template>
      
      <script>
          import axios from 'axios'
          
          export default {
              name:'App',methods: {
      			getStudents(){
      				axios.get('http://localhost:8080/jojo/students').then(
      					response => {
      						console.log('请求成功了',response.data)
      					},error => {
      						console.log('请求失败了',error.message)
      					}
      				)
      			},getCars(){
      				axios.get('http://localhost:8080/atguigu/cars').then(
      					response => {
      						console.log('请求成功了',error.message)
      					}
      				)
      			}
              }
          }
      </script>
      

      效果

      总结:

      vue脚手架配置代理服务器

      • 方法一:在vue.config.Js添加如下配置:

        devServer:{
            proxy:"http://localhost:5000"
        }
        

        说明:

        @H_741_404@
      • 优点:配置简单,请求资源时直接发给前端即可
      • 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
      • 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器优先匹配前端资源
      • @H_326_419@
      • 方法二:

        devServer: {
            proxy: {
              	'/API1': { // 匹配所有以 '/API1'开头的请求路径
                	target: 'http://localhost:5000',// 代理目标的基础路径
                	changeOrigin: true,
                	pathRewrite: {'^/API1': ''}
              	},
              	'/API2': { // 匹配所有以 '/API2'开头的请求路径
                	target: 'http://localhost:5001',
                	pathRewrite: {'^/API2': ''}
              	}
            }
        }
        
        // changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
        // changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
        

        说明:

        @H_741_404@
      • 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
      • 缺点:配置略微繁琐,请求资源时必须加前缀
      • @H_326_419@

      4.2. GitHub用户搜索案例

      public/index.HTML:

      <!DOCTYPE HTML>
      <HTML lang="">
          <head>
              <Meta charset="UTF-8">
              <!-- 针对IE浏览器的特殊配置,含义是让IE浏览器以最高渲染级别渲染页面 -->
              <Meta http-equiv="X-UA-Compatible" content="IE=edge">
              <!-- 开启移动端的理想端口 -->
              <Meta name="vIEwport" content="wIDth=device-wIDth,initial-scale=1.0">
              <!-- 配置页签图标 -->
              <link rel="icon" href="<%= BASE_URL %>favicon.ico">
              <!-- 引入bootstrap样式 -->
              <link rel="stylesheet" href="<%= BASE_URL %>CSS/bootstrap.CSS">
              <!-- 配置网页标题 -->
              <Title><%= HTMLWebpackPlugin.options.Title %></Title>
          </head>
          <body>
              <!-- 容器 -->
              <div ID="app"></div>
          </body>
      </HTML>
      

      src/main.Js:

      import Vue from 'vue'
      import App from './App.vue'
      
      Vue.config.productionTip = false
      
      new Vue({
          el:"#app",
          beforeCreate(){
              Vue.prototype.$bus = this
          }
      })
      

      src/App.vue:

      <template>
      	<div class="container">
      		<Search/>
      		<List/>
      	</div>
      </template>
      
      <script>
      	import Search from './components/Search.vue'
      	import List from './components/List.vue'
      
          export default {
              name:'App',components:{Search,List},}
      </script>
      

      src/components/Search.vue:

      <template>
          <section class="jumbotron">
      		<h3 class="jumbotron-heading">Search Github Users</h3>
      		<div>
                  <input type="text" placeholder="enter the name you search" v-model="keyWord"/>&nbsp;
                  <@R_502_5554@ @click="getUsers">Search</@R_502_5554@>
      		</div>
          </section>
      </template>
      
      <script>
          import axios from 'axios'
          export default {
              name:'Search',data() {
                  return {
                      keyWord:''
                  }
              },methods: {
                  getUsers(){
                      //请求前更新List的数据
      				this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false})
      				axios.get(`https://API.github.com/search/users?q=${this.keyWord}`).then(
      					response => {
      						console.log('请求成功了')
      						//请求成功后更新List的数据
      						this.$bus.$emit('updateListData',{isLoading:false,users:response.data.items})
      					},error => {
      						//请求后更新List的数据
      						this.$bus.$emit('updateListData',errMsg:error.message,users:[]})
      					}
      				)
                  }
              }
          }
      </script>
      

      src/components/List.vue:

      <template>
          <div class="row">
              <!-- 展示用户列表 -->
              <div class="card" v-show="info.users.length" v-for="user in info.users" :key="user.ID">
                  <a :href="user.HTML_url" target="_blank">
                      <img :src="user.avatar_url" style='wIDth: 100px'/>
                  </a>
                  <h4 class="card-Title">{{user.login}}</h4>
              </div>
              <!-- 展示欢迎词 -->
              <h1 v-show="info.isFirst">欢迎使用!</h1>
              <!-- 展示加载中 -->
              <h1 v-show="info.isLoading">加载中...</h1>
              <!-- 展示错误信息 -->
              <h1 v-show="info.errMsg">{{errMsg}}</h1>
          </div>
      </template>
      
      <script>
          export default {
              name:'List',data() {
                  return {
                      info:{
                          isFirst:true,isLoading:false,users:[]
                      }
                  }
              },mounted(){
                  this.$bus.$on('updateListData',(dataObj)=>{
                      //动态合并两个对象的属性
                      this.info = {...this.info,...dataObj}
                  })
              },beforeDestroy(){
                  this.$bus.$off('updateListData')
              }
          }
      </script>
      
      <style scoped>
          .album {
      		min-height: 50rem; /* Can be removed; just added for demo purposes */
      		padding-top: 3rem;
      		padding-bottom: 3rem;
      		background-color: #f7f7f7;
      	}
      
      	.card {
      		float: left;
      		wIDth: 33.333%;
      		padding: .75rem;
      		margin-bottom: 2rem;
      		border: 1px solID #efefef;
      		text-align: center;
      	}
      
      	.card > img {
      		margin-bottom: .75rem;
      		border-radius: 100px;
      	}
      
      	.card-text {
      		Font-size: 85%;
      	}
      </style>
      

      效果

      4.3. vue-resource

      下载 vue-resource库:npm i vue-resource

      src/main.Js:

      import Vue from 'vue'
      import App from './App.vue'
      import vueResource from 'vue-resource'
      
      Vue.config.productionTip = false
      Vue.use(vueResource)
      
      new Vue({
          el:"#app",}
      </script>
      

      src/components/Search.vue:

      <template>
          <section class="jumbotron">
      		<h3 class="jumbotron-heading">Search Github Users</h3>
      		<div>
                  <input type="text" placeholder="enter the name you search" v-model="keyWord"/>&nbsp;
                  <@R_502_5554@ @click="getUsers">Search</@R_502_5554@>
      		</div>
          </section>
      </template>
      
      <script>
          export default {
              name:'Search',isFirst:false})
      				this.$http.get(`https://API.github.com/search/users?q=${this.keyWord}`).then(
      					response => {
      						console.log('请求成功了')
      						//请求成功后更新List的数据
      						this.$bus.$emit('updateListData',(dataObj)=>{
                      this.info = {...this.info,beforeDestroy(){
                  this.$bus.$off('updateListData')
              }
          }
      </script>
      
      <style scoped>
          .album {
      		min-height: 50rem; /* Can be removed; just added for demo purposes */
      		padding-top: 3rem;
      		padding-bottom: 3rem;
      		background-color: #f7f7f7;
      	}
      
      	.card {
      		float: left;
      		wIDth: 33.333%;
      		padding: .75rem;
      		margin-bottom: 2rem;
      		border: 1px solID #efefef;
      		text-align: center;
      	}
      
      	.card > img {
      		margin-bottom: .75rem;
      		border-radius: 100px;
      	}
      
      	.card-text {
      		Font-size: 85%;
      	}
      </style>
      

      总结:

      vue项目常用的两个AJAX库:

      @H_741_404@
    6. axios:通用的AJAX请求库,官方推荐,效率高
    7. vue-resource:vue插件库,vue 1.x使用广泛,官方已不维护
    8. @H_326_419@

      4.4. slot插槽

      4.4.1. 认插槽

      src/App.vue:

      <template>
      	<div class="container">
      		<category Title="美食" >
      			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
      		</category>
      
      		<category Title="游戏" >
      			<ul>
      				<li v-for="(g,index) in games" :key="index">{{g}}</li>
      			</ul>
      		</category>
      
      		<category Title="电影">
      			<vIDeo controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></vIDeo>
      		</category>
      	</div>
      </template>
      
      <script>
      	import category from './components/category'
      	export default {
      		name:'App',components:{category},data() {
      			return {
      				games:['植物大战僵尸','红色警戒','空洞骑士','王国']
      			}
      		},}
      </script>
      
      <style scoped>
      	.container{
      		display: flex;
      		justify-content: space-around;
      	}
      </style>
      

      src/components/category.vue:

      <template>
      	<div class="category">
      		<h3>{{Title}}分类</h3>
      		<!-- 定义一个插槽(挖个坑,等着组件的使用进行填充) -->
      		<slot>我是一些认值,当使用者没有传递具体结构时,我会出现</slot>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'category',props:['Title']
      	}
      </script>
      
      <style scoped>
      	.category{
      		background-color: skyblue;
      		wIDth: 200px;
      		height: 300px;
      	}
      	h3{
      		text-align: center;
      		background-color: orange;
      	}
      	vIDeo{
      		wIDth: 100%;
      	}
      	img{
      		wIDth: 100%;
      	}
      </style>
      

      效果

      4.4.2. 具名插槽

      src/App.vue:

      <template>
      	<div class="container">
      		<category Title="美食" >
      			<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
      			<a slot="footer" href="http://www.atguigu.com">更多美食</a>
      		</category>
      
      		<category Title="游戏" >
      			<ul slot="center">
      				<li v-for="(g,index) in games" :key="index">{{g}}</li>
      			</ul>
      			<div class="foot" slot="footer">
      				<a href="http://www.atguigu.com">单机游戏</a>
      				<a href="http://www.atguigu.com">网络游戏</a>
      			</div>
      		</category>
      
      		<category Title="电影">
      			<vIDeo slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></vIDeo>
      			<template v-slot:footer>
      				<div class="foot">
      					<a href="http://www.atguigu.com">经典</a>
      					<a href="http://www.atguigu.com">热门</a>
      					<a href="http://www.atguigu.com">推荐</a>
      				</div>
      				<h4>欢迎前来观影</h4>
      			</template>
      		</category>
      	</div>
      </template>
      
      <script>
      	import category from './components/category'
      	export default {
      		name:'App',}
      </script>
      
      <style>
      	.container,.foot{
      		display: flex;
      		justify-content: space-around;
      	}
      	h4{
      		text-align: center;
      	}
      </style>
      

      src/components/category.vue:

      <template>
      	<div class="category">
      		<h3>{{Title}}分类</h3>
      		<!-- 定义一个插槽(挖个坑,等着组件的使用进行填充) -->
      		<slot name="center">我是一些认值,当使用者没有传递具体结构时,我会出现1</slot>
              <slot name="footer">我是一些认值,当使用者没有传递具体结构时,我会出现2</slot>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'category',props:['Title']
      	}
      </script>
      
      <style scoped>
      	.category{
      		background-color: skyblue;
      		wIDth: 200px;
      		height: 300px;
      	}
      	h3{
      		text-align: center;
      		background-color: orange;
      	}
      	vIDeo{
      		wIDth: 100%;
      	}
      	img{
      		wIDth: 100%;
      	}
      </style>
      

      效果

      4.4.3. 作用域插槽

      src/App.vue:

      <template>
      	<div class="container">
      		<category Title="游戏" >
      			<template scope="jojo">
      				<ul>
      					<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
      				</ul>
      			</template>
      		</category>
      
      		<category Title="游戏" >
      			<template scope="jojo">
      				<ol>
      					<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
      				</ol>
      			</template>
      		</category>
      
      		<category Title="游戏" >
      			<template scope="jojo">
      				<h4 v-for="(g,index) in jojo.games" :key="index">{{g}}</h4>
      			</template>
      		</category>
      	</div>
      </template>
      
      <script>
      	import category from './components/category'
      	export default {
      		name:'App',components:{category}
      	}
      </script>
      
      <style>
      	.container,.foot{
      		display: flex;
      		justify-content: space-around;
      	}
      	h4{
      		text-align: center;
      	}
      </style>
      

      src/components/category.vue:

      <template>
      	<div class="category">
      		<h3>{{Title}}分类</h3>
      		<!-- 定义一个插槽(挖个坑,等着组件的使用进行填充) -->
      		<slot :games="games">我是一些认值,当使用者没有传递具体结构时,我会出现1</slot>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'category',props:['Title'],}
      </script>
      
      <style scoped>
      	.category{
      		background-color: skyblue;
      		wIDth: 200px;
      		height: 300px;
      	}
      	h3{
      		text-align: center;
      		background-color: orange;
      	}
      	vIDeo{
      		wIDth: 100%;
      	}
      	img{
      		wIDth: 100%;
      	}
      </style>
      

      效果

      总结:

      插槽:

      @H_741_404@
    9. 作用:让父组件可以向子组件指定位置插入HTML结构,也是一种组件间通信的方式,适用于==父组件 > 子组件

    10. 分类认插槽、具名插槽、作用域插槽

    11. 使用方式:

      @H_741_404@
    12. 认插槽:

      父组件中:
              <category>
                 	<div>HTML结构1</div>
              </category>
      子组件中:
              <template>
                  <div>
                     	<slot>插槽内容...</slot>
                  </div>
              </template>
      
    13. 具名插槽:

      父组件中:
              <category>
                  <template slot="center">
                   	 <div>HTML结构1</div>
                  </template>
      
                  <template v-slot:footer>
                     	<div>HTML结构2</div>
                  </template>
              </category>
      子组件中:
              <template>
                  <div>
                     	<slot name="center">插槽内容...</slot>
                      <slot name="footer">插槽内容...</slot>
                  </div>
              </template>
      
    14. 作用域插槽:

      @H_741_404@
    15. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在category组件中,但使用数据所遍历出来的结构由App组件决定)

    16. 具体编码:

      父组件中:
      		<category>
      			<template scope="scopeData">
      				<!-- 生成的是ul列表 -->
      				<ul>
      					<li v-for="g in scopeData.games" :key="g">{{g}}</li>
      				</ul>
      			</template>
      		</category>
      
      		<category>
      			<template slot-scope="scopeData">
      				<!-- 生成的是h4标题 -->
      				<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
      			</template>
      		</category>
      子组件中:
              <template>
                  <div>
                      <slot :games="games"></slot>
                  </div>
              </template>
      		
              <script>
                  export default {
                      name:'category',//数据在子组件自身
                      data() {
                          return {
                              games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                          }
                      },}
              </script>
      
    17. @H_326_419@ @H_326_419@ @H_326_419@

      5. Vuex

      5.1. 理解Vuex

      5.1.1. Vuex是什么

      @H_741_404@
    18. 概念:专门在 Vue 中实现集中式状态(数据)管理一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
    19. Vuex Github地址
    20. @H_326_419@

      5.1.2. 什么时候使用Vuex

      @H_741_404@
    21. 多个组件依赖于同一状态
    22. 来自不同组件的行为需要变更同一状态
    23. @H_326_419@

      5.1.3. Vuex工作原理图

      vuex

      5.2. 求和案例

      下载vuexnpm i vuex

      5.2.1. 使用纯vue编写

      src/App.vue:

      <template>
      	<div class="container">
      		<Count/>
      	</div>
      </template>
      
      <script>
      	import Count from './components/Count'
      	export default {
      		name:'App',components:{Count}
      	}
      </script>
      

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{sum}}</h1>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'Count',data() {
      			return {
      				n:1,//用户选择的数字
      				sum:0 //当前的和
      			}
      		},methods: {
      			increment(){
      				this.sum += this.n
      			},decrement(){
      				this.sum -= this.n
      			},incrementOdd(){
      				if(this.sum % 2){
      					this.sum += this.n
      				}
      			},incrementWait(){
      				setTimeout(()=>{
      					this.sum += this.n
      				},500)
      			},}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      
      

      效果

      5.2.2. 搭建Vuex环境

      @H_741_404@
    24. 下载 Vuex:npm i vuex

    25. 创建src/store/index.Js

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //应用Vuex插件
      Vue.use(Vuex)
         
      //准备actions对象——响应组件中用户的动作、处理业务逻辑
      const actions = {}
      //准备mutations对象——修改state中的数据
      const mutations = {}
      //准备state对象——保存具体的数据
      const state = {}
         
      //创建并暴露store
      export default new Vuex.Store({
         	actions,
         	mutations,
         	state
      })
      
    26. src/main.Js创建 vm 时传入store配置项:

    27. @H_326_419@
      import Vue from 'vue'
      import App from './App.vue'
      import Vuex from 'vuex'
      import store from './store'
      
      Vue.config.productionTip = false
      
      Vue.use(Vuex)
      
      new Vue({
          el:"#app",
          store
      })
      

      5.2.3. 使用Vuex编写

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{$store.state.sum}}</h1>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'Count',//用户选择的数字
      			}
      		},methods: {
      			increment(){
      				this.$store.commit('ADD',this.n)
      			},decrement(){
      				this.$store.commit('SUBTRACT',incrementOdd(){
      				this.$store.dispatch('addOdd',incrementWait(){
      				this.$store.dispatch('adDWait',}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      

      src/store/index.Js:

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //应用Vuex插件
      Vue.use(Vuex)
         
      //准备actions对象——响应组件中用户的动作
      const actions = {
          addOdd(context,value){
              console.log("actions中的addOdd被调用了")
              if(context.state.sum % 2){
                  context.commit('ADD',value)
              }
          },
          adDWait(context,value){
              console.log("actions中的adDWait被调用了")
              setTimeout(()=>{
      			context.commit('ADD',value)
      		},500)
          },
      }
      //准备mutations对象——修改state中的数据
      const mutations = {
          ADD(state,value){
              state.sum += value
          },
          SUBTRACT(state,value){
              state.sum -= value
          }
      }
      //准备state对象——保存具体的数据
      const state = {
          sum:0 //当前的和
      }
         
      //创建并暴露store
      export default new Vuex.Store({
          actions,
          mutations,
          state
      })
      

      总结:

      Vuex的基本使用

      @H_741_404@
    28. 初始化数据state,配置actionsmutations,操作文件store.Js

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //引用Vuex
      Vue.use(Vuex)
      
      const actions = {
          //响应组件中加的动作
      	jia(context,value){
      		// console.log('actions中的jia被调用了',miniStore,value)
      		context.commit('JIA',value)
      	},
      }
      
      const mutations = {
          //执行加
      	JIA(state,value){
      		// console.log('mutations中的JIA被调用了',state,value)
      		state.sum += value
      	}
      }
      
      //初始化数据
      const state = {
         sum:0
      }
      
      //创建并暴露store
      export default new Vuex.Store({
      	actions,
      	mutations,
      	state,
      })
      
    29. 组件中读取vuex中的数据:$store.state.sum

    30. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    31. @H_326_419@

      备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

      5.3. getters配置项

      src/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{$store.state.sum}}</h1>
      		<h3>当前求和的10倍为:{{$store.getters.bigSum}}</h3>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'Count',value){
              state.sum -= value
          }
      }
      //准备state对象——保存具体的数据
      const state = {
          sum:0 //当前的和
      }
      //准备getters对象——用于将state中的数据进行加工
      const getters = {
          bigSum(){
              return state.sum * 10
          }
      }
         
      //创建并暴露store
      export default new Vuex.Store({
          actions,
          state,
          getters
      })
      

      效果

      总结:

      getters配置项的使用

      @H_741_404@
    32. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工

    33. store.Js中追加getters配置

      ...
      const getters = {
      	bigSum(state){
      		return state.sum * 10
      	}
      }
      
      //创建并暴露store
      export default new Vuex.Store({
      	...
      	getters
      })
      
    34. 组件中读取数据:$store.getters.bigSum

    35. @H_326_419@

      5.4. 四个map方法使用

      5.4.1. mapState与mapGetters

      src/store/index.Js:

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //应用Vuex插件
      Vue.use(Vuex)
         
      //准备actions对象——响应组件中用户的动作
      const actions = {
          addOdd(context,value){
              state.sum -= value
          }
      }
      //准备state对象——保存具体的数据
      const state = {
          sum:0, //当前的和
          name:'JOJO',
          school:'尚硅谷',
      }
      //准备getters对象——用于将state中的数据进行加工
      const getters = {
          bigSum(){
              return state.sum * 10
          }
      }
         
      //创建并暴露store
      export default new Vuex.Store({
          actions,
          getters
      })
      

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{sum}}</h1>
      		<h3>当前求和的10倍为:{{bigSum}}</h3>
      		<h3>我是{{name}},我在{{school}}学习</h3>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	import {mapState,mapGetters} from 'vuex'
      
      	export default {
      		name:'Count',computed:{		
      			// 借助mapState生成计算属性(数组写法)
      			// ...mapState(['sum','school','name']),// 借助mapState生成计算属性(对象写法)
      			...mapState({sum:'sum',school:'school',name:'name'}),...mapGetters(['bigSum'])
      		}
      	}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      

      效果

      总结:

      @H_741_404@
    36. mapState方法:用于帮助我们映射state中的数据

      computed: {
          //借助mapState生成计算属性:sum、school、subject(对象写法)
           ...mapState({sum:'sum',school:'school',subject:'subject'}),
               
          //借助mapState生成计算属性:sum、school、subject(数组写法)
          ...mapState(['sum','school','subject']),
      },
      
    37. mapGetters方法:用于帮助我们映射getters中的数据

      computed: {
          //借助mapGetters生成计算属性:bigSum(对象写法)
          ...mapGetters({bigSum:'bigSum'}),
      
          //借助mapGetters生成计算属性:bigSum(数组写法)
          ...mapGetters(['bigSum'])
      },
      
    38. @H_326_419@

      5.4.2. mapActions与mapMutations

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{sum}}</h1>
      		<h3>当前求和的10倍为:{{bigSum}}</h3>
      		<h3>我是{{name}},我在{{school}}学习</h3>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment(n)">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement(n)">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd(n)">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait(n)">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
      
      	export default {
      		name:'Count',methods: {
      			// 借助mapActions生成:increment、decrement(对象形式)
      			...mapMutations({increment:'ADD',decrement:'SUBTRACT'}),// 借助mapActions生成:incrementOdd、incrementWait(对象形式)
      			...mapActions({incrementOdd:'addOdd',incrementWait:'adDWait'})
      		},...mapGetters(['bigSum'])
      		}
      	}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      

      总结:

      @H_741_404@
    39. mapActions方法:用于帮助我们生成actions对话的方法,即:包含$store.dispatch(xxx)函数

      methods:{
          //靠mapActions生成:incrementOdd、incrementWait(对象形式)
          ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
      
          //靠mapActions生成:incrementOdd、incrementWait(数组形式)
          ...mapActions(['jiaOdd','jiaWait'])
      }
      
    40. mapMutations方法:用于帮助我们生成mutations对话的方法,即:包含$store.commit(xxx)函数

      methods:{
          //靠mapActions生成:increment、decrement(对象形式)
          ...mapMutations({increment:'JIA',decrement:'JIAN'}),
          
          //靠mapMutations生成:JIA、JIAN(对象形式)
          ...mapMutations(['JIA','JIAN']),
      }
      
    41. @H_326_419@

      备注:mapActionsmapMutations使用时,若需要传递参数,则需要在模板中绑定事件时传递好参数,否则参数是事件对象

      5.5. 多组件共享数据

      src/App.vue:

      <template>
      	<div class="container">
      		<Count/>
      		<hr/>
      		<Person/>
      	</div>
      </template>
      
      <script>
      	import Count from './components/Count'
      	import Person from './components/Person'
      
      	export default {
      		name:'App',components:{Count,Person}
      	}
      </script>
      

      src/store/index.Js:

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //应用Vuex插件
      Vue.use(Vuex)
         
      //准备actions对象——响应组件中用户的动作
      const actions = {
          addOdd(context,value){
              state.sum -= value
          },
      	ADD_PERSON(state,value){
      		console.log('mutations中的ADD_PERSON被调用了')
      		state.personList.unshift(value)
      	}
      }
      //准备state对象——保存具体的数据
      const state = {
          sum:0,
          personList:[
      		{ID:'001',name:'JOJO'}
      	]
      }
      //准备getters对象——用于将state中的数据进行加工
      const getters = {
          bigSum(){
              return state.sum * 10
          }
      }
         
      //创建并暴露store
      export default new Vuex.Store({
          actions,
          getters
      })
      

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{sum}}</h1>
      		<h3>当前求和的10倍为:{{bigSum}}</h3>
      		<h3>我是{{name}},我在{{school}}学习</h3>
      		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment(n)">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement(n)">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd(n)">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait(n)">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	import {mapState,methods: {
      			...mapMutations({increment:'ADD',...mapActions({incrementOdd:'addOdd',computed:{
      			...mapState(['sum','name','personList']),...mapGetters(['bigSum'])
      		}
      	}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      

      src/components/Person.vue:

      <template>
      	<div>
      		<h1>人员列表</h1>
      		<h3 style="color:red">Count组件求和为:{{sum}}</h3>
      		<input type="text" placeholder="请输入名字" v-model="name">
      		<@R_502_5554@ @click="add">添加</@R_502_5554@>
      		<ul>
      			<li v-for="p in personList" :key="p.ID">{{p.name}}</li>
      		</ul>
      	</div>
      </template>
      
      <script>
      	import {nanoID} from 'nanoID'
      	export default {
      		name:'Person',data() {
      			return {
      				name:''
      			}
      		},computed:{
      			personList(){
      				return this.$store.state.personList
      			},sum(){
      				return this.$store.state.sum
      			}
      		},methods: {
      			add(){
      				const personObj = {ID:nanoID(),name:this.name}
      				this.$store.commit('ADD_PERSON',personObj)
      				this.name = ''
      			}
      		}
      	}
      </script>
      

      效果

      5.6. 模块化+命名空间

      src/store/index.Js:

      //引入Vue核心库
      import Vue from 'vue'
      //引入Vuex
      import Vuex from 'vuex'
      //引入count
      import countoptions from './count'
      //引入person
      import personoptions from './person'
      //应用Vuex插件
      Vue.use(Vuex)
         
      //创建并暴露store
      export default new Vuex.Store({
          modules:{
              countAbout:countoptions,
              personAbout:personoptions,
          }
      })
      

      src/store/count.Js:

      export default{
          namespaced:true,
          actions:{
              addOdd(context,value){
                  console.log("actions中的addOdd被调用了")
                  if(context.state.sum % 2){
                      context.commit('ADD',value)
                  }
              },
              adDWait(context,value){
                  console.log("actions中的adDWait被调用了")
                  setTimeout(()=>{
                      context.commit('ADD',value)
                  },500)
              }
          },
          mutations:{
              ADD(state,value){
                  state.sum += value
              },
              SUBTRACT(state,value){
                  state.sum -= value
              }
          },
          state:{
              sum:0, //当前的和
              name:'JOJO',
              school:'尚硅谷',
          getters:{
              bigSum(state){
                  return state.sum * 10
              }
          }
      }
      

      src/store/person.Js:

      import axios from "axios"
      import { nanoID } from "nanoID"
      
      export default{
          namespaced:true,
          actions:{
              addPersonWang(context,value){
                  if(value.name.indexOf('王') === 0){
                      context.commit('ADD_PERSON',value)
                  }else{
                      alert('添加的人必须姓王!')
                  }
              },
              addPersonServer(context){
                  axios.get('http://API.uixsj.cn/hitokoto/get?type=social').then(
                      response => {
                          context.commit('ADD_PERSON',{ID:nanoID(),name:response.data})
                      },
                      error => {
                          alert(error.message)
                      }
                  )
              }
          },
          mutations:{
              ADD_PERSON(state,value){
                  console.log('mutations中的ADD_PERSON被调用了')
                  state.personList.unshift(value)
              }
          },
          state:{
              personList:[
                  {ID:'001',name:'JOJO'}
              ]
          },
          getters:{
              firstPersonname(state){
                  return state.personList[0].name
              }
          }
      }
      

      src/components/Count.vue:

      <template>
      	<div>
      		<h1>当前求和为:{{sum}}</h1>
      		<h3>当前求和的10倍为:{{bigSum}}</h3>
      		<h3>我是{{name}},我在{{school}}学习</h3>
      		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
      		<select v-model.number="n">
      			<option value="1">1</option>
      			<option value="2">2</option>
      			<option value="3">3</option>
      		</select>
      		<@R_502_5554@ @click="increment(n)">+</@R_502_5554@>
      		<@R_502_5554@ @click="decrement(n)">-</@R_502_5554@>
      		<@R_502_5554@ @click="incrementOdd(n)">当前求和为奇数再加</@R_502_5554@>
      		<@R_502_5554@ @click="incrementWait(n)">等一等再加</@R_502_5554@>
      	</div>
      </template>
      
      <script>
      	import {mapState,methods: {
      			...mapMutations('countAbout',{increment:'ADD',...mapActions('countAbout',{incrementOdd:'addOdd',computed:{
      			...mapState('countAbout',['sum',...mapGetters('countAbout',['bigSum']),...mapState('personAbout',['personList'])
      		}
      	}
      </script>
      
      <style>
      	@R_502_5554@{
      		margin-left: 5px;
      	}
      </style>
      

      src/components/Person.vue:

      <template>
      	<div>
      		<h1>人员列表</h1>
      		<h3 style="color:red">Count组件求和为:{{sum}}</h3>
              <h3>列表中第一个人的名字是:{{firstPersonname}}</h3>
      		<input type="text" placeholder="请输入名字" v-model="name">
      		<@R_502_5554@ @click="add">添加</@R_502_5554@>
              <@R_502_5554@ @click="adDWang">添加一个姓王的人</@R_502_5554@>
              <@R_502_5554@ @click="addPerson">随机添加一个人</@R_502_5554@>
      		<ul>
      			<li v-for="p in personList" :key="p.ID">{{p.name}}</li>
      		</ul>
      	</div>
      </template>
      
      <script>
      	import {nanoID} from 'nanoID'
      	export default {
      		name:'Person',computed:{
      			personList(){
      				return this.$store.state.personAbout.personList
      			},sum(){
      				return this.$store.state.countAbout.sum
      			},firstPersonname(){
                      return this.$store.getters['personAbout/firstPersonname']
                  }
      		},name:this.name}
      				this.$store.commit('personAbout/ADD_PERSON',personObj)
      				this.name = ''
      			},adDWang(){
                      const personObj = {ID:nanoID(),name:this.name}
      				this.$store.dispatch('personAbout/addPersonWang',personObj)
      				this.name = ''   
                  },addPerson(){
                      this.$store.dispatch('personAbout/addPersonServer')
                  }
      		},}
      </script>
      

      效果

      总结:

      模块化+命名空间:

      @H_741_404@
    42. 目的:代码更好维护,让多种数据分类更加明确

    43. 修改store.Js

      const countAbout = {
      	namespaced:true,//开启命名空间
      	state:{x:1},
          mutations: { ... },
          actions: { ... },
        	getters: {
          	bigSum(state){
             		return state.sum * 10
          	}
        	}
      }
      
      const personAbout = {
        	namespaced:true,//开启命名空间
        	state:{ ... },
        	mutations: { ... },
        	actions: { ... }
      }
      
      const store = new Vuex.Store({
        	modules: {
          	countAbout,
          	personAbout
        	}
      })
      
    44. 开启命名空间后,组件中读取state数据:

      //方式一:自己直接读取
      this.$store.state.personAbout.List
      //方式二:借助mapState读取:
      ...mapState('countAbout',['sum',
      
    45. 开启命名空间后,组件中读取getters数据:

      //方式一:自己直接读取
      this.$store.getters['personAbout/firstPersonname']
      //方式二:借助mapGetters读取:
      ...mapGetters('countAbout',['bigSum'])
      
    46. 开启命名空间后,组件中调用dispatch

      //方式一:自己直接dispatch
      this.$store.dispatch('personAbout/addPersonWang',person)
      //方式二:借助mapActions:
      ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
      
    47. 开启命名空间后,组件中调用commit

      //方式一:自己直接commit
      this.$store.commit('personAbout/ADD_PERSON',person)
      //方式二:借助mapMutations:
      ...mapMutations('countAbout',{increment:'JIA',
      
    48. @H_326_419@

      6. Vue Router路由管理

      6.1 相关理解

      6.1.1 vue-router的理解

      6.1.2 对SPA应用的理解

      @H_741_404@
    49. 单页 Web 应用(single page web application,SPA)
    50. 整个应用只有一个完整的页面
    51. 点击页面中的导航链接不会刷新页面,只会做页面局部更新
    52. 数据需要通过AJAX请求获取
    53. @H_326_419@

      6.1.3 路由的理解

      @H_741_404@
    54. 什么是路由?

      @H_741_404@
    55. 一个路由就是一组映射关系(key - value)
    56. key 为路径,value 可能是 function 或 componen
    57. @H_326_419@
    58. 路由分类

      @H_741_404@
    59. 后端路由:

      @H_741_404@
    60. 理解:value 是 function,用于处理客户端提交的请求
    61. 工作过程服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
    62. @H_326_419@
    63. 前端路由:

      @H_741_404@
    64. 理解:value 是 component,用于展示页面内容
    65. 工作过程浏览器路径改变时,对应的组件就会显示
    66. @H_326_419@ @H_326_419@ @H_326_419@

      6.2 基本路由

      下载vue-routernpm i vue-router

      src/router/index.Js:

      //该文件专门用于创建整个应用的路由器
      import VueRouter from "vue-router";
      //引入组件
      import Home from '../components/Home'
      import About from '../components/About'
      
      //创建并暴露一个路由器
      export default new VueRouter({
          routes:[
              {
                  path:'/about',
                  component:About
              },
              {
                  path:'/home',
                  component:Home
              }
          ]
      })
      

      src/main.Js:

      import Vue from 'vue'
      import App from './App.vue'
      import VueRouter from 'vue-router'
      import router from './router'
      
      Vue.config.productionTip = false
      Vue.use(VueRouter)
      
      new Vue({
          el:"#app",
          router
      })
      

      src/App.vue:

      <template>
      	<div>
      		<div class="row">
      			<div class="col-xs-offset-2 col-xs-8">
      				<div class="page-header"><h2>Vue Router Demo</h2></div>
      			</div>
      		</div>
      		<div class="row">
      			<div class="col-xs-2 col-xs-offset-2">
      				<div class="List-group">
      					<!-- 原始HTML我们使用a标签实现页面跳转 -->
      					<!-- <a class="List-group-item active" href="./about.HTML">About</a>
      					<a class="List-group-item" href="./home.HTML">Home</a> -->
      					
      					<!-- Vue中借助router-link标签实现路由的切换 -->
      					<router-link class="List-group-item" active-class="active" to="/about"> 							About
          				</router-link>
      					<router-link class="List-group-item" active-class="active" to="/home">
                              Home
          				</router-link>
      				</div>
      			</div>
      			<div class="col-xs-6">
      				<div class="panel">
      					<div class="panel-body">
      						<!-- 指定组件的呈现位置 -->
      						<router-vIEw></router-vIEw>
      					</div>
      				</div>
      			</div>
      		</div>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:'App',}
      </script>
      

      src/components/Home.vue:

      <template>
        <h2>我是Home组件的内容</h2>
      </template>
      
      <script>
          export default {
              name:'Home'
          }
      </script>
      

      src/components/About.vue:

      <template>
        <h2>我是About组件的内容</h2>
      </template>
      
      <script>
          export default {
              name:'About'
          }
      </script>
      

      总结:

      @H_741_404@
    67. 安装vue-router,命令:npm i vue-router

    68. 应用插件Vue.use(VueRouter)

    69. 编写router配置项:

      //引入VueRouter
      import VueRouter from 'vue-router'
      //引入Luyou 组件
      import About from '../components/About'
      import Home from '../components/Home'
      
      //创建router实例对象,去管理一组一组的路由规则
      const router = new VueRouter({
      	routes:[
      		{
      			path:'/about',
      			component:About
      		},
      		{
      			path:'/home',
      			component:Home
      		}
      	]
      })
      
      //暴露router
      export default router
      
    70. 实现切换active-class可配置高亮样式):

      <router-link active-class="active" to="/about">About</router-link>
      
    71. 指定展示位:<router-vIEw></router-vIEw>

    72. @H_326_419@

      6.3. 几个注意事项

      @H_741_404@
    73. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件

      比如上一节的案例就可以修改为:

      src/pages/Home.vue:

      <template>
        <h2>我是Home组件的内容</h2>
      </template>
      
      <script>
          export default {
              name:'Home'
          }
      </script>
      

      src/pages/About.vue:

      <template>
        <h2>我是About组件的内容</h2>
      </template>
      
      <script>
          export default {
              name:'About'
          }
      </script>
      

      src/router/index.Js:

      //该文件专门用于创建整个应用的路由器
      import VueRouter from "vue-router";
      //引入组件
      import Home from '../pages/Home'
      import About from '../pages/About'
      
      //创建并暴露一个路由器
      export default new VueRouter({
          routes:[
              {
                  path:'/about',
                  component:Home
              }
          ]
      })
      

      src/components/Banner.vue:

      <template>
          <div class="col-xs-offset-2 col-xs-8">
              <div class="page-header"><h2>Vue Router Demo</h2></div>
          </div>
      </template>
      
      <script>
          export default {
              name:'Banner'
          }
      </script>
      

      src/App.vue:

      <template>
          	<div>
          		<div class="row">
          			<Banner/>
          		</div>
          		<div class="row">
          			<div class="col-xs-2 col-xs-offset-2">
          				<div class="List-group">
          					<!-- 原始HTML我们使用a标签实现页面跳转 -->
          					<!-- <a class="List-group-item active" href="./about.HTML">About</a>
          					<a class="List-group-item" href="./home.HTML">Home</a> -->
      					<!-- Vue中借助router-link标签实现路由的切换 -->
      					<router-link class="List-group-item" active-class="active" to="/about">
                              About
          				</router-link>
      					<router-link class="List-group-item" active-class="active" to="/home">
                              Home
          				</router-link>
      				</div>
      			</div>
      			<div class="col-xs-6">
      				<div class="panel">
      					<div class="panel-body">
      						<!-- 指定组件的呈现位置 -->
      						<router-vIEw></router-vIEw>
      					</div>
      				</div>
      			</div>
      		</div>
      	</div>
      </template>
      
      <script>
      	import Banner from './components/Banner.vue'
      	export default {
      		name:'App',components:{Banner}
      	}
      </script>
      
    74. 通过切换,“隐藏”了的路由组件,认是被销毁掉的,需要的时候再去挂载

    75. 每个组件都有自己的$route属性,里面存储着自己的路由信息

    76. 整个应用只有一个router,可以通过组件的$router属性获取

    77. @H_326_419@

      6.4. 多级路由

      src/pages/Home.vue:

      <template>
          <div>
              <h2>Home组件内容</h2>
      		<div>
      			<ul class="nav nav-tabs">
      				<li>
      					<router-link class="List-group-item" active-class="active" to="/home/news"> 
                              News
          				</router-link>
      				</li>
      				<li>
      					<router-link class="List-group-item" active-class="active" to="/home/message">
                              Message
          				</router-link>
      				</li>
      			</ul>
      			<router-vIEw></router-vIEw>
      		</div>
          </div>
      </template>
      
      <script>
          export default {
              name:'Home'
          }
      </script>
      

      src/pages/News.vue:

      <template>
          <ul>
              <li>news001</li>
              <li>news002</li>
              <li>news003</li>
          </ul>
      </template>
      
      <script>
          export default {
              name:'News'
          }
      </script>
      

      src/pages/Message.vue:

      <template>
          <ul>
              <li>
                  <a href="/message1">message001</a>&nbsp;&nbsp;
              </li>
              <li>
                  <a href="/message2">message002</a>&nbsp;&nbsp;
              </li>
              <li>
                  <a href="/message/3">message003</a>&nbsp;&nbsp;
              </li>
          </ul>
      </template>
      
      <script>
          export default {
              name:'News'
          }
      </script>
      

      src/router/index.Js:

      //该文件专门用于创建整个应用的路由器
      import VueRouter from "vue-router";
      //引入组件
      import Home from '../pages/Home'
      import About from '../pages/About'
      import News from '../pages/News'
      import Message from '../pages/Message'
      
      //创建并暴露一个路由器
      export default new VueRouter({
          routes:[
              {
                  path:'/about',
                  component:Home,
                  children:[
                      {
                          path:'news',
                          component:News
                      },
                      {
                          path:'message',
                          component:Message
                      }
                  ]
              }
          ]
      })
      

      效果

      总结:

      @H_741_404@
    78. 配置路由规则,使用children配置项:

      routes:[
      	{
      		path:'/about',
      		component:About,
      	},
      	{
      		path:'/home',
      		component:Home,
      		children:[ //通过children配置子级路由
      			{
      				path:'news', //此处一定不要写:/news
      				component:News
      			},
      			{
      				path:'message', //此处一定不要写:/message
      				component:Message
      			}
      		]
      	}
      ]
      
    79. 跳转(要写完整路径):<router-link to="/home/news">News</router-link>

    80. @H_326_419@

      6.5. 路由的query参数

      src/router.index.Js:

      //该文件专门用于创建整个应用的路由器
      import VueRouter from "vue-router";
      //引入组件
      import Home from '../pages/Home'
      import About from '../pages/About'
      import News from '../pages/News'
      import Message from '../pages/Message'
      import Detail from '../pages/Detail'
      
      
      //创建并暴露一个路由器
      export default new VueRouter({
          routes:[
              {
                  path:'/about',
                          component:Message,
                          children:[
                              {
                                  path:'detail',
                                  component:Detail
                              }
                          ]
                      }
                  ]
              }
          ]
      })
      

      src/pages/Detail.vue:

      <template>
          <ul>
              <li>消息编号:{{$route.query.ID}}</li>
              <li>消息标题:{{$route.query.Title}}</li>
          </ul>
      </template>
      
      <script>
          export default {
              name:'Detail'
          }
      </script>
      

      src/pages/Message.vue:

      <template>
          <div>
              <ul>
                  <li v-for="m in messageList" :key="m.ID">
                      <!-- 跳转路由并携带query参数,to的字符串写法 -->
                      <!-- <router-link :to="`/home/message/detail?ID=${m.ID}&Title=${m.Title}`">
                          {{m.Title}}
                      </router-link>&nbsp;&nbsp; -->
      
                      <!-- 跳转路由并携带query参数,to的对象写法 -->
                      <router-link :to="{
                          path:'/home/message/detail',query:{
                              ID:m.ID,Title:m.Title
                          }
                      }">
                          {{m.Title}}
                      </router-link>&nbsp;&nbsp;
                  </li>
              </ul>
              <hr/>
              <router-vIEw></router-vIEw>
          </div>
      </template>
      
      <script>
          export default {
              name:'News',data(){
                  return{
                      messageList:[
                          {ID:'001',Title:'消息001'},Title:'消息002'},Title:'消息003'}
                      ]
                  }
              }
          }
      </script>
      

      效果

      总结:

      @H_741_404@
    81. 传递参数

      <!-- 跳转并携带query参数,to的字符串写法 -->
      <router-link :to="/home/message/detail?ID=666&Title=你好">跳转</router-link>
      				
      <!-- 跳转并携带