尚硅谷Vue笔记

1、Vue基础 
2、vue-cli
3、vue-router
4、Vuex
5、element-ui
6、Vue3

1、Vue基础

1.1、Vue介绍

Vue是一套用于构建用户界面的渐进式JavaScript框架

Vue可以自底向上逐层的应用:从引入一个轻量小巧的核心库到可以引入各式各样的Vue插件

1.1.1、Vue特点

 1.1.2、学习Vue前的基础

ES6语法规范、ES6模块化、包管理器、原型、原型链、数组常用方法、axios、promise...

1.1.3、Vue官网

Vue官网

1.1.4、搭建Vue开发环境

找到教程--安装--直接用<script>引入;我们下载开发版本,因为有提示

在控制台会看到这样的提示,解决方法:

(1)在官网下载Vue devtools,获取Chrome扩展程序,然后打开浏览器的扩展程序,将文件拉进来,若第一条还是没有消失,点击安装的这个插件,里面有详情,找到允许访问文件地址,开启就行

(2)在<script>标签里面写Vue.config.productionTip = false,这个就是阻止生产提示警告(如果还是出现提示,直接改写Vue.js里面的代码,把那个提示信息删除就行了)

1.2初始Vue

1、要有一个容器,然后创建一个Vue实例,并且传入一个配置对象;

2、容器中依然是符合html规范,只不过混入一些Vue语法,例如:{{name}};

3、容器里面的代码被称为【Vue模板】;

4、一个Vue实例不会接管多个容器,也就是说一一对应;

5、{{xxx}}中的xxx要写js表达式,并且xxx可以自动读取data里的所有属性;

6、一旦data数据改变,页面也会自动更新;

<body>
    <!-- 容器 -->
    <h1 class="root">
        hello,{{name}}
    </h1>
    <script>
        // Vue实例
        new Vue({
            el:'.root',//el用来指定当前Vue实例为哪个容器服务
            data:{//data用来存储数据
                name:'阿胜'
            }
        });

    </script>
</body>

1.3Vue模板语法

Vue模板语法有两大类:

1、插值语法:

        功能:用来解析标签体内容。

        写法:{{xxx}},xxx是js表达式,可以直接读取data里面的属性。

2、指令语法:

        功能:用来解析标签(包括:标签属性、标签体内容、绑定事件......)

        写法:v-bind:href='xxx' 或者简写成 :href = 'xxx',里面的xxx同样也是js表达式,也可以读取data里面的属性。

        备注:Vue里面指令很多,形式都是v-???,并不是所有简写都和v-bind一样。

<body>
    <!-- 容器 -->
    <h1 class="root">
        hello,{{name}}
        <br>
        <a v-bind:href="url">baidu</a>,{{address.name}}
    </h1>
    <script>
        // Vue实例
        new Vue({
            el:'.root',//el用来指定当前Vue实例为哪个容器服务
            data:{//data用来存储数据
                name:'阿胜', 
                url:'https://www.baidu.com',
                address:{
                    name:'China'
                }
            }
        });

    </script>
</body>

1.4数据绑定

Vue里面有两种数据绑定:

1、单向绑定(v-bind):数据只能从data流向页面。

2、双向绑定(v-model):数据不仅可以从data流向页面,还可以从页面流向data。

        备注:

                1、双向绑定一般用于表单类元素(如:input、select...)

                2、v-model:value 可以简写为 v-model(因为v-model默认收集就是value值)

1.5data和el两种写法

data和el两种写法:

1、el的两种写法:

        (1)new Vue时候配置el属性

        (2)先创建Vue实例,然后通过vm.$mount('.root')指向el的值。

2、data的两种写法:

        (1)对象式(就是之前写的那样)

        (2)函数式(注意:data用函数式,return后面必须是对象)

        如何选择:现在两种写法都可以,但是用到组件时,必须用函数式,否则报错。

data: function () {//data用来存储数据
                return {
                    name: '阿胜', 
                    url: 'https://www.baidu.com',
                    address: {
                        name: 'China'
                    }
                }



            }

3、原则:

        由Vue管理的函数,一定不能写箭头函数,箭头函数this指向window,如果写了,this就不再是Vue实例了。

1.6 MVVM模型

1、M:模型(model):data中的数据;

2、V:视图(view):模板代码;

3、VM:视图模型(viewmodel):Vue实例

观察发现:

        1、data中的属性,最后都会出现在VM身上

        2、VM身上的属性以及Vue实例中的属性,在Vue模板(代码)中都可以直接使用

1.7Object.defineProperty(定义属性)

object.defineproperty(对象名,对象属性,值)

Object.defineProperty(v,'age',{
            // value:18;
            // enumerable:true,//控制属性是否可以枚举,也就是遍历,默认false
            // writable:true,//控制属性是否可以修改,默认false
            // configurable:true,//控制属性是否可以被删除,默认false
            get(){//当读取时,get函数就被调佣
                console.log('有人读取age属性');
                return number;
            },
            set(value){//当修改时,set函数就被调用
                console.log('修改属性');
                number = value
            }
        })

object.keys(对象名):可以将对象的属性转换成数组 

1.8数据代理

<script>//通过修改obj1的x值,间接修改obj中的x
        var obj = {x:12}
        var obj1 = {y:21}
        Object.defineProperty(obj1,'x',{
            get(){
                return obj.x;
            },
            set(value){
                obj.x = value
            }
        })
    </script>

Vue中的数据代理

 1、Vue中数据代理:

        通过VM对象来代理data对象中属性的操作(读/写)

2、Vue中数据代理的好处:

        更加方便操作data中数据

3、基本原理:

        通过object.defineproperty()把data对象中所有属性添加到VM中,并且给每一个属性添加getter和setter方法,在getter/setter内部去操作(读/写)data中对应的属性。

1.9事件处理

<body>

    <div id="root">
        <h1>{{name}}</h1>
        <button @click='showInfo($event,66)'>点我提示信息1</button>
        <button @click="showInfo">点我提示信息2</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data:{
                name:"he"
            },
            methods:{
                showInfo(e,number,a,b,c){
                    console.log(e,number,a,b,c);
                }
            }
        })
    </script>
</body>

事件的基本使用:

        1、使用v-on:xxx 或@xxx绑定事件。其中xxx是事件名;

        2、事件的回调写在methods对象中,最终会在vm中;

        3、methods中配置的函数,不要用箭头函数!!!否者this就不是vm了;

        4、methods中的函数,都是Vue所管理的函数,this指向vm 或组件实例对象;

        5、@click=“demo” 和@click=“demo($event)”效果一致,但是后者传参。

        6、@xxx="yyy",yyy可以写简单的语句

1.10事件修饰符

Vue中的事件修饰符:

        1、prevent:阻止默认事件(常用);

        2、stop:阻止事件冒泡(常用);

        3、once:事件只触发一次(常用);

        4、capture:使用事件的捕获模式;

        5、self:只有event.target是当前操作的元素时才触发事件;

        6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕;(一般用来优化,移动端用的较多)

        7、修饰符可以连着写!!!例如:@click.prevent.stop(阻止默认行为而且阻止冒泡)

    <style>
        * {
            margin-top: 10px;
        }
        .demo {
            width: 200px;
            height: 100px;
            background-color: skyblue;
        }
        ul {
            height: 200px;
            width: 200px;
            background-color: #ccc;
            overflow: auto;
        }
        li {
            height: 100px;

        }
    </style>
</head>
<body>
    <div class="root">
        <h2>hello,{{name}}</h2>
        <!-- 在事件后面加上prevent后,后面的跳转就不会发生,阻止了默认事件-->
        <a href="http://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>

        <!-- 在事件上添加stop会阻止事件冒泡 -->
        <div class="demo " @click="showInfo">
            <button @click.stop="showInfo">点我提示信息</button>
        </div>

        <!-- 在事件上添加once,事件只会触发一次 -->
        <button @click.once="showInfo">点我提示信息</button>

        <!-- 默认事件冒泡,在事件后面添加capture后,就是处理捕获 -->
        <div class="demo " @click.capture="showMsg(2)">
            <button @click="showMsg(1)">点我提示信息1</button>
        </div>
        <!-- self:只有event.target是当前操作的元素时才触发事件; -->
        <div class="demo " @click.self="showMsg(2)">
            <button @click="showMsg(1)">点我提示信息1</button>
        </div>

        <!-- passive:事件的默认行为立即执行,无需等待事件回调执行完毕;(一般用来优化,移动端用的较多) -->
        <ul @wheel.passive="showInfo2">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>

    </div>
    <script>
        new Vue({
            el:'.root',
            data:{name:'阿胜'},
            methods:{
                showInfo(){
                    alert('同学你好帅!!!')
                },
                showMsg(e){
                    console.log(e);
                    alert('hello')
                },
                showInfo2(){
                    for(var i =0;i<100000;i++){
                        console.log('#');
                    }
                }
            }
        })
    </script>
</body>

1.11键盘事件

1、Vue中常用的键盘别名:

        回车 => enter

        删除 => delete(捕获“删除”和‘退格’键)

        退出 => esc

        空格 => space

        换行 => tab(特殊,必须配合keydown去使用)

        上 => up

       下=> down

        左 => left

        右 => right

2、Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意转为kebab-case(短横线命名)

3、系统修饰键(用法特殊):Ctrl、Alt、shift、meta(win键)

        (1)配合keyup使用:按下修饰键的同时,还得按下其他键,随后释放其他键,事件才触发。

        (2)配合keydown使用:正常触发

        (3)如果想ctrl + y 触发,可以写@keyup.ctrl.y

4、可以使用keycode去指定具体按键(不推荐,以后弃用)

5、Vue.config.keyCodes.自定义键名 = 建码。可以定制按键别名

<body>
    <div class="root">
        <h2>hello,{{name}}</h2>
        <!-- 按下enter后才会触发事件 -->
        <input type="text" placeholder="输入完后按enter" @keydown.up="showInfo">

        <!-- 没有别名的大写键 -->
        <input type="text" placeholder="输入完后按capslock" @keydown.caps-lock="showInfo">

        
        <input type="text" placeholder="输入完后按ctrl" @keyup.ctrl="showInfo">

    </div>
    <script> 
        new Vue({
            el:'.root',
            data:{
                name:'阿胜'
            },
            methods:{
                showInfo(e){
                    console.log(e.target.value);
                }
            }
        })
    </script>
</body>

1.12计算属性

计算属性:

        1、定义:要用的属性不存在,通过已有的属性计算得来。

        2、原理:底层借助了object.defineproperty方法提供的getter和setter。

        3、get函数什么时候执行?

                (1)初次读取时执行一次

                (2)当依赖的数据发生变化时会被再次调用

        4、优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

        5、备注:

                1、计算属性最终出现在vm上,直接读取使用即可

                2、如果计算属性要被修改,那必须的写set函数去响应修改,且set中要引起计算时依赖的数据发生变化。

<body>
    <div class="root">
        姓: <input type="text" v-model=firstname><br><br>
        名: <input type="text" v-model=lastname><br><br>
        全名:{{firstname + '-'+ lastname}}<br><br>
        <!-- 全名:{{fullname()}}<br><br> -->
        全名:{{fullname}}

    </div>
    <script>
        var vm = new Vue({
            el: '.root',
            data:{
                firstname:'阿',
                lastname:'胜'
            },
            // methods:{
            //     fullname(){
            //         return this.firstname +'-'+this.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>

       当不需要进行修改,只读取时,可以省略set,如下

computed:{
                // 详细
                // fullname:{
                //     get(){
                //         return this.firstname + this.lastname
                //     },
                //     set(value){
                //         const arr = value.split('-')
                //         this.firstname = arr[0];
                //         this.lastname = arr[1];
                //     }
                // }
                // 简写
                fullname(){
                    return this.firstname + this.lastname

                }

            }

1.13监视属性

监视属性watch:

        1、当被监视的属性变化时,回调函数自动调用,进行相关操作。

        2、监视的属性必须存在,才能进行监视!!

        3、监视的两种写法:

                (1)new Vue时传入watch配置

                (2)通过vm.$watch监视

<body>
    <div class="root">
        <h2>今天天气很{{info}}</h2><br><br>
        <button @click="isHot = !isHot">点我切换</button>
    </div>
    <script>
        var vm =new Vue({
            el:'.root',
            data:{
                isHot:true
            },
            
            computed:{
                info(){
                    return this.isHot?'炎热':'凉爽'
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            watch: {
                isHot:{
                    immediate:true,//初始化时让handle调用一下
                    handler(newValue,oldValue){//当ishot发生改变时,handler调用
                        console.log('isHot被修改',newValue,oldValue);
                    }
                }
            },
                 
                    
                
            
        })
        vm.$watch('isHot',{
                immediate: true,//初始化时让handle调用一下
                    handler(newValue, oldValue) {
                        console.log('isHot被修改', newValue, oldValue);
                    }
            })
    </script>
</body>

本节讲了watch属性里面的这两个属性和方法,immediate和handler

1.14深度监视

深度监视:

        (1)Vue中的watch默认不监视对象内部值得改变(一层)。

        (2)配置deep:true可以监视对象内部值得改变(多层)。

备注:

        (1)Vue自身可以监视对象内部值的改变,但是Vue提供的watch默认不可以!

        (2)使用watch时根据数据的具体结构,决定是否深度监视。

简写:

watch: {
                // 正常写法
                // isHot: {
                //     immediate: true,//初始化时让handle调用一下
                //     handler(newValue, oldValue) {
                //         console.log('isHot被修改', newValue, oldValue);
                //     }
                // }
                // 简写
                isHot(newValue, oldValue){
                    console.log('isHot被修改', newValue, oldValue);
                }
            },


            

        })
        // 正常写法
        // vm.$watch('isHot',{
        //         immediate: true,//初始化时让handle调用一下
        //             handler(newValue, oldValue) {
        //                 console.log('isHot被修改', newValue, oldValue);
        //             }
        //     })

        // 简写
        vm.$watch('isHot',function (newValue, oldValue) {
            console.log('isHot被修改', newValue, oldValue);
        })

1.15computed和watch之间的区别

computed和watch之间的区别:

        1、computed能完成的功能,watch都可以完成

        2、watch能完成的,computed不一定可以完成,例如:watch可以进行异步操作(函数的回调)

两个重要的小原则:

        1、被Vue管理的函数,最好写成普通函数function(){},这样this就指向Vue或组件实例对象

        2、所有不被Vue管理的函数(定时器回调函数,ajax的回调函数、promise的回调函数等),最好写成箭头函数()=>{},这样this的指向才是vm 或者组件实例对象。

1.16绑定样式

绑定样式:

                    1. class样式

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

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

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

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

                    2. style样式

                                :style="{fontSize: xxx}"其中xxx是动态值。

                                :style="[a,b]"其中a、b是样式对象。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>绑定样式</title>
		<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>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 
			绑定样式:
					1. class样式
								写法:class="xxx" xxx可以是字符串、对象、数组。
										字符串写法适用于:类名不确定,要动态获取。
										对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
										数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
					2. style样式
								:style="{fontSize: xxx}"其中xxx是动态值。
								:style="[a,b]"其中a、b是样式对象。
		-->
		<!-- 准备好一个容器-->
		<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>
	</body>

	<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>
	
</html>

1.17条件渲染

条件渲染:

                            1.v-if

                                        写法:

                                                (1).v-if="表达式"

                                                (2).v-else-if="表达式"

                                                (3).v-else="表达式"

                                        适用于:切换频率较低的场景。

                                        特点:不展示的DOM元素直接被移除!!!(DOM中看不到)

                                        注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”!!!还可以和标签template使用,template标签不会出现在DOM中

                            2.v-show

                                        写法:v-show="表达式"

                                        适用于:切换频率较高的场景。

                                        特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

                               

                            3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

		<div id="root">
			<h2>当前的n值是:{{n}}</h2>
			<button @click="n++">点我n+1</button>
			<!-- 使用v-show做条件渲染 -->
			<!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
			<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->

			<!-- 使用v-if做条件渲染 -->
			<!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
			<!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->

			<!-- v-else和v-else-if -->
			<!-- <div v-if="n === 1">Angular</div>
			<div v-else-if="n === 2">React</div>
			<div v-else-if="n === 3">Vue</div>
			<div v-else>哈哈</div> -->

			<!-- v-if与template的配合使用 -->
			<template v-if="n === 1">
				<h2>你好</h2>
				<h2>尚硅谷</h2>
				<h2>北京</h2>
			</template>

		</div>

1.18列表渲染

1.18.1v-for遍历

v-for指令:

                        1.用于展示列表数据

                        2.语法:v-for="(item, index) in xxx" :key="yyy"

                        3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

		<!-- 准备好一个容器-->
		<div id="root">
			<!-- 遍历数组 -->
			<h2>人员列表(遍历数组)</h2>
			<ul>
				<li v-for="(p,index) of persons" :key="index">
					{{p.name}}-{{p.age}}
				</li>
			</ul>

			<!-- 遍历对象 -->
			<h2>汽车信息(遍历对象)</h2>
			<ul>
				<li v-for="(value,k) of car" :key="k">
					{{k}}-{{value}}
				</li>
			</ul>

			<!-- 遍历字符串 -->
			<h2>测试遍历字符串(用得少)</h2>
			<ul>
				<li v-for="(char,index) of str" :key="index">
					{{char}}-{{index}}
				</li>
			</ul>
			
			<!-- 遍历指定次数 -->
			<h2>测试遍历指定次数(用得少)</h2>
			<ul>
				<li v-for="(number,index) of 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>

1.18.2key作用与原理

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

                        1. 虚拟DOM中key的作用:

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

                        2.对比规则:

                                    (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

                                                ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

                                                ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

                                 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key:

                                                创建新的真实DOM,随后渲染到到页面。       

                        3. 用index作为key可能会引发的问题:

                                            1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

                                                            会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

                                            2. 如果结构中还包含输入类的DOM:

                                                            会产生错误DOM更新 ==> 界面有问题。

                        4. 开发中如何选择key?:

                                            1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

                                            2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示, 使用index作为key是没有问题的。

 1.18.3列表过滤

用到了filter(element)方法

<body>
    <div class="root">
        <h2>One Piece</h2>
    <input type="text" placeholder="请输入你想输入的海贼" v-model="keyword">
    <ul>
        <li v-for="(p,index) in filpersons" :key="filpersons.id">{{p.name}}-{{p.age}}</li>
    </ul>
    </div>
    <script>
        new Vue({
            el:'.root',
            data() {
                return {
                    keyword:'',
                    persons:[
                        {id:001,name:"娜美",age:18},
                        {id:002,name:'蒙奇D路飞',age:19},
                        {id:003,name:'蒙奇D卡普',age:50},
                        {id:004,name:'蒙奇D龙',age:35},
                        {id:005,name:'美杜莎',age:25},
                    ]
                }
            },
            computed: {//列表过滤用到filter方法,必须传递一个参数,可以参考MDN
                filpersons(){
                    return this.persons.filter((p)=>{
                        //console.log(this);
                        return  p.name.indexOf(this.keyword) !== -1
                        
                    })
                }
            },
            
        })
    </script>
</body>

 1.18.4列表排序

用到了数组sort(p1,p2)方法

<body>
    <div class="root">
        <h2>One Piece</h2>
    <input type="text" placeholder="请输入你想输入的海贼" v-model="keyword">
    <button @click="sortInfo=0">年龄原序</button>
    <button @click="sortInfo=1">年龄升序</button>
    <button @click="sortInfo=2">年龄降序</button>
    
    <ul>
        <li v-for="(p,index) in filpersons" :key="filpersons.id">{{p.name}}-{{p.age}}</li>
    </ul>
    </div>
    <script>
        new Vue({
            el:'.root',
            data() {
                return {
                    keyword:'',
                    persons:[
                        {id:001,name:"娜美",age:18},
                        {id:002,name:'蒙奇D路飞',age:19},
                        {id:003,name:'蒙奇D卡普',age:50},
                        {id:004,name:'蒙奇D龙',age:35},
                        {id:005,name:'美杜莎',age:25},
                    ],
                    sortInfo:0
                }
            },
            computed: {//列表过滤用到filter方法,必须传递一个参数,可以参考MDN
                filpersons(){
                    const arr= this.persons.filter((p)=>{
                        // console.log(this);
                        return  p.name.indexOf(this.keyword) !== -1
                        
                    })
                    if(this.sortInfo){
                            return arr.sort((p1,p2)=>{
                                return this.sortInfo==1? p1.age - p2.age : p2.age-p1.age
                            })
                        }else{
                            return arr
                        }
                    
                }
            },
            
        })
    </script>
</body>

1.19Vue数据监视

        1.19.1模拟数据监视原理(对象)

        简单模拟了vue的数据监视,不过vue监视的更加完善,多层都可以监视

<body>
    <script>
        let data = {
            name:'路飞',
            address:'忘了'
        }
        // 创建一个监视的实例对象,用来监视data中属性的变化
        const obs = new Observer(data)
        console.log(obs);

        // 准备一个vm实例对象
        let vm ={}
        vm._data = data = obs
        
        function Observer(obj){
            // 将对象中的属性遍历成数组
            const keys = Object.keys(obj)
            // 遍历数组
            keys.forEach((k)=>{
                Object.defineProperty(this,k,{//this指的是obs这个实例对象
                    get(){
                        // console.log(this);
                        return obj[k]
                    },
                    set(val){
                        console.log('k被修改,我得解析模板');
                        obj[k] = val
                    }
                })
            })
        }
    </script>
</body>

        1.19.2Vue.set()方法

        Vue.set(target,key,val)或者vm.$set(target,key,val)可以向响应式对象里面添加(修改        )一个属性。

        注意:

                对象不能是Vue实例,或是Vue实例的根数据对象!!!(记住不能给vue或者vm添加就行)

<body>
    <div class="root">
        <h2>性别:{{school.sex}}</h2>
    </div>
    <script>
        // Vue.set(target,key,val)或者vm.$set(target,key,val)
        const vm =new Vue({
            el:'.root',
            data() {
                return {
                    school:{
                          
                    }
                }
            },
        })
    </script>


</body>

        1.19.3模拟数据监视原理(数组)

给数组添加或者删除可以用:

        (1)vm.$set(vm.person.hobby,0,'gan')

        (2)数组的操作方法:vm.person.hobby.push('做梦'),vm.person.hobby.splice(0,1,'chi')

<body>
    <div class="root">
        <h2>我的爱好</h2>
        <ul>
            <li v-for="(item,index) in person.hobby" :key="index">
                {{item}}
            </li>
        </ul>
    </div>
    <script>
        //vm.person.hobby.splice(0,1,'chi')
        // vm.person.hobby.push('做梦')
        // vm.$set(vm.person.hobby,0,'gan')
        const vm = new Vue({
            el:'.root',
            data(){
                return {
                    person:{
                        hobby:['干饭','动漫','打游戏']
                    }


                }
            }

        })
    </script>
</body>

        1.19.4数据监视总结

1.20收集表单数据

收集表单数据:

                    若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。

                    若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。

                    若:<input type="checkbox"/>

                            1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)

                            2.配置input的value属性:

                                    (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)

                                    (2)v-model的初始值是数组,那么收集的的就是value组成的数组

                    备注:v-model的三个修饰符:

                                    lazy:失去焦点再收集数据

                                    number:输入字符串转为有效的数字

                                    trim:输入首尾空格过滤

<body>
    <div class="root">
        <form @submit.prevent="demo">
            账号:<input type="text" v-model.lazy.trim="account"><br><br>
            密码:<input type="password" v-model="password"><br><br>
            年龄:<input type="number" v-model.number="age"><br><br>
            <!-- 收集性别时,我们可以给input添加value -->
            性别: <input type="radio" name="sex" v-model="sex" value="male">男<input type="radio" name="sex" v-model="sex" value="female">女<br><br>
            爱好: <input type="checkbox" v-model="hobby" value="study">睡觉
                  <input type="checkbox" v-model="hobby" value="cartoon">动漫
                  <input type="checkbox" v-model="hobby" value="game">打游戏<br><br>
            所属地区:
            <select v-model="city">
                <option value="">点我选择你在哪儿</option>
                <option value="bj">北京</option>
                <option value="hn">河南</option>
                <option value="78">M78</option>
            </select><br><br>
            其他信息:
            <textarea ></textarea><br><br>
            <input type="checkbox" v-model="agree">
            你敢点我?<a href="http://www.baidu.com">给你好看的看看</a>
            <button>起飞</button>
        </form>
    </div>
    <script>
         new Vue({
            el:'.root',
            data() {
                return {
                    account:'',
                    password:'',
                    sex:'male',
                    hobby:[],//收集多个数据时,用数组
                    city:'',
                    agree:'true',
                    age:''
                }
                
            },
            methods: {
                    demo(){
                        console.log(JSON.stringify(this._data));//将数据用字符串的形式输出
                    }
                },
        })
    </script>
</body>

1.21过滤器

过滤器:

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

                语法:

                        1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}

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

                备注:

                        1.过滤器也可以接收额外参数、多个过滤器也可以串联

                        2.并没有改变原本的数据, 是产生新的对应的数据

<body>
    <div class="root">
        <h2>显示格式化后的时间</h2>
        <!-- 计算属性 -->
        <h3>现在是{{fmtime}}</h3>
        <!-- methods方法 -->
        <h3>现在是{{gettime()}}</h3>
        <!-- 过滤器 -->
        <h3>现在是{{nowtime | getFormate}}</h3>
        <!-- 过滤器传递参数 -->
        <h3>现在是{{nowtime | getFormate('YYYY年MM月DD日')}}</h3>
        <!-- 先执行nowtime | getFormate('YYYY年MM月DD日'),再把结果和myslice执行 -->
        <h3>现在是{{nowtime | getFormate('YYYY年MM月DD日') | myslice}}</h3>
        <!-- 可以单向绑定用,不能双向!! -->
        <h3 :x="name | myslice">现在是{{fmtime}}</h3>


    </div>
    <script>
        // 全局声明
        Vue.filter('myslice',function(value) {
            return value.slice(0,4)
        })
        new Vue({
            el:'.root',
            data() {
                return {
                    nowtime:'1658562464535',
                    name:'12132132132'
            }
        },
        computed: {
            fmtime(){
                return dayjs(this.nowtime).format('YYYY年MM月DD日 HH:mm:ss')
            }
        },
        methods: {
            gettime(){
                return dayjs(this.nowtime).format('YYYY年MM月DD日 HH:mm:ss')

            }
        },
        // 局部声明
        filters:{
            getFormate(value,str='YYYY年MM月DD日 HH:mm:ss'){//es6新语法,如果没有str就按照括号里面的参数,如果传递的有参数,就按照参数来
                return dayjs(value).format(str)
            }
            
        }
    })
    </script>
</body>

1.22内置指令

我们学过的指令:

                        v-bind  : 单向绑定解析表达式, 可简写为 :xxx

                        v-model : 双向数据绑定

                        v-for   : 遍历数组/对象/字符串

                        v-on    : 绑定事件监听, 可简写为@

                        v-if        : 条件渲染(动态控制节点是否存存在)

                        v-else  : 条件渲染(动态控制节点是否存存在)

                        v-show  : 条件渲染 (动态控制节点是否展示)

                v-text指令:

                        1.作用:向其所在的节点中渲染文本内容。

                        2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会,并且不识别html标签。

                v-html指令:

                        1.作用:向指定节点中渲染包含html结构的内容。

                        2.与插值语法的区别:

                                    (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。

                                    (2).v-html可以识别html结构。

                        3.严重注意:v-html有安全性问题!!!!

                                    (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。

                                    (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!(可能会带走你的cookie哦!!!)

		<div id="root">
			<div>hello,{{name}}</div>
			<div v-html="str"></div>
			<div v-html="str2"></div>
		</div>
	</body>

	<script type="text/javascript">
		new Vue({
			el:'#root',
			data:{
				name:'阿胜',
				str:'<h3>你好啊!</h3>',
				str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟冲呀</a>',
			}
		})
	</script>

                v-cloak指令(没有值):

                        1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。

                        2.使用css配合v-cloak(css里面用属性原则器【v-cloak】)可以解决网速慢时页面展示出{{xxx}}的问题。

                v-once指令(没有值):

                        1.v-once所在节点在初次动态渲染后,就视为静态内容了。

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

<h2 v-once>初始化的n值是:{{n}}</h2>

                v-pre指令:

                    1.跳过其所在节点的编译过程。

                    2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

1.23自定义指令

       1.23.1 函数式和对象式

<body>
        <div class="root">
        <h2>当前的n值是<span v-text="n"></span></h2>
        <h2>扩大10倍后的值<span v-big="n"></span></h2>
        <button @click="n++">点我值加1</button>
        <hr>
        <input type="text" v-fbind:value="n">
        </div>
        <script>
            //定义全局指令
		/* Vue.directive('fbind',{
			//指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			//指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			//指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		}) */
            new Vue({
                el:'.root',
                data(){
                    return {
                        n:1
                    }
                },
                directives:{
                    //big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
				    /* 'big-number'(element,binding){
					// console.log('big')
					element.innerText = binding.value * 10
				    }, */
                    // 函数式
                    big(element,binding){
                        console.log(binding);
                        element.innerHTML = binding.value * 10
                    },
                    // 对象式
                    fbind:{
                        // 绑定时调用
                        bind(element,binding){
                            console.log('big',this) //注意此处的this是window
                            element.value= binding.value
                        },
                        // 插入是调用
                        inserted(element,binding){
                            element.focus()
                        },
                        // 模板重新解析时调用
                        update(element,binding) {
                            element.value = binding.value
                            
                        },
                    }
                }
            })
    </script>

</body>

自定义指令总结:

                        一、定义语法:

                                    (1).局部指令:

                                                new Vue({ directives:{指令名:配置对象}) 或者     

                                                new Vue({ directives{指令名:回调函数})                             

                                    (2).全局指令:

                                                    Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

                        二、配置对象中常用的3个回调:

                                    (1).bind:指令与元素成功绑定时调用。

                                    (2).inserted:指令所在元素被插入页面时调用。

                                    (3).update:指令所在模板结构被重新解析时调用。

                        三、备注:

                                    1.指令定义时不加v-,但使用时要加v-;

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

                                     3.注意!!!里面的this不是vue,是window!

1.24生命周期

        1.24.1引出生命周期

生命周期:

                        1.又名:生命周期回调函数、生命周期函数、生命周期钩子。

                        2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。

                        3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

                        4.生命周期函数中的this指向是vm 或 组件实例对象。

<body>
    <div class="root">
        <h2 :style="{opacity}">史蒂芬·胜</h2>
    </div>
    <script>
        const vm = new Vue({
            el:'.root',
            data() {
                return {
                    opacity:1
                }
            },
            mounted() {//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
                setInterval(() => {
                this.opacity-=0.1
                if(this.opacity <= 0) this.opacity = 1
        }, 100);
            },
        })
        /* window.onload = ()=>{//不推荐,这样写就不需要vm了,浪费内存
            setInterval(() => {
            vm.opacity-=0.1
            if(vm.opacity <= 0) vm.opacity = 1
        }, 100);
        } */
        
    </script>
</body>

        1.24.2生命周期总结

常用的生命周期钩子:

                        1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。

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

                关于销毁Vue实例

                        1.销毁后借助Vue开发者工具看不到任何信息。

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

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

需要注意是:

前面的创建是数据代理和数据监视,一定不要以为是vm哦!!!

 

<body>
    <div class="root">
        <h2 :style="{opacity}">史蒂芬·胜</h2>
        <button @click="stop">点我嗝屁</button>
        <button @click="opacity=1">点我变回原来模样</button>
    </div>
    <script>
        const vm = new Vue({
            el:'.root',
            data() {
                return {
                    opacity:1
                }
            },
            methods: {
                stop(){ 
                    vm.$destroy()
                }
            },
            mounted() {//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
                this.timer = setInterval(() => {
                this.opacity-=0.1
                console.log('救命啊');
                if(this.opacity <= 0) this.opacity = 1
        }, 100);
            },
            beforeDestroy() {//这个以后再组件的时候,方便他杀哈哈
                clearInterval(this.timer)
            },
        })
        /* window.onload = ()=>{//不推荐,这样写就不需要vm了,浪费内存
            setInterval(() => {
            vm.opacity-=0.1
            if(vm.opacity <= 0) vm.opacity = 1
        }, 100);
        } */
        
    </script>
</body>

2.Vue组件化编程

        2.1组件化理解

        模块

                1. 理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件

                2. 为什么: js 文件很多很复杂

                3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率
        组件
        1. 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image…..)
        2.为什么: 一个界面的功能很复杂
        3. 作用: 复用编码, 简化项目编码, 提高运行效率
模块化
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。(就是把一个js文件拆成多个)
组件化
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。

        2.2非单文件组件

        非单文件组件:一个文件里有多个组件

        单文件组件:一个文件里有1个组件

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

                    一、定义组件(创建组件)

                    二、注册组件

                    三、使用组件(写组件标签)

            一、如何定义一个组件?

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

                        区别如下:

                                1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。

                                2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。

                        备注:使用template可以配置组件结构。

            二、如何注册组件?

                            1.局部注册:靠new Vue的时候传入components选项

                            2.全局注册:靠Vue.component('组件名',组件)

            三、编写组件标签:

                            <school></school>

<body>
    <div class="root">
        <Hello></Hello>
        <School></School>
        <Student></Student>
    </div>
    <hr>
    <div class="root2">
        <Hello></Hello>
    </div>
    <script>
        const School = Vue.extend({
            template:`
                <div>
                    <h2>孤狼名称:{{name}}</h2>
                    <h2>孤狼地区:{{address}}</h2>    
                </div>
            `,
            data() {
                return {
                    name:'西伯利亚土狗',
                    address:'西伯利亚'

                }
            },
        })
        const Student = Vue.extend({
            template:`
                <div>
                    <h2>学生名称:{{name}}</h2>
                    <h2>学生地区:{{address}}</h2>    
                </div>
            `,
            data() {
                return {
                    name:'蒙奇D路飞',
                    address:'风车村'

                }
            },
        })
        const Hello = Vue.extend({
            template:`
                <div>
                    <h2>你好!{{name}}</h2>
                </div>
            `,
            data() {
                return {
                    name:'大马猴',
                }
            },
        })
        Vue.component('Hello',Hello)//全局声明
        new Vue({
            el:'.root',
            components:{
                School,
                Student
            }
        })
        new Vue({
            el:'.root2',
        })
    </script>
</body>

        2.3组件注意事项

几个注意点:

                    1.关于组件名:

                                一个单词组成:

                                            第一种写法(首字母小写):school

                                            第二种写法(首字母大写):School

                                多个单词组成:

                                            第一种写法(kebab-case命名):my-school

                                            第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)

                                备注:

                                        (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。

                                        (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

                    2.关于组件标签:

                                第一种写法:<school></school>

                                第二种写法:<school/>

                                备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。

                    3.一个简写方式:

                                const school = Vue.extend(options) 可简写为:const school = options

        2.4组件嵌套

<body>
    <div class="root">
        <App></App>
    </div>
   
    <script>
        // 定义student组件
        const Student = Vue.extend({
            template:`
                <div>
                    <h2>学生名称:{{name}}</h2>
                    <h2>学生地区:{{address}}</h2>    
                </div>
            `,
            data() {
                return {
                    name:'蒙奇D路飞',
                    address:'风车村'

                }
            },
        })
        // 定义school组件
        const School = Vue.extend({
            template:`
                <div>
                    <h2>孤狼名称:{{name}}</h2>
                    <h2>孤狼地区:{{address}}</h2>
                    <Student></Student>  
                </div>
            `,
            data() {
                return {
                    name:'西伯利亚土狗',
                    address:'西伯利亚'

                }
            },
            components:{
                Student
            }
        })
        
        // 定义hello组件
        const Hello = Vue.extend({
            template:`
                <div>
                    <h2>你好!{{name}}</h2>
                </div>
            `,
            data() {
                return {
                    name:'大马猴',
                }
            },
        })
        // 定义APP组件
        const App = Vue.extend({
            template:`
                <div>
                    <School></School>
                    <Hello></Hello>
                </div>
            `,
            components:{
                Hello,
                School
            }
            
        })
        Vue.component('Hello',Hello)//全局声明
        
        new Vue({
            el:'.root',
            template:`
                    <div>
                        <App></App>
                        <hr>
                        <App></App>
                      
                    </div>            
                `
            ,
            components:{
                App
            }
        })
       
    </script>
</body>

2.5 Vuecomponent

关于VueComponent:

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

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

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

                        4.关于this指向:

                                (1).组件配置中:

                                            data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。

                                (2).new Vue(options)配置中:

                                            data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

                        5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。

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

2.6一个重要的内置关系

                1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype

                2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

<body>
    <div class="root">

    </div>
    <script>
        // 定义一个构造函数
        function demo() {
            this.a = 1
            this.b = 2
        }
        // 创建一个demo实例对象
        const d =new demo()

        // 显式原型属性 和 隐式原型属性 他俩统统指向一个对象:原型对象
        // 只有函数有显式原型属性,实例对象有隐式原型属性
        console.log(demo.prototype);//显式原型属性

        console.log(d.__proto__);//隐式原型属性

        console.log(demo.prototype === d.__proto__);
        console.log(d);
        new Vue({
            el:'.root',
            data() {
                return {
                    
                }
            },
        })
    </script>
</body>

2.7单文件组件

Student.vue

<template>
  <div class="demo">
    <h2>姓名:{{name}}</h2>
    <h2>住址:{{address}}</h2>
    <button @click="showhaha">点我嘻哈哈</button>
  </div>
</template>

<script>
        // 简写
        export default {
        name:'Student',
        data() {
             return {
                 name:'路飞',
                 address:'风车村'
             }
         },
         methods: {
             showhaha(){
                 alert('xixi')
             }
         }, 
        }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>

School.vue

<template>
  <div class="demo">
    <h2>姓名:{{name}}</h2>
    <h2>住址:{{address}}</h2>
    <button @click="showhaha">点我嘻哈哈</button>
  </div>
</template>

<script>
        // export分别暴露
        /* export const school =  Vue.extend({
            data() {
                return {
                    name:'路飞',
                    address:'风车村'
                }
            },
            methods: {
                showhaha(){
                    alert('xixi')
                }
            },
        }) */


        // 统一暴露
        /* const school =  Vue.extend({
            data() {
                return {
                    name:'路飞',
                    address:'风车村'
                }
            },
            methods: {
                showhaha(){
                    alert('xixi')
                }
            },
        })
        export{school} */


        // 默认暴露
        /* const school =  Vue.extend({
            data() {
                return {
                    name:'路飞',
                    address:'风车村'
                }
            },
            methods: {
                showhaha(){
                    alert('xixi')
                }
            },
        })
        export default school */

        // 使用默认暴露,引入的时候用  import ... from ...
        // 使用统一暴露或者分别暴露,引入的时候用  import {...} from ...

        // 简写
        export default {
        name:'School',
        data() {
             return {
                 name:'海贼培训基地',
                 address:'东海'
             }
         },
         methods: {
             showhaha(){
                 alert('xixi')
             }
         }, 
        }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>

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>

<style>

</style>

main.js

import App from './App.vue'

new Vue({
    el:'.root',
    components:{
        App
    }
})

index.html

<body>
    <div class="root">
        <App></App>
    </div>
    <script src="../js/vue.js"></script>
    <script src="./main.js"></script>
    
</body>

注意:

         使用默认暴露,引入的时候用  import ... from ...

         使用统一暴露或者分别暴露,引入的时候用  import {...} from ...

使用情况在school.vue代码里面有

 3.Vue.cli脚手架(command line interface)

3.1初始化脚手架

最重要!!!        安装脚手架之前首先需要安装node.js

3.1.1说明

        1. Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。
        2. 最新的版本是 4.x。
        3. 文档: vue.cli

3.1.2 具体步骤

第一步(仅第一次执行):全局安装@vue/cli。
                npm install -g @vue/cli
第二步: 切换到你要创建项目的目录 ,然后使用命令创建项目
                vue create xxxx
第三步:启动项目
                npm run serve
当需要暂停项目时:Ctrl+C
注意:
1. 如出现下载缓慢请配置 npm 淘宝镜像:
        npm config set registry https://registry.npm.taobao.org
2. 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:包版本控制文件

3.1.4render函数

关于不同版本的Vue:

        1.vue.js与vue.runtime.xxx.js的区别:

                (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。

                (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。

        2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用

            render函数接收到的createElement函数去指定具体内容。

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

3.1.5修改默认配置

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpack 配置, 请执行:
        vue inspect > output.js
不可以改的配置:文件夹:public、src;favicon.con、index.html、main.js
1、先创建一个vue.config.js
2、前往vue官网找到vue cli ,复制配置参考里面的pages

配置参考 | Vue CLI

语法检查lintOnSave(和pages同级)

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

3.2ref属性

ref属性

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

<template>
  <div class="demo">
    <h2>姓名:{{name}}</h2>
    <h2 ref="title">住址:{{address}}</h2>
    <button @click="showInfo" >点我显示DOM元素</button>
    <button @click="showhaha">点我嘻哈哈</button>
  </div>
</template>

<script>
        // 简写
        export default {
        name:'MyStudent',
        data() {
             return {
                 name:'路飞',
                 address:'风车村'
             }
         },
         methods: {
             showhaha(){
                 alert('xixi')
             },
             showInfo(){
                console.log(this.$refs.title);
                // console.log(document.querySelector('#title'));
             }
         }, 
        }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>


3.3props配置

props配置项

1. 功能:让组件接收外部传过来的数据(外部数据优先级大于内部)

2. 传递数据:```<Demo name="xxx"/>```

3. 接收数据:

    1. 第一种方式(只接收):```props:['name'] ```

    2. 第二种方式(限制类型):```props:{name:String}```

    3. 第三种方式(限制类型、限制必要性、指定默认值):
        props:{
            name:{
            type:String, //类型
            required:true, //必要性
            default:'老王' //默认值
            }
        }

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

student.vue

<template>
  <div class="demo">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{myage+1}}</h2>
    <h2>性别:{{sex}}</h2>
    <button @click='myage++'>点我年龄++</button>

  </div>
</template>

<script>
        // 简写
        export default {
        name:'MyStudent',
        data() {
             return {
                 myage:this.age
             }
         },
        
        // props:['sex','name','age']//简单接受
        //接受的同时,对类型进行限制
        /* props:{
            name:String,
            age:Number,
            sex:String

        } */
        //接受的同时,对类型进行限制+必要的限制
        props:{
            name:{
                type:String,
                required:true//必要的
            },
            sex:{
                type:String,
                required:true//必要的
            },
            age:{
                type:Number,
                default:99//默认
            }
        }
    }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>

app.vue 

<template>
  <div>
      <MyStudent name='路飞' sex='man' :age='18'/>
      <MyStudent name='娜美' sex='woman' />

  </div>
</template>

<script>
import MyStudent from './components/Student'
function extend() {
  
}

export default {
    name:'App',
    components:{
        MyStudent
    }
}
</script>

<style>

</style>

3.4mixin(混合、混入)

mixin(混入)

1. 功能:可以把多个组件共用的配置提取成一个混入对象(内部数据优先级大于外部)

2. 使用方式:

    第一步定义混合:

    ```
    {
        data(){....},
        methods:{....}
        ....
    }
    ```

    第二步使用混入:

    ​    全局混入:```Vue.mixin(xxx)```全局混入时在main.js里面引入


    ​    局部混入:```mixins:['xxx']    ```

mixin.js

export const a = {
    methods: {
        showName(){
            alert(this.name)
            console.log(666);
        }
    },
}

student.vue

<template>
  <div class="demo">
    <h2 @click="showName">姓名:{{name}}</h2>
    <h2>年龄:{{myage}}</h2>
    <h2>性别:{{sex}}</h2>
    <button @click='myage++'>点我年龄++</button>

  </div>
</template>

<script>
        // import {a} from '../mixin.js'
        // 简写
        export default {
        name:'MyStudent',
        data() {
             return {
                name:'路飞',
                age:18,
                myage:this.age,
                sex:'man'

             }
         },
        // mixins:[a]
        
        
    }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>

school.vue

<template>
  <div class="demo">
    <h2 @click="showName">姓名:{{name}}</h2>
    <h2>住址:{{address}}</h2>
    <button>点我嘻哈哈</button>
  </div>
</template>

<script>
        // import {a} from '../mixin.js'

        export default {
        name:'MySchool',
        data() {
             return {
                 name:'海贼培训基地',
                 address:'东海'
             }
         },
        //  mixins:[a]
        }

</script>

<style>
    .demo {
        background-color: skyblue;
    }
</style>

app.vue

<template>
  <div>
      <MyStudent/>
      <MySchool/>

  </div>
</template>

<script>

import MySchool from './components/School'
import MyStudent from './components/Student'



export default {
    name:'App',
    components:{
        MyStudent,MySchool
    }
}
</script>

<style>

</style>

main.js

/* 
  该文件是整个项目的入口文件
 */

// 引入vue
import Vue from 'vue'
// 引入APP组件,它是所有组件的父组件
import App from './App.vue'
// 关闭vue生产提示
Vue.config.productionTip = false
import {a} from './mixin.js'
Vue.mixin(a)
// 创建vue实例
new Vue({
  render: h => h(App),
}).$mount('#app')

3.5.插件

插件

1. 功能:用于增强Vue

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

3. 定义插件:

    ```js
    对象.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入(合)
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    ```

4. 使用插件:```Vue.use()```

3.6scoped样式

scoped样式

1. 作用:让样式在局部生效,防止冲突。
2. 写法:```<style scoped>```

3.7todoList案例

myItem

<template>
    <li>
          <label>
            <input type="checkbox" :checked='todo.done' @click="handleCheck(todo.id)"/>
            <span>{{todo.title}}</span>
          </label>
          <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    </li>
    
    
</template>

<script>
    export default {
        name:'MyItem',
        props:['todo','checkTodo','deleteTodo'],
        methods: {
          handleCheck(id){
            this.checkTodo(id)
          },
          handleDelete(id){
            if(confirm('确定删除吗')) 
            this.deleteTodo(id)
          }
        },
    }
</script>
<style scoped>
    /*item*/
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 button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}
li:hover {
  background-color: #ddd;
}
li:hover button{
  display:block;
}
</style>

mylist

<template>
    <ul class="todo-main">
        <MyItem v-for="item in todos" 
            :key='item.id' 
            :todo='item' 
            :checkTodo='checkTodo'
            :deleteTodo="deleteTodo"
        />
        
    </ul>
</template>

<script>
    import  MyItem from './MyItem.vue'
    export default {
        name:'MyList',
        components:{MyItem},
        props:['todos','checkTodo','deleteTodo']
    }
</script>
<style scoped>
    /*main*/
.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>

myheader

<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"  @keyup.enter="add" />
    </div>
    
</template>

<script>
    import {nanoid} from "nanoid";
    export default {
        name:'MyHeader',
        props:['receive'],
        
        methods: {
            add(e){
                //将输入的变成对象,id是唯一的,通过nanoid方法获得
                // trim()方法可以去掉文字两边的空格
                if(e.target.value !== ''){
                    const todoObj = {id:nanoid(),title:e.target.value.trim(),done:false}
                    this.receive(todoObj)
                    e.target.value=''
                    }else{
                        alert('内容为空')
                    }
                
                
            }
        },
    }
</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, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

myfooter

<template>
    <div class="todo-footer" v-show="todos.length">
        <label>
          <input type="checkbox" :checked="todoDone == todos.length &&todos.length>0" @change="checkAll"/>
        </label>
        <span>
          <span>已完成{{todoDone}}</span> / 全部{{todos.length}}
        </span>
        <button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
      </div>
</template>

<script>
    export default {
        name:'MyFooter',
        props:['todos','clearAll'],
        computed:{
          /* todoDone(){
            let i = 0
            this.todos.forEach((todo) => {
              if(todo.done) i++
            })
            return i
          } */
          todoDone(){
            // pre是上一次返回的值,第一次是后面开始统计的数0,current是遍历的每一个对象
            return this.todos.reduce((pre,current)=>pre +(current.done ? 1  :0),0)
          }
        },
        methods:{
          checkAll(e){
            this.todos.forEach(todo => {
              todo.done = e.target.checked
            })
          },
          clearTodo(){
            if(confirm('确定删除吗')) this.clearAll()
          }
        }
        
    }
</script>

<style scoped>
    /*footer*/
.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 button {
  float: right;
  margin-top: 5px;
}
</style>

myapp

<template>
  <div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <MyHeader :receive='receive'/>
      <MyList :todos='todos' :checkTodo='checkTodo' :deleteTodo='deleteTodo'/>
      <MyFooter :todos='todos' :clearAll="clearAll"/>
    </div>
  </div>
</div>
</template>

<script>
import MyHeader from './components/MyHeader.vue'
import MyFooter from './components/MyFooter.vue'
import MyItem from './components/MyItem.vue'
import MyList from './components/MyList.vue'

export default {
    name:'App',
    components:{
        MyHeader,MyFooter,MyItem,MyList
    },
    data() {
            return {
                todos:[
                    {id:'001',title:'看假面骑士',done:true},
                    {id:'002',title:'海贼王',done:false},
                    {id:'003',title:'开车',done:true}
                ]
            }
        },
    methods:{
      // 添加一个todo项
      receive(todoObj){
        this.todos.unshift(todoObj)
      },
      // 勾选框
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id == id) todo.done = !todo.done
        })
      },
      // 删除
       deleteTodo(id){
        this.todos = this.todos.filter(todo=>todo.id !== id )
       
       },
      //  清除做了的
      clearAll(){
        this.todos = this.todos.filter(todo=> !todo.done)
      }

    }
}
</script>

<style>
  /*base*/
  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, 255, 0.2), 0 1px 2px rgba(0, 0, 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>

总结TodoList案例

1. 组件化编码流程:

    ​    (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​    (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​            1).一个组件在用:放在组件自身即可。

    ​            2). 一些组件在用:放在他们共同的父组件上(<span style="color:red">状态提升</span>)。

    ​    (3).实现交互:从绑定事件开始。

2. props适用于:

    ​    (1).父组件 ==> 子组件 通信

    ​    (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

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


3.8WebStorage

webStorage

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

2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

3. 相关API:

    1. ```xxxxxStorage.setItem('key', 'value');```
                        该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

    2. ```xxxxxStorage.getItem('person');```

        ​        该方法接受一个键名作为参数,返回键名对应的值。

    3. ```xxxxxStorage.removeItem('key');```

        ​        该方法接受一个键名作为参数,并把该键名从存储中删除。

    4. ``` xxxxxStorage.clear()```

        ​        该方法会清空存储中的所有数据。

4. 备注:

    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. ```xxxxxStorage.getItem(xxx)```如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. ```JSON.parse(null)```的结果依然是null。

3.9todoList本地存储

app

<template>
  <div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <MyHeader :receive='receive'/>
      <MyList :todos='todos' :checkTodo='checkTodo' :deleteTodo='deleteTodo'/>
      <MyFooter :todos='todos' :clearAll="clearAll"/>
    </div>
  </div>
</div>
</template>

<script>
import MyHeader from './components/MyHeader.vue'
import MyFooter from './components/MyFooter.vue'
import MyItem from './components/MyItem.vue'
import MyList from './components/MyList.vue'

export default {
    name:'App',
    components:{
        MyHeader,MyFooter,MyItem,MyList
    },
    data() {
            return {
                todos:JSON.parse(localStorage.getItem('todo'))||[]
            }
        },
    methods:{
      // 添加一个todo项
      receive(todoObj){
        this.todos.unshift(todoObj)
      },
      // 勾选框
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id == id) todo.done = !todo.done
        })
      },
      // 删除
       deleteTodo(id){
        this.todos = this.todos.filter(todo=>todo.id !== id )
       
       },
      //  清除做了的
      clearAll(){
        this.todos = this.todos.filter(todo=> !todo.done)
      }

    },
    watch:{
      todos:{
        deep:true,
        handler(val){
          window.localStorage.setItem('todo',JSON.stringify(val))
        }
      }
    }

}
</script>

<style>
  /*base*/
  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, 255, 0.2), 0 1px 2px rgba(0, 0, 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>

myfooter

<template>
    <div class="todo-footer" v-show="todos.length">
        <label>
          <input type="checkbox" :checked="todoDone == todos.length &&todos.length>0" @change="checkAll"/>
        </label>
        <span>
          <span>已完成{{todoDone}}</span> / 全部{{todos.length}}
        </span>
        <button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
      </div>
</template>

<script>
    export default {
        name:'MyFooter',
        props:['todos','clearAll'],
        computed:{
          /* todoDone(){
            let i = 0
            this.todos.forEach((todo) => {
              if(todo.done) i++
            })
            return i
          } */
          todoDone(){
            // pre是上一次返回的值,第一次是后面开始统计的数0,current是遍历的每一个对象
            return this.todos.reduce((pre,current)=>pre +(current.done ? 1  :0),0)
          }
        },
        methods:{
          checkAll(e){
            this.todos.forEach(todo => {
              todo.done = e.target.checked
            })
          },
          clearTodo(){
            if(confirm('确定删除吗')) this.clearAll()
          }
        }
        
    }
</script>

<style scoped>
    /*footer*/
.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 button {
  float: right;
  margin-top: 5px;
}
</style>

3.10组件自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

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

  3. 绑定自定义事件:

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

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

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

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

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

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

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

通过props实现子给父传值(以下是student给APP传值):

 app.vue

<template>
  <div>
    <School name="海贼培训基地" />
    <Student :receive="receiveName" />
  </div>
</template>

<script>
import School from "./components/School.vue";
import Student from "./components/Student.vue";
export default {
  name: "App",
  components: {
    School,
    Student,
  },
  data() {
    return {};
  },
  methods: {
    receiveName(name) {
      console.log("app",name);
    },
  },
};
</script>

student.vue

<template>
  <div class="demo">
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <h2>性别:{{ sex }}</h2>
    <button @click="sendInfo">名字传给app</button>
  </div>
</template>

<script>
// import pubsub from "pubsub-js";

export default {
  name: "MyStudent",
  props: ["receive"],
  data() {
    return {
      name: "路飞",
      age: 18,
      sex: "man",
    };
  },
  methods: {
    sendInfo() {
      this.receive(this.name);
    },
  },
};
</script>

总结:给父组件配置方法传递给子组件,子组件通过props接收后,通过调用该方法将值传递给父组件。

通过自定义事件子给父传值(以下是student给APP传值):(第一种方式)

app.vue

<template>
  <div>
    <School name="海贼培训基地" />
    <Student @receive="test" />
  </div>
</template>

<script>
import School from "./components/School.vue";
import Student from "./components/Student.vue";
export default {
  name: "App",
  components: {
    School,
    Student,
  },
  data() {
    return {};
  },
  methods: {
    test(name) {
      console.log('这是APP',name);
    },
  },
};

student.vue

<template>
  <div class="demo" ref="title">
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <h2>性别:{{ sex }}</h2>
    <button @click="sendInfo">名字传给app</button>
    <button @click="showInfo" >展示dom</button>
  </div>
</template>

<script>
// import pubsub from "pubsub-js";

export default {
  name: "MyStudent",
  data() {
    return {
      name: "路飞",
      age: 18,
      sex: "man",
    };
  },
  methods: {
    sendInfo() {
      this.$emit("receive", this.name);
    },
    showInfo() {
      console.log(this.$refs.title);
    },
  },
};
</script>

通过自定义事件子给父传值(以下是student给APP传值):(第二种方式)

app.vue

<template>
  <div>
    <School name="海贼培训基地" />
    <Student ref="student" />
  </div>
</template>

<script>
import School from "./components/School.vue";
import Student from "./components/Student.vue";
export default {
  name: "App",
  components: {
    School,
    Student,
  },
  data() {
    return {};
  },
  methods: {},
  mounted() {
    this.$refs.student.$on("receive", (arg) => {
      console.log("app", arg);
    });
  },
};
</script>

student.vue

<template>
  <div class="demo" ref="title">
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <h2>性别:{{ sex }}</h2>
    <button @click="sendInfo">名字传给app</button>
  </div>
</template>

<script>
// import pubsub from "pubsub-js";

export default {
  name: "MyStudent",
  data() {
    return {
      name: "路飞",
      age: 18,
      sex: "man",
    };
  },
  methods: {
    sendInfo() {
      this.$emit('receive',this.name)
    },
  },
};
</script>

3.11全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

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

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

main.js

// 引入vue
import Vue from 'vue'
// 引入APP组件,它是所有组件的父组件
import App from './App.vue'
// 关闭vue生产提示
Vue.config.productionTip = false

// 创建vue实例
new Vue({
  render: h => h(App),
  beforeCreate(){
    Vue.prototype.$bus = this
  },
  
}).$mount('#app')

 school.vue

<template>
  <div class="demo">
    <h2>School</h2>
    <h2 class="title">姓名:{{ name }}</h2>
    <h2>住址:{{ address }}</h2>
    <button @click="showInfo">点我将名字送给student</button>
  </div>
</template>

<script>
// import pubsub from "pubsub-js";
export default {
  name: "MySchool",
  props: ["name"],
  data() {
    return {
      // name: "123",
      address: "东海",
    };
  },
  methods: {
    showInfo() {
      this.$bus.$emit('receiveSchool',this.name)
    },
  },
  mounted() {
    this.$bus.$on("send", (name) => {
      console.log(this, name);
    });
  },
};
</script>

student.vue

<template>
  <div class="demo">
    <h2>student</h2>
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <h2>性别:{{ sex }}</h2>
    <button @click="sendInfo">名字传给school</button>
  </div>
</template>

<script>
// import pubsub from "pubsub-js";

export default {
  name: "MyStudent",
  data() {
    return {
      name: "路飞",
      age: 18,
      sex: "man",
    };
  },
  methods: {
    sendInfo() {
      this.$bus.$emit("send", this.name);
    },
  },
  mounted() {
    this.$bus.$on("receiveSchool", (name) => {
      console.log(this, name);
    });
  },
};
</script>

3.12消息订阅与发布(pubsub)

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(this.pid)去取消订阅。

 student.vue

<template>
  <div class="demo">
    <h2>student</h2>
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <h2>性别:{{ sex }}</h2>
    <button @click="sendInfo">名字传给school</button>
    <button @click="dead">点我取消订阅</button>
  </div>
</template>

<script>
import pubsub from "pubsub-js";

export default {
  name: "MyStudent",
  data() {
    return {
      name: "路飞",
      age: 18,
      sex: "man",
    };
  },
  methods: {
    sendInfo() {
      this.pubId = pubsub.publish("studentMsg", this.name);
    },
    dead() {
      this.$destroy();
    },
  },
  mounted() {
    //subscribe后面的回调函数里面参数第一个是订阅消息的名称,第二个往后才是接收到的参数
    pubsub.subscribe("schoolMsg", (name, urg) => {
      console.log(this, name, urg);
    });
  },
  beforeDestroy() {
    pubsub.unsubscribe(this.pubId);
  },
};
</script>

school.vue

<template>
  <div class="demo">
    <h2>School</h2>
    <h2 class="title">姓名:{{ name }}</h2>
    <h2>住址:{{ address }}</h2>
    <button @click="showInfo">点我将名字送给student</button>
  </div>
</template>

<script>
import pubsub from "pubsub-js";
export default {
  name: "MySchool",
  props: ["name"],
  data() {
    return {
      // name: "123",
      address: "东海",
    };
  },
  methods: {
    showInfo() {
      pubsub.publish("schoolMsg", this.name);
    },
  },
  mounted() {
    //subscribe后面的回调函数里面参数第一个是订阅消息的名称,第二个往后才是接收到的参数
    pubsub.subscribe("studentMsg", (name, urg) => {
      console.log(this, name,urg);
    });
  },
};
</script>

3.13nextTick

  1. 语法:this.$nextTick(回调函数)

  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

视频中是先让dom结构发生改变后,然后获取焦点,就把这个获取焦点的回调函数放在nextTick中 

 3.14Vue封装的过渡与动画

1.作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名

2.图示:

3.写法:

  1. 准备好样式:

    • 元素进入的样式:

      1. v-enter:进入的起点

      2. v-enter-active:进入过程中

      3. v-enter-to:进入的终点

    • 元素离开的样式:

      1. v-leave:离开的起点

      2. v-leave-active:离开过程中

      3. v-leave-to:离开的终点

  2. 使用<transition>包裹要过度的元素,并配置name属性:

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

 第一种:封装动画

<template>
  <div>
    <button @click="isShow = !isShow">点击显示/隐藏</button>
    <!-- appear设置后可以让他一上来就显示动画 -->
    <transition name="hela" appear>
      <h2 v-show="isShow">呼啦呼啦</h2>
    </transition>
  </div>
</template>

<script>
export default {
  name: "animationTest",
  data() {
    return {
      isShow: true,
    };
  },
  methods: {},
};
</script>

<style>
h2 {
  background-color: skyblue;
  height: 50px;
}
.hela-enter-active {
  animation: tom linear 2s;
}
.hela-leave-active {
  animation: tom linear 2s reverse;
}
@keyframes tom {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0px);
  }
}
</style>

第二种:用过渡

<template>
  <div>
    <button @click="isShow = !isShow">点击显示/隐藏</button>
    <!-- appear设置后可以让他一上来就显示动画 -->
    <transition-group name="hela" appear>
      <h2 v-show="!isShow" key="1">利威尔兵长</h2>
      <h2 v-show="isShow" key="2">艾伦耶格尔</h2>
    </transition-group>
  </div>
</template>

<script>
export default {
  name: "animationTest2",
  data() {
    return {
      isShow: true,
    };
  },
  methods: {},
};
</script>

<style>
h2 {
  background-color: skyblue;
  height: 50px;
}
/* 进来的起点,离开的终点 */
.hela-enter,
.hela-leave {
  transform: translateX(-100%);
}
/* 进来的终点,离开的起点  */
.hela-enter-to,
.hela-leave {
  transform: translateX(0px);
}
.hela-leave-active,
.hela-enter-active {
  transition: 2s, linear;
}
</style>

4.Vue中的ajax

4.1vue脚手架配置代理

方法一

在vue.config.js中添加如下配置:

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

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。

  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理(如果public文件夹里面有的话,优先提供public里面的数据,这样就走不了代理)。

  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则:

module.exports = {
    devServer: {
      proxy: {
      '/api1': {// 匹配所有以 '/api1'开头的请求路径
        target: 'http://localhost:5000',// 代理目标的基础路径
        changeOrigin: true,
        ws: true,//用于支持websocket
        pathRewrite: {'^/api1': ''}//将路径带api1的都变成'',再加后面字符串
      },
      '/api2': {// 匹配所有以 '/api2'开头的请求路径
        target: 'http://localhost:5001',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api2': ''}
      }
    }
  }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。

  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

vue.config.js

module.exports = {
    pages: {
      index: {
        // page 的入口
        entry: 'src/main.js'
       
      }
  
    },
    lintOnSave:false,//关闭语法检查
    // 服务器代理(方式一)
    /* devServer: {
      proxy: 'http://localhost:5000'
    } */

    // 服务器代理(方式二)
    devServer: {
      proxy: {
        '/api': {//匹配所有以 '/api'开头的请求路径
          target: 'http://localhost:5000',
          pathRewrite:{'^/api':''},//将路径带api的都变成'',再加后面字符串
          ws: true,//用于支持websocket
          changeOrigin: true//当为true时,告诉请求服务器host为5000,如果为false,则告诉请求服务器host为8080
        },
        '/demo': {//匹配所有以 '/demo'开头的请求路径
          target: 'http://localhost:5001',
          pathRewrite:{'^/demo':''},
          ws: true,//用于支持websocket
          changeOrigin: true//当为true时,告诉请求服务器host为5000,如果为false,则告诉请求服务器host为8080
        },
        
      }
    }
}

app.vue

<template>
  <div>
    <button @click="getInfo">获取学生信息</button>
    <button @click="getInfo2">获取汽车信息</button>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  components: {},
  data() {
    return {};
  },
  methods: {
    getInfo() {
      axios.get("http://localhost:8080/api/students").then(
        (response) => {
          console.log("请求成功", response.data);
        },
        (error) => {
          console.log("请求失败", error.message);
        }
      );
    },
    getInfo2() {
      //这儿get的是代理服务器,然后代理服务器再向后端服务器发送请求
      axios.get("http://localhost:8080/demo/cars").then(
        (response) => {
          console.log("请求成功", response.data);
        },
        (error) => {
          console.log("请求失败", error.message);
        }
      );
    },
  },
};
</script>

4.2vue-resource(了解)

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

使用:在main.js中引用,vue.use(vueResource),在需要发送请求的地方,以前用的是axios.get,现在需要this.$http.get就可以使用了。

vue项目常用的两个Ajax库:

  1. axios:通用的Ajax请求库,官方推荐,效率高
  2. vue-resource:vue插件库,vue 1.x使用广泛,官方已不维护

main.js

// 引入vue
import Vue from 'vue'
// 引入APP组件,它是所有组件的父组件
import App from './App.vue'
import vueResource from "vue-resource";
// 关闭vue生产提示
Vue.config.productionTip = false
Vue.use(vueResource)
// 创建vue实例
new Vue({
  render: h => h(App),
}).$mount('#app')

app.vue

<template>
  <div>
    <button @click="getInfo">获取学生信息</button>
    <button @click="getInfo2">获取汽车信息</button>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  data() {
    return {};
  },
  methods: {
    getInfo() {
      this.$http.get("http://localhost:8080/api/students").then(
        (response) => {
          console.log("请求成功", response.data);
        },
        (error) => {
          console.log("请求失败", error.message);
        }
      );
    },
    getInfo2() {
      //这儿get的是代理服务器,然后代理服务器再向后端服务器发送请求
      this.$http.get("http://localhost:8080/demo/cars").then(
        (response) => {
          console.log("请求成功", response.data);
        },
        (error) => {
          console.log("请求失败", error.message);
        }
      );
    },
  },
};
</script>

4.3插槽

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

  2. 分类:默认插槽、具名插槽、作用域插槽

  3. 使用方式:

    1. 默认插槽:

      父组件中:
              <Category>
                 <div>html结构1</div>
              </Category>
      子组件中:
              <template>
                  <div>
                     <!-- 定义插槽 -->
                     <slot>插槽默认内容...</slot>
                  </div>
              </template>
    2. 具名插槽:

      父组件中:
              <Category>
                  <template slot="center">
                    <div>html结构1</div>
                  </template>
      ​
                  <template v-slot:footer>//相当于slot='footer'并且必须配合template标签
                     <div>html结构2</div>
                  </template>
              </Category>
      子组件中:
              <template>
                  <div>
                     <!-- 定义插槽 -->
                     <slot name="center">插槽默认内容...</slot>
                     <slot name="footer">插槽默认内容...</slot>
                  </div>
              </template>
    3. 作用域插槽:

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

      2. 具体编码:

        父组件中:
                <Category>
                    <template scope="scopeData">//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',
                        props:['title'],
                        //数据在子组件自身
                        data() {
                            return {
                                games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                            }
                        },
                    }
                </script>

 默认插槽用法:

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="(data, index) in games" :key="index">{{ data }}</li>
      </ul>
    </category>
    <category title="电影">
      <video
        src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
        controls
      ></video>
    </category>
  </div>
</template>

<script>
import category from "./components/category.vue";
export default {
  name: "App",
  components: { category },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾", "牛排"],
      games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
      films: [
        "《海贼王》",
        "《进击的巨人》",
        "《拆弹专家》",
        "《你好!李焕英》",
      ],
    };
  },
};
</script>
<style>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
</style>

category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
    <slot>如果没有添加的内容,我就会显示</slot>
  </div>
</template>

<script>
export default {
  name: "cateGory",
  props: ["title"],
  data() {
    return {};
  },
};
</script>

<style>
.category {
  width: 200px;
  height: 300px;
  background-color: skyblue;
}
h3 {
  background-color: orange;
  text-align: center;
}
</style>

具名插槽(有名字)

app.vue

<template>
  <div class="container">
    <category title="美食">
      <img
        src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg"
        alt=""
        slot="slot2"
      />
      <h4 slot="slot1">海贼·王路飞</h4>
    </category>
    <category title="游戏">
      <template v-slot:slot2>
        <ul>
          <li v-for="(data, index) in games" :key="index">{{ data }}</li>
        </ul>
      </template>
    </category>
    <category title="电影">
      <!-- v-slot:slot1必须配合template标签 -->
      <template v-slot:slot1>
        <video
          src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
          controls
        ></video>
      </template>
    </category>
  </div>
</template>

<script>
import category from "./components/category.vue";
export default {
  name: "App",
  components: { category },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾", "牛排"],
      games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
      films: [
        "《海贼王》",
        "《进击的巨人》",
        "《拆弹专家》",
        "《你好!李焕英》",
      ],
    };
  },
};
</script>
<style>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
</style>

category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
    <slot name="slot1">如果没有添加的内容,我就会显示....</slot>
    <slot name="slot2">如果没有添加的内容,我就会显示????</slot>
  </div>
</template>

<script>
export default {
  name: "cateGory",
  props: ["title"],
  data() {
    return {};
  },
};
</script>

<style>
.category {
  width: 200px;
  height: 300px;
  background-color: skyblue;
}
h3 {
  background-color: orange;
  text-align: center;
}
</style>

作用域插槽

app.vue

<template>
  <div class="container">
    <category title="游戏">
      <!-- 作用域插槽中传过来的数据是一个对象,这儿用dataObj来接收 -->
      <template scope="dataObj">
        <ul>
          <li v-for="(data, index) in dataObj.games" :key="index">
            {{ data }}
          </li>
        </ul>
      </template>
    </category>
    <category title="游戏">
      <template slot-scope="dataObj">
        <ul>
          <li v-for="(data, index) in dataObj.games" :key="index">
            {{ data }}
          </li>
        </ul>
      </template>
    </category>
  </div>
</template>

<script>
import category from "./components/category.vue";
export default {
  name: "App",
  components: { category },
  data() {
    return {};
  },
};
</script>
<style>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
</style>

category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
    <slot :games="games">如果没有添加的内容,我就会显示....</slot>
  </div>
</template>

<script>
export default {
  name: "cateGory",
  props: ["title"],
  data() {
    return {
      games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
    };
  },
};
</script>

<style>
.category {
  width: 200px;
  height: 300px;
  background-color: skyblue;
}
h3 {
  background-color: orange;
  text-align: center;
}
</style>

5.vuex

1.概念

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

2.何时使用?

多个组件需要共享数据时

3.搭建vuex环境

下载vuexnpm i vuex

  1. 创建文件: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
    })
  2. main.js中创建vm时传入store配置项

    ......
    //引入store
    import store from './store'
    ......
    ​
    //创建vm
    new Vue({
        el:'#app',
        render: h => h(App),
        store
    })

4.基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件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,
    })
  2. 组件中读取vuex中的数据:$store.state.sum

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

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

5.getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。(类似vue中的computed)

  2. store.js中追加getters配置

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

不用vuex写的

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>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一会再加</button>
  </div>
</template>

<script>
export default {
  name: "countNum",
  data() {
    return {
      sum: 0, //求和
      n: 1, //选择的数
    };
  },
  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;
      }, 1000);
    },
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

用纯vuex写

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>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一会再加</button>
  </div>
</template>

<script>
export default {
  name: "countNum",
  data() {
    return {
      // sum: 0, //求和
      n: 1, //选择的数
    };
  },
  methods: {
    increment() {
      this.$store.dispatch("increment", this.n);
    },
    decrement() {
      //直接调用commit与mutation对话
      this.$store.commit('decrement',this.n)
    },
    incrementOdd() {
      if (this.$store.state.sum % 2) {
        this.increment()
      }
    },
    incrementWait() {
      setTimeout(() => {
        this.increment()
      }, 1000);
    },
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

main.js

// 引入vue
import Vue from 'vue'
// 引入APP组件,它是所有组件的父组件
import App from './App.vue'
import vueResource from "vue-resource";
import store from './store/index'
// 关闭vue生产提示
Vue.config.productionTip = false
Vue.use(vueResource)
// 创建vue实例
new Vue({
  render: h => h(App),
  store,
  beforeCreate(){
    Vue.prototype.$bus = this
  }
}).$mount('#app')

store/index.js

import Vue from "vue"
import Vuex  from 'vuex'
//使用vuex插件
Vue.use(Vuex)
// 创建actions--用于响应组件中的行为
const actions = {
    increment(context,value){
        console.log('action被调用');
        context.commit('increment',value)
    },
    decrement(context,value){
        console.log('action中decrement被调动');
        context.commit('decrement',value)
    }
    
}
//创建mutations--用于操作数据(state)
const mutations = {
    increment(state,value){
        console.log('mutations中jia被调用');
        state.sum += value
    },
    decrement(state,value){
        console.log('mutations中decrement被调动');
        state.sum -= value
    }
}
//创建state--用于存储数据
const state = {
    sum:0
}
const getters = {
    BigSum(state){
        return state.sum * 10
    }
}
//创建并暴露store
export default new Vuex.Store({
    actions,mutations,state,getters
})

6.四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

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

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    ​
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

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

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

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

count.vue

<template>
  <div>
    <h1>当前求和为{{ sum }}</h1>
    <h3>当前求和的10倍为{{ BigSum }}</h3>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一会再加</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
  name: "countNum",
  data() {
    return {
      // sum: 0, //求和
      n: 1, //选择的数
    };
  },
  computed: {
    //mapState对象写法,k是计算属性的名字,value是在store中state的数据
    // ...mapState({ sum: "sum" }),
    //mapState数组写法,当计算属性的名称与state中数据名字一样时
    ...mapState(["sum"]),

    // mapGetters对象写法,k是计算属性的名字,value是在store中Getters的数据
    // ...mapGetters({ BigSum: "BigSum" }),
    // mapGetters数组写法,当计算属性的名称与Getters中数据名字一样时
    ...mapGetters(["BigSum"]),
  },
  methods: {
    /* increment() {
      this.$store.dispatch("increment", this.n);
    }, */
    // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
    ...mapActions({ increment: "increment" }),
    /* decrement() {
      //直接调用commit与mutation对话
      this.$store.commit("decrement", this.n);
    }, */
    // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)

    ...mapMutations({ decrement: "decrement" }),
    /* incrementOdd() {
      this.$store.dispatch("incrementOdd", this.n);
    }, */
    // 数组写法
    ...mapActions(['incrementOdd']),

    /* incrementWait() {
      this.$store.dispatch("incrementWait", this.n);
    }, */
    ...mapActions(['incrementWait'])

  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

 index.js

//该文件用于创建vuex中核心--store
import Vue from "vue"
import Vuex  from 'vuex'
//使用vuex插件
Vue.use(Vuex)
// 创建actions--用于响应组件中的行为
const actions = {
    increment(context,value){
        console.log('action被调用');
        context.commit('increment',value)
    },
    decrement(context,value){
        console.log('action中decrement被调动');
        context.commit('decrement',value)
    },
    incrementOdd(context,value){
        if(context.state.sum % 2){
            context.commit('increment',value)
        }
    },
    incrementWait(context,value){
        setTimeout(()=>{
            context.commit('increment',value)
        },500)
    }
    
}
//创建mutations--用于操作数据(state)
const mutations = {
    increment(state,value){
        console.log('mutations中jia被调用');
        state.sum += value
    },
    decrement(state,value){
        console.log('mutations中decrement被调动');
        state.sum -= value
    }
}
//创建state--用于存储数据
const state = {
    sum:0
}
const getters = {
    BigSum(state){
        return state.sum * 10
    }
}
//创建并暴露store
export default new Vuex.Store({
    actions,mutations,state,getters
})

7.模块化+命名空间

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

  2. 修改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
      }
    })
  3. 开启命名空间后,组件中读取state数据:

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

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

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

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

count.vue

<template>
  <div>
    <h1>当前求和为{{ sum }}</h1>
    <h3>当前求和的10倍为{{ BigSum }}</h3>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一会再加</button>
  </div>
</template>

<script>
// import { nanoid } from "nanoid";
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
  name: "countNum",
  data() {
    return {
      // sum: 0, //求和
      n: 1, //选择的数
    };
  },
  computed: {
    //mapState对象写法,k是计算属性的名字,value是在store中state的数据
    // ...mapState({ sum: "sum" }),
    //mapState数组写法,当计算属性的名称与state中数据名字一样时
    ...mapState("countAbout", ["sum"]),

    // mapGetters对象写法,k是计算属性的名字,value是在store中Getters的数据
    // ...mapGetters({ BigSum: "BigSum" }),
    // mapGetters数组写法,当计算属性的名称与Getters中数据名字一样时
    ...mapGetters("countAbout", ["BigSum"]),
  },
  methods: {
    /* increment() {
      this.$store.dispatch("increment", this.n);
    }, */
    // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
    ...mapActions("countAbout", { increment: "increment" }),
    /* decrement() {
      //直接调用commit与mutation对话
      this.$store.commit("decrement", this.n);
    }, */
    // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)

    ...mapMutations("countAbout", { decrement: "decrement" }),
    /* incrementOdd() {
      this.$store.dispatch("incrementOdd", this.n);
    }, */
    // 数组写法
    ...mapActions('countAbout',["incrementOdd"]),

    /* incrementWait() {
      this.$store.dispatch("incrementWait", this.n);
    }, */
    ...mapActions('countAbout',["incrementWait"]),
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

 person.vue

<template>
  <div>
    <h1>人员列表</h1>
    <h4>Count组件的总和是{{ sum }}</h4>
    <h4>随机的名字是{{ personList[0].name }}</h4>
    <input type="text" placeholder="请输入名字" v-model="name" />
    <button @click="addPerson">添加</button>
    <button @click="addPersonServe">随机添加一个名字</button>
    <ul>
      <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
  data() {
    return {
      name: "",
    };
  },
  computed: {
    ...mapState("personAbout", ["personList"]),
    ...mapState("countAbout", ["sum"]),
  },
  methods: {
    addPerson() {
      const person = { id: nanoid(), name: this.name };
      console.log(person);
      this.$store.commit("personAbout/addPerson", person);
      this.name = "";
    },
    addPersonServe() {
      this.$store.dispatch("personAbout/addPersonServe");
    },
  },
  /* mounted(){
    console.log(this.$store);
  } */
};
</script>

<style>
</style>

index.js

//该文件用于创建vuex中核心--store

import Vue from "vue"
import Vuex  from 'vuex'
import countAbout  from './countAbout'
import personAbout  from './personAbout'
//使用vuex插件
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
    //引入的模块写在配置项modules中
    modules:{
        countAbout,personAbout
    }
})

countAbout.vue

export default {
    //开启命名空间,默认是false,开启后就可以访问
    namespaced:true,
    actions:{
        increment(context,value){
            console.log('action被调用');
            context.commit('increment',value)
        },
        decrement(context,value){
            console.log('action中decrement被调动');
            context.commit('decrement',value)
        },
        incrementOdd(context,value){
            if(context.state.sum % 2){
                context.commit('increment',value)
            }
        },
        incrementWait(context,value){
            setTimeout(()=>{
                context.commit('increment',value)
            },500)
        }
    },
    mutations:{
        increment(state,value){
            console.log('mutations中jia被调用');
            state.sum += value
        },
        decrement(state,value){
            console.log('mutations中decrement被调动');
            state.sum -= value
        },
    },
    state:{
        sum:0,
    },
    getters:{
        BigSum(state){
            return state.sum * 10
        }
    }
}

personAbout.vue

import { nanoid } from "nanoid"
import axios from "axios"
export default {
    //开启命名空间,默认是false,开启后就可以访问
    namespaced:true,
    actions:{
        addPersonServe(context){
            console.log(context);
            axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
                response =>{
                    context.commit('addPerson',{name:response.data,id:nanoid()})
                },
                erro =>{
                    console.log(erro.message);
                }
            )
        }
    },
    mutations:{
        addPerson(state,value){
            state.personList.unshift(value)
        },
    },
    state:{
        personList:[{name:'张三',id:nanoid()}]

    },
    getters:{
        
    }
}

6.路由

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。

  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router@3

  2. 应用插件:Vue.use(VueRouter)

  3. 编写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
  4. 实现切换(active-class可配置高亮样式)

    <router-link active-class="active" to="/about">About</router-link>
  5. 指定展示位置

    <router-view></router-view>

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。

  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。

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

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

 app.vue

<template>
  <div class="container">
    <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">
            <!-- //原始用a链接跳转
            <a class="list-group-item active" href="./about.html">About</a>
            <a class="list-group-item" href="./home.html">Home</a> -->
            <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>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {};
  },
  /*   mounted() {
    console.log("app", this);
  }, */
};
</script>

home.vue和about.vue差不多

<template>
  <div>
    <h2>我是home的内容</h2>
  </div>
</template>

<script>
export default {
    name:'aboutVue'
}
</script>

<style>

</style>

main.js

/* 
  该文件是整个项目的入口文件
 */

// 引入vue
import Vue from 'vue'
// 引入APP组件,它是所有组件的父组件
import App from './App.vue'
//引入Vuerouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router/index'
// import store from './store/index'
// 关闭vue生产提示
Vue.config.productionTip = false
//使用VueRouter
Vue.use(VueRouter)
// 创建vue实例
new Vue({
  render: h => h(App),
  router,
}).$mount('#app')

router/index.js

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import about from '../components/about.vue'
import home from '../components/home.vue'
//创建一个路由器
export default new VueRouter({
    routes:[{
        path:'/about',
        component:about
    },{
        path:'/home',
        component:home
    }]
})

3.嵌套路由(多级路由)

  1. 配置路由规则,使用children配置项:

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

    <router-link to="/home/news">News</router-link>

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>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "aboutVue",
};
</script>

<style>
</style>

 router/index.js

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import about from '../pages/about.vue'
import home from '../pages/home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'
//创建一个路由器
export default new VueRouter({
    routes:[{
        path:'/about',
        component:about
    },{
        path:'/home',
        component:home,
        children:[{
            path:'News',
            component:News
        },{
            path:'Message',
            component:Message
        }]
    }]
})

4.路由的query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    <!--用模板字符串解析-->
    <!-- <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:666,
                title:'你好'
            }
        }"
    >跳转</router-link>
  2. 接收参数:

    $route.query.id
    $route.query.title

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>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: "MessageVue",
  data() {
    return {
      messageList: [
        {
          id: "001",
          title: "消息001",
        },
        {
          id: "002",
          title: "消息002",
        },
        {
          id: "003",
          title: "消息003",
        },
      ],
    };
  },
};
</script>

 detail.vue

<template>
  <ul>
    <li>消息编号{{$route.query.id}}</li>
    <li>消息名称{{$route.query.title}}</li>
  </ul>
</template>

<script>
export default {

}
</script>

<style>

</style>

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      {
          path:'/demo',
          component:Demo,
          children:[
              {
                  path:'test',
                  component:Test,
                  children:[
                      {
                            name:'hello' //给路由命名
                          path:'welcome',
                          component:Hello,
                      }
                  ]
              }
          ]
      }
    2. 简化跳转:

      <!--简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>
      ​
      <!--简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>
      ​
      <!--简化写法配合传递参数 -->
      <router-link 
          :to="{
              name:'hello',
              query:{
                 id:666,
                  title:'你好'
              }
          }"
      >跳转</router-link>

6.路由的params参数

  1. 配置路由,声明接收params参数

    {
        path:'/home',
        component:Home,
        children:[
            {
                path:'news',
                component:News
            },
            {
                component:Message,
                children:[
                    {
                        name:'xiangqing',
                        path:'detail/:id/:title', //使用占位符声明接收params参数
                        component:Detail
                    }
                ]
            }
        ]
    }
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    <router-link :to="`/home/Message/detail/${m.id}/${m.title}`">{{ m.title}}</router-link>             
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
        :to="{
            name:'xiangqing',
            params:{
               id:666,
                title:'你好'
            }
        }"
    >跳转</router-link>

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    $route.params.id
    $route.params.title

message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- 跳转并携带params参数,to的字符串写法 -->
        <router-link :to="`/home/Message/detail/${m.id}/${m.title}`">{{
          m.title
        }}</router-link
        >&nbsp;&nbsp;
        <!-- 跳转并携带params参数,to的对象写法 -->
        <!--  <router-link
          :to="{
            name: 'detail',
            path: '/home/Message/detail',
            params: {
              id: m.id,
              title: m.title,
            },
          }"
          >{{ m.title }}</router-link
        >&nbsp;&nbsp; -->
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "MessageVue",
  data() {
    return {
      messageList: [
        {
          id: "001",
          title: "消息001",
        },
        {
          id: "002",
          title: "消息002",
        },
        {
          id: "003",
          title: "消息003",
        },
      ],
    };
  },
};
</script>

 detail.vue

<template>
  <ul>
    <li>消息编号{{$route.params.id}}</li>
    <li>消息名称{{$route.params.title}}</li>
  </ul>
</template>

<script>
export default {

}
</script>

<style>

</style>

router/index.js 

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import about from '../pages/about.vue'
import home from '../pages/home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'
import detail from '../pages/detail.vue'
//创建一个路由器
export default new VueRouter({
    routes:[{
        path:'/about',
        component:about
    },{
        path:'/home',
        component:home,
        children:[{
            path:'News',
            component:News
        },{
            
            path:'Message',
            component:Message,
            children:[{
                // name:'detail',
                path:'detail/:id/:title',
                component:detail
            }]
        }]
    }]
})

7.路由的props配置

作用:让路由组件更方便的收到参数

{
    name:'xiangqing',
    path:'detail/:id',
    component:Detail,
​
    //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    // props:{a:900}
​
    //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
    // props:true
    
    //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
    props(route){
        return {
            id:route.query.id,
            title:route.query.title
        }
    }
}

router/index.js

import VueRouter from 'vue-router'
import about from '../pages/about.vue'
import home from '../pages/home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'
import detail from '../pages/detail.vue'
//创建一个路由器
export default new VueRouter({
    routes:[{
        path:'/about',
        component:about
    },{
        path:'/home',
        component:home,
        children:[{
            path:'News',
            component:News
        },{
            
            path:'Message',
            component:Message,
            children:[{
                // name:'detail',
                path:'detail',
                component:detail,

                /* //props的第一种写法,不推荐,值是对象,该对象中所有key-value都会以props形式传给detail组件
                props:{
                    id:'001',
                    title:'hello'
                } */
                /* //props的第二种写法,值为布尔值,若值为真,就会把该组件接收到的所有params参数,以pros形式传给detail组件
                props:true */
                //props的第三种写法,值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
                props($route){
                    return {id:$route.query.id,title:$route.query.title}
                }
            }]
        }]
    }]
})

 detail.vue

<template>
  <ul>
    <li>消息编号{{ id }}</li>
    <li>消息名称{{ title }}</li>
  </ul>
</template>

<script>
export default {
  props:['id','title'],
  mounted(){
    console.log(this.id,this.title);
  },
  updated() {
    console.log(this.id,this.title);
    
  },
};
</script>

<style>
</style>

message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- 跳转并携带params参数,to的字符串写法 -->
        <router-link :to="`/home/Message/detail?id=${m.id}&title=${m.title}`">{{
          m.title
        }}</router-link
        >&nbsp;&nbsp;
        <!-- 跳转并携带params参数,to的对象写法 -->
        <!--  <router-link
          :to="{
            name: 'detail',
            path: '/home/Message/detail',
            params: {
              id: m.id,
              title: m.title,
            },
          }"
          >{{ m.title }}</router-link
        >&nbsp;&nbsp; -->
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "MessageVue",
  data() {
    return {
      messageList: [
        {
          id: "001",
          title: "消息001",
        },
        {
          id: "002",
          title: "消息002",
        },
        {
          id: "003",
          title: "消息003",
        },
      ],
    };
  },
};
</script>

8.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式

  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
        name:'xiangqing',
            params:{
                id:xxx,
                title:xxx
            }
    })
    ​
    this.$router.replace({
        name:'xiangqing',
            params:{
                id:xxx,
                title:xxx
            }
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退,里面的参数是正的时候就是前进的步数,负就是后退的

10.缓存路由组件

  • 11
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值