vue2学习笔记

文章目录

vue的基本指令

一、框架

framework

是一个半成品,是一套开发规范(封装了js、css、html),作为框架的使用者,需要按照规范填写相应的参数,实现相应的需求

1、例如:bootstrap框架,只需要使用改变html标签的type和class属性即可实现不同样式

2、作用:提高开发效率

二、vue框架

1、是一个”渐进式“的,用来构建用户界面的前端框架

渐进式:vue框架的体系庞大,使用者不需要一次性使用所有功能,可以根据实际的需求,逐层逐次使用某些模块

2、作者:尤雨溪团队开发

3、框架的本质:html、css、js的封装体

4、vue的版本:vue2基于js、vue3基于ts

三、库和框架的区别

1、库:对部分功能的封装,不是半成品,可以简化语法,但是不能快速构建成品

2、jquery库:简化css和js操作

四、vue的基本应用
1、使用方式

(1)传统方式:script标签引入vue.js文件或cdn加速器

    <script src="../js/vue.js"></script>//引入 

	<div id="app">
        <p>{{info}}</p>
    </div>
    <script>
        let vm = new Vue({//创建vue实例
            el: '#app',//el:element,将vue实例与id为app的dom元素绑定到一起
            data: {
                info: '蜗牛学苑'
            }
        })
    </script>

(2)使用vue的脚手架:vue-cli

2、vue的基本指令:通常以v-开头

(1){{变量或表达式}}:称为mustache语法(插值表达式),可以进行运算

(2)v-text:文本显示指令。直接将文本显示在标签中—封装的是标签的InnerText属性(不能识别文本中的标签)插入文本

(3)v-html:文本显示指令。直接将文本显示在标签中—封装的是标签的InnerHTML属性(能识别文本中的标签)插入字符串

强调

A.“{{}}”,v-text,v-html的区别:

B.单向的数据绑定,将vue实例中的数据绑定到dom元素上;“{{}}”,v-text,v-html称为vue的单向数据绑定指令。

(4)v-on:事件绑定指令。首先通过v-on指令给元素绑定事件,之后在vue实例中添加methods属性(对象),在该属性下定义事件处理程序

 <button v-on:click="事件处理程序">按钮</button>
 
  methods: {//方法
                事件处理程序() {
                    alert('111')
                }
            }

原生js中事件绑定方法:

①在元素的属性中绑定

<button onclick = "函数名"></button>

②js中获取元素,元素名.οnclick=function (){}

③事件监听者,元素名.addeventListener(“事件名”,函数)

强调:v-on指令的语法糖—>@事件=”事件处理程序“

<button @click="事件处理程序">按钮</button>

(5)v-show:控制dom元素的显示与隐藏

首先在vue实例的data属性中定义flag,当flag为true时显示,flag为false时隐藏

        <div id="test">
        <p>{{info}}</p>
        <p v-show="flag">大唐不夜城</p>
        <img src="./images/1685081486193.4197.jpg" v-show="flag">
        <div>
            <!-- <button @click="fn">{{flag?"隐藏":"显示"}}</button> -->
            <button @click="fn" v-html="html"></button>
        </div>
    </div>


    <script>
        let name = new Vue({
            el: '#test',
            data: {
                info: "蜗牛学苑",
                flag: true,
                html: "隐藏"
            },
            methods: {//方法
                fn() {
                    this.flag ? this.flag = false : this.flag = true
                    // if (this.flag) {
                    //     this.flag = false
                    // } else {
                    //     this.flag = true
                    // }
                    this.html = this.flag ? "显示" : "隐藏"
                }
            }
        })
    </script>

(6)v-if、v-else、v-if-elseif:根据条件判断结果决定dom元素是否显示

​ v-if:逻辑不放在data中,data中只执行一次

**v-if与v-show的区别:**切换次数多v-show,切换次数少v-if

①实现方式不同:

​ v-show通过控制元素的display属性来实现显示与隐藏,一直存在

​ v-if通过调用dom的appendChild来实现显示,通过调用dom的removeChild来实现隐藏

②切换开销不同:

​ v-show开销小

​ v-if开销大

③加载性能不同:

​ v-if加载效率高

​ v-show加载效率低

(7)v-for:循环指令。对应forin循环

A.循环指令

① v-for=“item in arr”:遍历简单数组

 		<ul>
            <li v-for="item in arr">{{item}}</li>
        </ul>

② v-for=“(item,index) in arr”:index表示item在arr中的下标(索引值)

③ v-for=“item in obj”:obj是对象,item是对象的属性值

④ v-for=“(value,key) in obj”:key表示对象的属性名,value表示对象的属性值

⑤v-for=“(value,key,index) in obj”:key表示对象的属性名,value表示对象的属性值,index表示对象属性的索引

B.v-for循环嵌套(对象数组)

		<ul>
            <li v-for="(item,index) in objs">
                {{index}}: {{item}}
                <ul>
                    <li v-for="(value,key) in item">
                        {{key}}: {{value}}
                    </li>
                </ul>
            </li>
        </ul>

C.遍历数字:从1开始

	//九九乘法表	
		<p v-for="x in 9">
            <span v-for="y in x">
                {{x}}*{{y}}={{x*y}}
            </span>
        </p>

(8)v-model:双向绑定指令

①什么是双向绑定:vue实例《—》dom元素

②实现双向绑定的dom元素

  • input
  • select
  • textarea

③实现方式

<div id="app">
        <input type="text" v-model:value="msg">
        <p>{{msg}}</p>
        <button @click="jump">跳转</button>
        <a v-model:href="msg">点击</a>
        <!-- <a href="http://www.baidu.com">点击</a> -->
    </div>
    <script src="../js/vue.js"></script>
    <script>
        let vm = new Vue({
            el: '#app',
            data: {
                msg: "放假啦"
            },
            methods: {
                jump() {
                    location.href = this.msg
                }
            }
        })
    </script>
3、vue的执行过程:双向

(1)在页面中创建dom容器

(2)创建vue实例:通过el属性和dom容器进行绑定

​ ①el:指定绑定dom元素

​ ②data:变量(number、string、object、array、boolean)

​ ③methods:方法(函数)

五、vue脚手架的使用
1.安装

(1)安装node:查看node版本node -v

(2)安装vue脚手架:npm install -g @vue/cli

​ 查看版本号:vue --version

强调vue-cli3.0以前的版本卸载:npm uninstall -g vue-cli

node的包管理器:npm、yarn

2.创建vue2.0的项目

vue create 项目名(不要有大写字母,不要以数字开头,上级目录不要有中文)

down选择vue2 回车 【babel将es6语法转为es5语法,eslint检查语法】

3.进入项目所在文件夹

cd 项目名

4.运行项目

npm run serve

5.使用vscode打开项目

(1)node_modules文件夹:存放支持vue项目运行的基本模块

(2)public目录:index.html是vue项目的默认首页

(3)src目录

​ assets目录:用于存放项目的静态资源(图像、css、js)文件夹

​ components目录:用于存放vu组件(后缀为.vue)

​ app.vue:vue项目的根组件(项目运行时首先运行的)

​ main.js:vue项目的入口文件

(4)package.json文件:项目的配置文件

​ package-lock.json文件:配置文件的详细版

执行过程:在main.js中创建vue实例(new Vue())—渲染App.vue—将vue实例绑定到public的index.html中id为app的div

六、vue组件的使用
1.vue组件:是一个独立的功能模块,可以重复使用

在组件中封装了template(Dom部分)、script、style

(1)template(Dom部分),必须的。用来显示内容

  • 它是vue组件必须的组成部分
  • 在template下必须有且只能有一个根标签

(2)script:组建的js部分,可选的,可以实现动态的数据处理

在vue组件中,data必须是函数,该函数返回值是一个对象
  • 封装vue组件的目的是为复用
  • 在vue组件复用过程中为了避免出现数据污染,组件的data必须定义成函数

(3)style:组建的css部分,可选的。用于美化dom部分的内容

2.常用指令

(1)v-on:时间的绑定指令。语法糖@

(2)v-for:循环渲染指令

(3)v-if:条件渲染指令

(4)v-show:

(5){{}}:mustache语法

(6)v-model:双向数据绑定

①双线数据绑定:vue实例<—>dom元素

②可以使用v-model的dom元素:input、select、textarea将这些dom元素的value,绑定到v-model所指定的变量上

(7)v-bind:vue的动态绑定指令

①作用:用于将dom元素的属性绑定到给定的变量上

②用法:

 <img v-bind:src="imgUrl">

③语法糖

  • 使用v-bind绑定元素的class属性

①对象语法:给元素的class属性绑定一个对象

//DOM部分

<p :class="myStyle">123456 </p>
<p><button @click="style">样式切换</button></p>
//js部分
data(){
    return{
      myStyle:{
        class1:true,
        class2:false,
      }
    }

    
     style(){
      // if(this.myStyle.class1 ==true || this.myStyle.class2 ==false){
      //   this.myStyle.class1 = false;
      //   this.myStyle.class2 = true;
      // }else{
      //   this.myStyle.class1 = true;
      //   this.myStyle.class2 = false;
      
        
      // }
      this.myStyle.class1 = !this.myStyle.class1
      this.myStyle.class2 = !this.myStyle.class2
    }
   

//css部分
.class1{
  background-color: skyblue;
  color: #333;
}
.class2{
  background-color: chartreuse;
  color: #333;
}

②数组语法:给class属性绑定一个数组

//DOM部分
<p :class="[s1,s2]">1111111111111111111</p>

//js部分
 data(){
    return{
      
      s1:'style1',
      s2:'style2',
    }
  },


//css部分
 .style1{
  background-color: aqua;
}
.style2{
  color: #000;
}     
      
  • style属性的动态绑定

①对象语法:给DOM元素的style属性(内嵌的css样式)绑定一个对象

//DOM部分
<div :style="{width:myWidth,height:myHeight,background:myColor}">
      <span>大唐芙蓉园</span>
 </div>

//JS部分
data(){
    return {
      myWidth:'200px',
      myHeight:'200px',
      myColor:'red'
    }
  }

②数组语法:给DOM元素的style属性(内嵌的css样式)绑定一个数组。

//DOM部分
<div :style="[textStyle,textSize]">
      <span>大雁塔</span>
 </div>
//JS部分
data(){
    return {
      textStyle:{
        color: 'blue'
      },
      textSize: {
        fontSize: '35px'
      }
    }
  }
七、自定义vue组件
1、项目的compoents文件夹:用来存放项目中公共的组件
2、创建保存自定义组件的文件夹:src / views
3、在src/views/FirstComponent.vue组件
4、在App.vue中引用自定义的组件:

​ (1)引入自定义组件

import FirstComponent from './views/FirstComponent.vue'

​ (2)在App.vue的JS中注册组件:在components属性下进行注册

export default {
    name: 'App',
    components: { FirstComponent },
}

​ (3)在App.vue的DOM中使用自定义组件:

<first-component></first-component>
6、对《i-旅行》项目的首页进行组件化处理

(1)创建页头组件:HeaderPage.vue

(2)创建导航组件:NavPage.vue

(3)创建轮播图组件:SwiperPage.vue

八、swiper插件的使用
1、swiper插件的使用:https://www.swiper.com.cn/cdn/index.html
2、安装swiper 5.4.5版本:npm install swiper@5.4.5
3、安装vue-awesome-swiper:npm install vue-awesome-swiper@4.1.1
4、在main.js文件中引入并注册swipe模块
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
Vue.use(VueAwesomeSwiper)
九、v-model的高级用法
1.作用

实现数据的双向绑定

2.实现原理

底层触发input事件获取input、textarea、select的value属性值。并将值赋给元素的innerHTML并显示出来。

3.可以进行数据双向绑定的dom元素:

input、textarea、select

本质:将v-model给定的变量与dom元素的value属性绑定在一起

4.具体实现
(1)单行文本框的绑定:绑定的是string变量
<input type="text" v-model="userName">
(2)单选按钮的绑定:绑定的是boolean值
<input type="radio" name="gender" v-model="gender" value="男"><input type="radio" name="gender" v-model="gender" value="女">
(3)复选按钮的绑定:绑定的是string数组
<input type="checkbox" name="hobby" v-model="hobby" value="爬山">爬山
<input type="checkbox" name="hobby" v-model="hobby" value="旅游">旅游
<input type="checkbox" name="hobby" v-model="hobby" value="学习">学习
<input type="checkbox" name="hobby" v-model="hobby" value="睡觉">睡觉
<input type="checkbox" name="hobby" v-model="hobby" value="敲代码">敲代码
(4)文本域的绑定:
<textarea cols="30" rows="10" v-model="info"></textarea>
(5)下拉列表的绑定:绑定的是string变量(单选)
<select v-model="edu">
                <option value="西交大">西交大</option>
                <option value="西工大">西工大</option>
                <option value="西电大">西电大</option>
            </select>
(6)下拉列表的绑定:绑定的是数组(多选)
<select v-model="edu" multiple>
     <option value="java">java</option>
     <option value="c++">c++</option>
     <option value="js">js</option>
</select>
5.v-model的修饰符
(1).lazy:懒加载修饰符
(2).number:转换为number
(3).trim:清除字符串的前后空格
十、vue的计算属性:computed
1.用法:

和data、methods(方法)、components(注册组件)一样,属于vue组建的属性。

 data() {
        return {
           
        }
    },
    methods:{},
    components:{},
    computed:{}
2.作用

(1)本质是属性,定义时以函数的形式定义。使用时不能像函数一样调用(不能使用()调用)

(2)属性在data中不定义

(3)通过computer定义的属性,其值是可以缓存的

computer和methods的区别?

①使用methods定义的函数,当某个属性值发生改变,所有的函数均执行一次(因为函数没有缓存)

②使用computed定义计算属性时,当某个属性值发生改变时,只有当前被改变的属性会重新执行,其他不改变的不会重新执行。(因为copmuted具有缓存功能)

为什么不把计算属性放入data中?

①data中的属性是在组件挂载时运行一次(即初始化),不能进行复杂的逻辑运算

②computed中的属性以函数的方式定义,可以接收参数,可以进行复杂的运算

3.应用场景

若组件中某个属性的值会受到其他属性的影响,则该属性建议用计算属性。例如购物车结算。

十一、vue的监听属性:watch
1.监听器

(1)是vue实例(组件)的属性

(2)用来监听v-model绑定的变量

2.用法
watch:{
	被监听的对象:{
		handler(newVal,oldVal){
		
		}
	}
}watch:{
   被监听的对象:function(newVal,oldVal){
       
   }
}

(1)被监听的对象:是在data中定义的变量

(2)handler是监听函数,函数名不变

(3)newVal:变量变化后的新值,oldVal是变量变化前的值

3.监听简单的数据类型
<template>
    <div>
        <label>
            年龄:<input type="text" v-model="age">
        </label>
        <br>
        <label>
            米:<input type="text" v-model="meters">
        </label>
        <br>
        <label>
            千米:<input type="text" v-model="kilometers">
        </label>
        <br>
        <!-- 7.12 -->
        <label>
            人民币:<input type="text" v-model="money">
        </label>
        <br>
        <label>
            美元:<input type="text" v-model="doler">
        </label>
        <br>
    </div>
</template>
<script>
export default {
    name: "WatchDemo",
    data() {
        return {
            age: "23",
            meters: "",
            kilometers: "",
            money: "",
            doler: ""
        }
    },
    watch: {
        age: {
            handler(newVal, oldVal) {
                console.log(newVal);
                console.log(oldVal);
                console.log("------------");
            }
        },
        meters: {
            handler(newVal) {
                this.kilometers = newVal / 1000
            }
        },
        kilometers: function (newVal) {
            this.meters = newVal * 1000
        },
        money: {
            handler(newVal) {
                this.doler = newVal / 7.15
            }
        },
        doler: function (newVal) {
            this.money = newVal * 7.15
        }
    }
}
</script>
4.监听复杂数据类型

person是在data中定义的对象,name是该对象的属性

监听时语法是:“对象名.属性名”,必须用引号包裹

5.启动立即监听:immediate

(1)默认false,不会立即被监听,发生变化时才被监听

(2)immediate的值为true时,表示vue项目启动时立即对相应的属性进行监听

		"person.name": {
            handler(newVal, oldVal) {
                console.log(newVal);
                console.log(oldVal);
                console.log("------------");
            },
            immediate: true
        },
6.深度监听:deep

(1)默认是false,默认不会监听对象的属性变化

(2)deep的值为true时,表示会监听对象的属性值的变化

		person: {
            handler(newVal, oldVal) {
                console.log("--------------", newVal);
                console.log("--------------", oldVal);
                console.log("------------");
            },
            deep: true
        }
7.应用场景

(1)本质是属性,监听的是data中定义的属性

(2)若某个属性值会影响其他属性则该属性就要被监听

综合案例:计算属性+监听属性的省市区三级联动
1.定义数据:json格式(键值对)
<template>
    <div>
        <!---->
        <select v-model="sheng">
            <option value="">请选择省份</option>
            <option v-for="(item, index) in province" :key="index" :value="item.province">
                {{ item.province }}
            </option>
        </select>

        <!---->
        <select v-model="shi" v-if="this.citys.length >= 1">
            <option disabled value="">请选择市</option>
            <option v-for="(item, index) in citys" :key="index" :value="item.name" :label="item.name"></option>
        </select>

        <!---->
        <select v-model="qu" v-if="this.areas.length >= 1">
            <option disabled value="">请选择区</option>
            <option v-for="(item, index) in areas" :key="index" :value="item.name" :label="item.name"></option>
        </select>
        <div><label>你当前选择的地址:</label>{{ fullAddress }}</div>
    </div>
</template>

<script>

import citys from '../assets/china.json'
export default {
    name: "ProvinceCity",
    data() {
        return {
            province: citys,//china.json中的数据
            citys: [],
            areas: [],

            sheng: "",
            shi: "",
            qu: ""
        }
    },
    watch: {
        sheng: function () {
            this.getCitys()
            this.getAreas()
        },
        shi: function () {
            this.getAreas()
        }
    },
    methods: {
        getCitys() {
            this.citys = []
            if (this.province.length > 0) {
                this.province.forEach(item => {
                    if (this.sheng === item.province) {
                        this.citys = item.citys;
                        this.shi = this.citys[0].name
                    }
                });
            }
        },
        getAreas() {
            this.areas = []
            if (this.citys.length > 0) {
                this.citys.forEach(item => {
                    console.log(item);
                    if (this.shi === item.name) {
                        this.areas = item.areas;
                        this.qu = this.areas[0].name
                    }
                });
            }
        }
    },
    computed: {
        fullAddress: function () {
            if (this.sheng === this.shi) {
                return this.sheng + "-" + this.qu;
            } else {
                return this.sheng + "-" + this.shi + "-" + this.qu;
            }
        },
    }

}
</script>
十二、vue的过滤器:filter
1.作用:文本格式化、日期格式化、数据格式化
2.定义方法:

(1)在vue组件中添加filters属性:和data、methods、computed、watch、components同级

(2)在filters属性下定义过滤器函数:

filters:{//
	formatPrice(){//过滤器函数
		格式化文本
	}
}
3、使用过滤器:需要采用管道符 ( | )—> 需要过滤的数据 | 过滤器函数名
需要过滤的数据| 过滤器函数名

<h1>总计价格是:{{ getTotalprice | formatPrice }}</h1>

将 getTotalprice的值作为参数传递给formatPrice函数
4、过滤器链:
需要过滤的数据 |  过滤器1 | 过滤器2 | ......"需要过滤的数据"传递给“过滤器1”然后将“ 过滤器1”的返回值作为参数传递给“过滤器2

课堂作业:将数字的15789.45转换成:壹萬伍仟柒佰捌拾玖圆肆角伍分

5、过滤器的参数
{{ getTotalprice | formatPrice(args1,args2) }}

getTotalprice 作为过滤器函数formatPrice的第一个参数

formatPrice(args1,args2):有三个参数,第一个参数是管道符前的"要过滤的数据"

filters:{
    formatPrice(v1,v2,v3){
        参数v1接收的是getTotalprice值,
        参数v2接收的是args1
        参数v3接收的是args2
    }
}
十三、vue事件的高级
1、使用v-on指令绑定多个事件:给v-on传递一个对象,对象的属性是事件名,属性值就是事件处理程序(函数名)
 <div class="on-test" v-on="{click:fun1,mouseover:fun2}">
            西安蜗牛学苑
 </div>
2、一个事件绑定多个函数:这些函数按先后顺序依次执行
 <button v-on:click="f1(),f2()">一个事件绑定多个函数</button>
3、vue事件的修饰符

(1)阻止默认事件的修饰符:.prevent

 <a href="http://www.baidu.com" v-on:click.prevent="fun">百度一下</a>

(2)阻止事件冒泡:.stop

<div class="outer" @click="f3">
            <div class="inner" @click="f2">
                <button @click.stop="f1">按钮</button>
            </div>
</div>
十四、vue中ref属性的应用:获取组件中原生的DOM元素
1、JS中获取原生DOM元素的方法:

(1)document.getElementById() :不存在浏览器兼容性的问题

(2)jQuery中的获取方法:$(‘#id’) ====>jQuery(‘#id’)

注:$是jQuery的全局对象(jQuery)的简写

2、vue中获取组件原生DOM元素的方式:

(1)给组件的DOM元素添加ref属性,属性值就是该DOM元素的别名

<p ref="refDiv">乾陵</p>
<button @click="change">变色</button>

(2)在组件的js中通过$refs来获取DOM元素:

this.$refs.ref属性值

例如:

change(){
            this.$refs.refDiv.style.color= 'red'
}

课堂练习:如图所示进行表单验证(当input失去焦点时进行验证)

image-20230609144534467

十五、vue组件的分类
1、全局组件:在项目中任何地方都可以引用的组件
2、全局组件的定义和声明方式:
第一种方式:

(1)自定义组件:LoadCom.vue

(2)在项目的main.js文件中引入自定义组件并注册成全局的

import LoadCom from './views/LoadCom.vue' //引入自定义组件

Vue.component('load-com',LoadCom) //将自定义组件注册成全局组件

在其他组件中使用'load-com'   --> <load-com></load-com>
第二种方式:

(1)自定义组件:LoadCom.vue

(2)创建一个.js文件:load.js

import LoadCom from "@/views/LoadCom.vue";

const Load = {
    install(Vue){
        Vue.component('load-com',LoadCom)
    }
}

export default Load

(3)在main.js文件中引入load.js

import Load from './assets/js/load'

Vue.use(Load)
3、局部组件:在组件的components属性下注册的组件。只能在当前的组件中使用不能在其他地方使用
注意:

1.修改vue项目的默认端口号:vue.config.js文件中

devServer:{
	port:8080//默认
}

2.如何关闭ESLint语法检查:vue.config.js文件中

lintOnSave:false
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    port: 8088
  }
})
十六、组件的切换
1、vi-if实现:是在创建组件和销毁组件之间进行切换(通过dom的appendchild和removechild)

(1)创建login.vue组件

(2)创建register.vue组件

(3)在app.js文件中,使用v-if指令实现组件切换

<template>
  <div id="ad">
    <a href="#" @click.prevent="flag = true">登录</a>
    <a href="#" @click.prevent="flag = false">注册</a>
    <Login v-if="flag"></Login>
    <Register v-else></Register>
  </div>
</template>

<script>
import Login from './views/Login.vue';
import Register from './views/Register.vue';

export default {
  name: 'App',
  components: {
    Login,
    Register
  },
  data() {
    return {
      flag: true
    }
  }
}
</script>
2、v-show实现:(通过css的display)

(1)创建login.vue组件

(2)创建register.vue组件

(3)在app.js文件中,使用v-show指令实现组件切换

<template>
  <div id="ad">
    <a href="#" @click.prevent="login">登录</a>
    &nbsp;&nbsp;&nbsp;&nbsp;
    <a href="#" @click.prevent="reg">注册</a>
    <hr>
    <Login v-show="login_flag"></Login>
    <Register v-show="reg_flag"></Register>
  </div>
</template>

<script>

import Login from './views/Login.vue';
import Register from './views/Register.vue';

export default {
  name: 'App',
  components: {
    Login,
    Register
  },
  data() {
    return {
      login_flag: false,
      reg_flag: false,
    }
  },
  methods: {
    login() {
      this.login_flag = true
      this.reg_flag = false
    },
    reg() {
      this.login_flag = false
      this.reg_flag = true
    }
  }
}
</script>

<style>
* {
  padding: 0;
  margin: 0;
}
</style>

3、动态组件切换:component is=“组件名称”

(1)在vue中提供了一个内置组件:可以看为一个占位符,底层实现方式与v-if相同

<component is="组件名称"/>

(2)具体使用:

<template>
  <div id="ad">
    <a href="#" @click.prevent="changelogin">登录</a>
    &nbsp;&nbsp;&nbsp;&nbsp;
    <a href="#" @click.prevent="changereg">注册</a>
    <hr>
    <component :is="nameCom"></component>
  </div>
</template>

<script>
import Login from './views/Login.vue';
import Register from './views/Register.vue';

export default {
  name: 'App',
  components: {
    Login,
    Register
  },
  data() {
    return {
      nameCom: ''
    }
  },
  methods: {
    changelogin() {
      this.nameCom = 'Login'
    },
    changereg() {
      this.nameCom = 'Register'
    }
  }
}
</script>

<style>
* {
  padding: 0;
  margin: 0;
}
</style>

十七、组件之间的通信
1、vue中组件的关系

(1)父子关系:嵌套关系

(2)兄弟关系:有共同父组件的组件之间的关系是兄弟关系

(3)跨级组件:组件与组件之间存在多层嵌套或上级组件不同时

2、组件之间的通信:即组件之间的参数传递

(1)父子关系:

​ ①父向子传值

​ ②子向父传值

(2)兄弟关系、跨级组件:vuex、事件总线(eventbus)、 p a r e n t 、 parent、 parentchildren

3、父组件向子组件传值

(1)父组件传值:

<子组件名 自定义属性名="属性值"/>

(2)子组件接收值:在子组件的script中定义props属性接收父组件传递的值(props与data、methods、computed同级)

data、computed、props中均可存放数据

第一种:可限制数据类型
props:{
    自定义属性名//父组件中定义的属性名
}
第二种:只用于接收数据
props:["属性名1","属性名2"……]
第三种:设置默认值、是否是必须的
props:{
    属性名:{
        type:数据类型,
        required:true/false,
        default:function(){
            return  默认值
        }       
    }
}
第二种:
//app.js
<Login userName="小李" passWord="111111"></Login>
//login
<template>
    <div>
        <h2>子组件-----------登录</h2>
        <label>
            用户名:<input type="text" v-model="userName">
        </label>
        <br>
        <label>
            密码:<input type="text" v-model="passWord">
        </label>
        <br>
        <button>登录</button>
    </div>
</template>

<script>
export default {
    props: ["userName", "passWord"]
}
</script>
第一种:
//app.js
<Login :users="obj"></Login>
 data() {
    return {
      obj: {
        userName: "小李",
        passWord: "111111"
      }
    }
  },
//login
<template>
    <div>
        <h2>子组件-----------登录</h2>
        <label>
            用户名:<input type="text" v-model="users.userName">
        </label>
        <br>
        <label>
            密码:<input type="text" v-model="users.passWord">
        </label>
        <br>
        <button>登录</button>
    </div>
</template>

<script>
export default {
    props: {
        users: {
            type: Object
        }
    }
}
</script>
4、子组件向父组件传值:通过$emit方式来传值,即在子组件中触发父组件中的事件

(1)在父组件中自定义事件

<子组件名 @自定义事件名="事件处理函数"/>

(2)在子组件中触发父组件中的自定义事件:使用$emit

①子组件中的删除按钮:

  <button @click="del">删除</button>
 methods: {
        del() {
            this.$emit('delBtn')//通过this.$emit触发父组件中的自定义事件
        }
    }

②父组件中使用TableCom.vue组件时,自定义事件:delBtn,delBtn的事件处理函数是removeStudent,在函数中确定删除

 <TableCom @delBtn="removeStudent"></TableCom>
 methods: {
    removeStudent() {
      let flag = confirm("确定删除吗?")
      if (flag) {
        alert("确定删除")
      } else {
        alert("取消删除")
      }
    }
  }

③通过$emit触发父组件自定义事件的同时传递参数

this.$emit("自定义事件名",参数1,参数2……)

④在父组件中接收子组件通过$emit传递的参数

removeStudent(val1,val2) { //val1:参数1,val2:参数2 }

5、兄弟组件之间传值:采用事件总线(EventBus)的方式
1.实现过程

(1)在项目的main.js文件中创建一个Vue实例(空的vue对象)作为事件总线

Vue.prototype.$变量名=new Vue()
将一个Vue实例绑定在项目的对象原型上
$变量名是绑定在原型上的变量,为全局共享---$bus称为事件总线

(2)兄弟组件之间发送数据方使用$emit方法触发事件总线中的某个事件

<template>
    <div>
        <h3>发送方:{{ happy }}</h3>
        <button @click="sendToChildren2">生日快乐</button>
    </div>
</template>
<script>

export default {
    data() {
        return {
            happy: '发财 暴富 好运常在'
        }
    },
    methods: {
        sendToChildren2() {
            this.$bus.$emit('sendTo2', this.happy)//sendTo2为自定义时间名,this.happy为要传递过去的内容
        }
    }
}
</script>

(3)兄弟组件中的接收方使用$on监听相应的事件,若发现事件被触发,则开始接收数据

<template>
    <div>
        <h3>接收方:{{ info }}</h3>
    </div>
</template>
<script>

export default {
    data() {
        return {
            info: ''
        }
    },
    methods: {
        getChildren1(value) {
            this.info = value
        }
    },
    created() {//钩子函数(生命周期函数):在组件创建完成后自动调用
        this.$bus.$on('sendTo2', this.getChildren1)
    }
}
</script>
6、跨级组件(隔代组件)之间传值
1.provide:本质是个函数,消息的发布者
2.inject:本质是个函数,消息的接收者
<template>
    <div>
        <h2>发布者:康熙---{{ words }}</h2>
        <YongZheng></YongZheng>
    </div>
</template>

<script>
import YongZheng from './YongZheng.vue';

export default {
    data() {
        return {
            words: "整顿吏治"
        };
    },
    provide: function () {
        return {
            lastWords: () => this.words
        };
    },
    components: { YongZheng }
}
</script>
<template>
    <div>
        <h2>接收者:雍正---{{ getFaterInfo }}</h2>
        <QiangLong></QiangLong>
    </div>
</template>

<script>
import QiangLong from '../views/QiangLong.vue'
export default {
    data() {
        return {

        }
    },
    inject: ['lastWords'],
    computed: {
        getFaterInfo() {
            return this.lastWords();
        }
    },
    components: { QiangLong }
}

</script>
<template>
    <div>
        <h2>接收者:乾隆---{{ getGrandfatherInfo }}</h2>
    </div>
</template>

<script>
export default {
    data() {
        return {

        }
    },
    inject: ['lastWords'],
    computed: {
        getGrandfatherInfo() {
            return this.lastWords();
        }
    }
}

</script>
7、动态组件切换时传值
1.实现方式采用计算属性

(1)在父组件中定义切换按钮

①obj属性是自定义属性,子组件均通过该属性接收数据

②sendData是计算属性,根据切换的组件名返回不同的数据,同时传递给不同的组件

<template>
  <div id="app">
    <div>
      <a href="#" @click.prevent="changeStu">学生管理</a>
      &nbsp;&nbsp;&nbsp;&nbsp;
      <a href="#" @click.prevent="changeClazz">班级管理</a>
      <component :is="comName" :obj="sendData"></component>
    </div>
  </div>
</template>

<script>
import Student from './views/Student.vue';
import Clazz from './views/Clazz.vue';
export default {
  name: 'App',
  components: {
    Student,
    Clazz
  },
  computed: {
    sendData() {
      switch (this.comName) {
        case 'Student':
          return { id: 1001, name: '小李' }
        case 'Clazz':
          return "web前端"
        default:
          return ''
      }
    }
  },
  data() {
    return {
      comName: ''
    }
  },
  methods: {
    changeStu() {
      this.comName = 'Student'
    },
    changeClazz() {
      this.comName = 'Clazz'
    }
  }
}
</script>

(2)子组件中通过props的obj来接收数据

//student.vue
<script>
export default {
    props: {
        obj: {
            type: Object,
            default: function () {
                return { id: 1111, name: "张三" }
            }
        }
    }
}
</script>

//clazz.vue
<script>
export default {
    props: {
        obj: {
            type: String,
            default: function () {
                return 'java开发'
            }
        }
    }
}
</script>
2.组件切换时的状态保持

keepalive

8、组件之间通信的其他方法
1.ref用法

数据存放位置:data,计算属性,props

(1)在父组件中使用ref给子组件取一个别名

 	<Children1 ref="son"></Children1>
    <hr>
    <p>父组件:{{ msg }}</p>
    <button @click="getSonData">获取子组件内容</button>

(2)在父组件中通过this.$refs.子组件别名.属性名

export default {
  name: 'App',
  components: {
    Children1
  },
  data() {
    return {
      msg: ''
    }
  },
  methods: {
    getSonData() {
      this.msg = this.$refs.son.info
    }
  }
}
2.$parent用法:子组件中获取父组件的属性值
 <p>父的内容:{{ fatherInfo }}</p>
<button @click="getFatherData">获取父组件内容</button>
        
methods: {
        getFatherData() {
            this.fatherInfo = this.$parent.fa//fa是父组件的属性
        }
    }
3.$children用法:父组件中获取子组件的属性值

父组件中可能有多个子组件,因此$children获取到的是一个数组(使用下标),数组中依次存放的是父组件中引用的子组件,存放顺序与引用顺序一致(即dom中引用顺序)

$children是一个数组

补充:vue目录结构
  • src
    • views:用户自定义的组件
    • components:项目的公共组件
    • utils:工具文件例如ajax
    • plugins:第三方的工具插件
    • router:路由文件
    • store:vuex的状态机
    • assets:静态资源文件
    • api:接口文件(Application Program Interface:应用程序接口)
4.$attrs用法:可以访问到组件中没有被props访问的
<p>子组件:{{ this.$attrs.name }}</p>//若在props中存在则不能通过此方法获取

属性的跨级传输

5. l i s t e n e r s 用法:可以通过 v − o n = “ listeners用法:可以通过v-on=“ listeners用法:可以通过von=listeners”方式实现事件的向下传递

事件的跨级传输

(1)在孙子组件中触发爷爷组件中的事件:不能直接触发

(2)在子组件中引用孙子组件时使用v-on=“$listeners”指令,将事件进行跨级传递

(3)若在自定义事件中使用.native修饰符,则该事件不能通过$listeners向下传递

//爷爷
<children @handlerEvent1="method1"></children>
//父亲
<ChildrenSon v-on="this.$listeners"></ChildrenSon>
//孙子
 <button @click="ye">触发爷爷组件事件</button>

 methods: {
        ye() {
            this.$emit('handlerEvent1');
        }
    }

强调

$el:获取vue实例绑定的DOM元素
$data:访问vue实例的data属性
$options:访问vue实例的methods下定义的方法
$watch:访问监听器
十八、组件切换时的状态保持

1、动态组件的实现原理:在切换时创建组件并渲染出来,被换掉的组件被销毁。若频繁切换组件,会造成系统性能下降,用户体验差

2、动态切换时,保持组件状态:既让组件创建后始终保持在内存中,切换时换掉的组件显示为不活动(不可用),当前组件显示为活动(可用)

3、实现方式:使用vue的内置组件keep-alive

<keep-alive>
	<component :is="comName"></component>
</keep-alive>
十九、组件的生命周期
1、生命周期

组件的生命周期指组件从创建到运行到销毁的过程

2、生命周期中的钩子函数:即在组件生命周期的不同阶段自动运行的内置函数
1.组件创建阶段的钩子函数

beforeCreate():组件创建之前会执行。data、props、methods属性均不可用

created():组件创建完成后。data、props、methods属性可用,此时组件未渲染完成即未挂载到dom元素上。不能操作dom,可以向服务器发起ajax请求。 ----是vue组件最早可以发起ajax请求的地方。之后均可以发起ajax请求

beforeMount():组件挂载之前,不能操作dom元素

mounted():组件挂载之后,可以操作dom。最早可以操作dom的地方

2.组件运行阶段的钩子函数

beforeUpdate():组件更新之前,组件data中的属性值已经改变,但还没有渲染到dom上

updated():组件更新完成之后,组件data中的属性值已经改变,并渲染到dom上

beforeUpdate()与updated之间间隔时间太短导致看不出区别

3.销毁阶段的钩子函数

beforeDestroy():组件销毁之前,此时组件可以正常工作

destroyed():组件销毁之后,此时组件解除所有的绑定

二十、UI库:element-ui
1、用法

(1)全局引用:

①安装

npm i element-ui 

②在vue项目的main.js文件中进行配置

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk'
Vue.use(ElementUI)

③在组件中使用element-ui 的组件

(2)按需引用:

2、组件的使用

https://element.eleme.cn/#/zh-CN/component/table

1.e-button、icon
2.栅格布局(Layout布局):响应式布局

将页面中的一行分为24个栅格(分栏)

el-row表示一行
el-row :gutter="间隔"
el-col表示一列
el-col :span='分栏个数'
3、容器:container
4、表格
5、警告框
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.$message({
            type: 'success',
            message: '删除成功!'
          });
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });          
        });
      }
6.分页
 <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
            :page-sizes="[5, 10, 15, 20, 25, 30]" :page-size="5" layout="total, sizes, prev, pager, next, jumper"
            :total="total">
        </el-pagination>

size-change:自定义事件,当每页显示的记录数发生改变时触发
current-change:自定义事件,当当前页的页码发生改变时触发current-page:属性,存放当前页的页码
page-sizes:属性(数组),每页显示的记录数的列表
page-size:属性,每页显示的记录数
total:总记录数

二十一、vue的插槽
1、作用

父组件可以向子组件指定位置插入dom结构

(1)在子组件中定义插槽(占位符)

(2)父组件在引用子组件时,根据实际需要,在插槽的位置放入dom结构

本质:扩展了父组件向子组件传值的方式

2、分类:默认插槽(匿名插槽)、具名插槽、作用域插槽
1.默认插槽(匿名插槽)

定义方法:在子组件中定义

//子组件Son.vue
<template>
    <div>
    	<slot></slot>//插槽标签
    </div>
</template>

在父组件中引用

//父组件中(App.vue)
<Son>
    <img src=""/>
</Son>

一个组件有且只能有一个默认插槽(匿名插槽),避免产生歧义

2.具名插槽:即带有名称的插槽。在子组件中定义插槽时通过name属性给插槽命名

(1)定义方法:在子组件中定义

<slot name="插槽名"></slot>

(2)父组件在使用时如何将html结构放入指定名称的插槽中

①vue2.6以前的版本

<标签名 slot="插槽名"></标签名>

②v-slot:插槽名,只能写在template上-----官方推荐

<template v-slot:插槽名>
	html结构
</template>
3.作用域插槽:数据在组件自身(即数据在子组件中定义),但数据如何显示由组件的使用者决定(由父组件决定数据的显示方式)

(1)定义方法:在子组件中定义

<slot :sonData="games"></slot>
sonData:插槽绑定的属性(变量)
games:script中data中定义的数据

(2)在父组件中引用:

<template scope="ABC">
	<ul>
		<li v-for="(item,index) in ABC.sonData" :key="index">
		{{item}}
		</li>
	</ul>
</template>
scope属性用在template中,用来接收插槽传递的数据
ABC.sonData:把插槽传递的sonData
强调:

①vue2.6.0以前的版本中作用域插槽的写法:slot-scope

②vue2.6.0以后的版本中作用域插槽的写法:scope

<ChaChao title="美食">
      <template scope="ms">
        <ul>
          <li v-for="(item, index) in ms.sonData" :key="index">{{ item }}</li>
        </ul>
      </template>
    </ChaChao>
    <ChaChao title="美食">
      <template scope="{sonData}">
        <ol>
          <li v-for="(item, index) in sonData" :key="index">{{ item }}</li>
        </ol>
      </template>
    </ChaChao>
    <ChaChao title="美食">
      <template slot-scope="{sonData}">
        <h4 v-for="(item, index) in sonData" :key="index">{{ item }}</h4>
      </template>
    </ChaChao>
    <ChaChao title="美食">
      <template v-slot="{ sonData }">
        <p v-for="(item, index) in sonData" :key="index">{{ item }}</p>
      </template>
    </ChaChao>
二十二、Axios
1、简介

(1)基于promise的网络请求库

(2)是对原生的xml HttpRequest对象进行封装,使用起来更简单

(3)相对于jquery封装的ajax扩展性更强

2、特点

(1)符合restful风格的网络请求:每个接口有独立的地址,使用http协议,请求方式包括get,post,put,delete等

补充:HTTP、TCP 和 IP

1.HTTP、TCP 和 IP 都是计算机网络中非常重要的协议,它们分别负责不同层次上的通信和数据传输。

HTTP(Hyper Text Transfer Protocol)是应用层协议,主要用于 Web 浏览器和 Web 服务器之间的通信。HTTP 协议定义了客户端向服务器请求资源的方式,以及服务器向客户端返回资源的方式。例如,在浏览器上输入网址访问一个网站,浏览器会向服务器发送 HTTP 请求,并接收服务器返回的 HTTP 响应,从而展示出网页。

TCP(Transmission Control Protocol)是传输层协议,主要负责在网络上建立可靠的连接。TCP 提供面向连接的数据传输服务,保证数据不丢失、不重复、不失序、并按序到达。TCP 协议通过三次握手来建立连接,通过四次挥手来关闭连接。

IP(Internet Protocol)是网络层协议,主要负责在网络上寻址和路由选择。IP 协议定义了数据报文格式和处理规则,通过 IP 地址定位网络上的主机和路由器。当发送数据时,IP 协议会决定数据的路径,并将数据分成若干个数据包进行传输,最终将数据包重新组装成完整的数据。

2.RESTful 是一种 Web 服务架构风格,是 Representational State Transfer (表征性状态转移)的缩写。

RESTful 架构风格是基于 HTTP 协议设计的,它将网络资源抽象为一组 URI(Uniform Resource Identifier),通过 HTTP 协议的方法(GET、POST、PUT、DELETE 等)对这些资源进行操作,即实现对资源的 CRUD(Create、Read、Update、Delete)操作。RESTful 架构风格强调规范、简洁、可扩展,不需要像 SOAP 一样使用复杂的 XML 消息格式和处理机制。该架构风格通常被用于设计和开发 Web API。

以下是 RESTful 风格的一些规则:

  1. 使用有意义的 URI 标识资源,例如 /users、/products 等;
  2. 通过 HTTP 方法实现对资源的操作,例如 GET 获取资源、POST 创建资源、PUT 更新资源、DELETE 删除资源等;
  3. 使用标准的 HTTP 状态码表示请求的结果,如 200 表示成功、404 表示未找到资源、500 表示服务器内部错误等;
  4. 对于资源之间的关联关系,可以使用嵌套 URI,例如 /users/{userId}/orders/{orderId};
  5. 对于查询参数,可以使用 ? 和 & 符号,例如 /users?page=1&size=10。

使用 RESTful 风格可以使得 Web 服务的接口设计更加统一、可读性更好,同时也方便开发和维护。

(2)在接收服务器端的响应数据时,会在数据外面加上”外壳“(即对数据进行二次封装)

3、使用方法(基本用法)

(1)在vue项目中安装axios模块

npm install axios

(2)在main.js中的配置

import axios from 'axios'//引入axios
Vue.prototype.$http = axios//将axios绑到vue对象的原型上,从而实现全局共享

(3)示例:模拟访问远程数据

<button @click="getBooks">获取书籍</button>

methods: {
    getBooks() {
      this.$http.get('/books.json').then(res => {
        console.log(res.data);
      })
    }
  }

public下创建books.json文件
{
    "arr": [
        {
            "bookId": "1001",
            "bookName": "三国演义",
            "bookPrice": "45.8",
            "publicDate": "2001-12-12"
        },
        {
            "bookId": "1002",
            "bookName": "红楼梦",
            "bookPrice": "75.8",
            "publicDate": "2001-12-12"
        },
        {
            "bookId": "1003",
            "bookName": "西游记",
            "bookPrice": "45.8",
            "publicDate": "2001-12-12"
        },
        {
            "bookId": "1004",
            "bookName": "水浒传",
            "bookPrice": "45.8",
            "publicDate": "2001-12-12"
        }
    ]
}

(4)将获取的服务端数据渲染到element-ui的table上

(5)操作

<el-table-column label="操作" align="center" width="250px">
    <template v-slot="scope">//作用域插槽,scope为表格数据
       <el-button type="primary" icon="el-icon-edit" plain>修改</el-button>
       <el-button @click="delBook(scope.row.bookId)" type="danger" icon="el-icon-delete" plain>删除</el-button>//scope.row表示当前按钮所在行
    </template>
</el-table-column>
4、二次封装(请求拦截器)
新建utils/request.js
// 1.引入axios模块
import axios from "axios";

// 2.调用axios的create方法创建一个实例对象
const instance = axios.create({
    baseURL: 'http://39.105.194.116:8089',
    timeout: 5000//连接时间
})

// 3.定义axios的请求拦截器:给所有的请求都带上token
instance.interceptors.request.use((config) => {
    // 获取本地存储的token(用户登陆成功后存入的token)
    let token = localStorage.getItem('Token')
    if (token) {//token不为空,则将token添加到请求头中
        config.headers.authorization = token
    }
    return config//将请求信息发送给服务器
}, (err) => {
    return Promise.reject(err)
})

// 4.导出axios实例对象
export default instance
在app.vue里使用
<button @click="test">获取后台信息</button>

import $http from './utils/request.js'

 methods: {
    test() {
      $http.get('/api/clazz/all', {
        params: {
          pageSize: 5,
          currentPage: 1
        }
      }).then(res => {
        console.log(res.data);
      })
    }
  }
在api请求模块中再次进行封装

(1)封装不同模块的接口文件

api/modules/clazz.js

// 1.引用axios实例对象
import instance from "@/utils/request";
// 2.封装接口访问的函数
export default {
    getClazzInfo: (params) => instance.get('/api/clazz/all', { params })
}

(2)将所有模块封装到统一的文件中:index.js

import clazz from './modules/clazz.js'
export default {
    clazz
}

(3)将模块的封装文件导入main.js中,做全局配置

import api from './api/index.js'
Vue.prototype.$api = api

(4)在vue组件中引用api接口文件

 methods: {
    async test() {
      let params = {
        pageSize: 5,
        currentPage: 1
      }
      let result = await this.$api.clazz.getClazzInfo(params)
      console.log(result.data);
    }
  }
5、不同请求方式的参数封装
1.get方式请求
axios.get('/url地址',{params:{参数名1:参数值1,……}})
2.post方式请求
axios.post('/url地址',{参数名1:参数值1,……})
3.put方式请求
axios.put('/url地址',{参数名1:参数值1,……})
4.delete方式请求
axios.delete('/url地址',{params:{参数名1:参数值1,……}})
示例:axios+elementui+分页
<template>
    <div>
        <el-table border :data="clazzInfo">
            <el-table-column prop="Id" label="序号" align="center" width="150px"></el-table-column>
            <el-table-column prop="classId" label="班级编号" align="center" width="150px"></el-table-column>
            <el-table-column prop="className" label="班级名称" align="center" width="150px"></el-table-column>
            <el-table-column prop="classRemark" label="备注" align="center" width="150px"></el-table-column>
            <el-table-column label="操作" align="center" width="250px">
                <template v-slot="scope">
                    <el-button type="primary" @click="edit(scope.row)">修改</el-button>
                    <el-button type="danger" @click="edit(scope.row.Id)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
            :page-sizes="[5, 10, 15, 20, 25, 30]" :page-size="5" layout="total, sizes, prev, pager, next, jumper"
            :total="total">
        </el-pagination>
    </div>
</template>

<script>
export default {
    data() {
        return {
            clazzInfo: [],
            params: {
                pageSize: 5,
                currentPage: 1
            },
            total: 0
        }
    },
    methods: {
        async getClazzs() {
            let result = await this.$api.clazz.getClazzInfo(this.params)
            this.total = result.data.total
            this.clazzInfo = result.data.data
            console.log(result.data.data);
        },
        handleSizeChange(val) {
            console.log(`每页 ${val}`);
            this.params.pageSize = val
            this.getClazzs()
        },
        handleCurrentChange(val) {
            console.log(`当前页: ${val}`);
            this.params.currentPage = val
            this.getClazzs()
        }
    },
    created() {
        this.getClazzs()
    }
}
</script>

<style></style>
6、二次封装(响应拦截器)
1.在utils/request.js中定义
2.主要作用:

(1)对服务器端的响应数据进行统一处理

(2)对服务器端的响应状态进行统一处理

3.定义方法
//4.定义响应拦截器:对服务器端的响应信息进行统一的处理后,传给前端页面
instance.interceptors.response.use((response) => {//response时服务器端的响应信息
    return response.data//将axios的data直接返回给前端页面
}, (err) => {
    if (err.response) {//若异常信息存在则对状态码进行判断,不同的异常状态码进行处理
        switch (err.response.status) {
            case 401:
                alert('Token过期,请重新登录')
                localStorage.removeItem('Token')//删除本地存储中的token
                break
        }
    }
})

二十三、elementui组件的使用
1、表单验证
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
  <el-form-item label="活动名称" prop="name">
    <el-input v-model="ruleForm.name"></el-input>
  </el-form-item>
</el-form>
描述
  • el-form:

(1)model属性:表单数据对象

(2)rules属性:表单验证规则

(3)ref:表单别名

(4)label-width:表单域标签的宽度

  • el-form-item

(1)label:标签文本

(2)prop表单项绑定的变量,用于表单验证和el-form的rules配合使用

密码和确认密码的验证:通过函数实现

(1)函数定义在data中return外

 let confirmedPasswordValid = (rule, value, callback) => {
            if (value === this.ruleForm.password) {
                callback()
            } else {
                callback('两次输入的密码不一致')
            }
        }

(2)在rules中进行使用

 confirmpassword: [
                    { required: true, message: '密码不能为空', trigger: 'blur' },
                    { validator: confirmedPasswordValid, trigger: 'blur' }
                ]
在验证时使用正则
 name: [
                    { required: true, message: '用户名不能为空', trigger: 'blur' },
                    { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' },
                    { pattern: /^wn/, message: '用户名必须以wn开头', trigger: 'blur' }
                ],
2、表单控件的显示问题

一行内显示:el-col(栅格布局)

3、在登录按钮中对form进行验证
二十四、通过axios向服务器发起登录验证
1、对用户管理模块进行二次封装
// 1.引用axios实例对象
import instance from "@/utils/request";
// 2.封装接口访问的函数
export default {
    login: (data) => instance.post('/api/users/login', data)
}
2、在组件中使用axios向服务器发起请求
methods: {
        submitForm(formName) {
            this.$refs[formName].validate(async (valid) => {
                // valid未true表示所有验证均通过
                if (valid) {
                    // alert("验证通过")
                    const { code, message, token } = await this.$api.users.login(this.ruleForm)
                    console.log(code, message, token);
                    if (code == 1) {
                        // 验证成功
                        this.$message({
                            message: message,
                            type: 'success'
                        })
                        localStorage.setItem('Token', token)
                    } else {
                        this.$message.error(message)
                    }
                } else {

                    alert("验证未通过")
                    // return false
                }
            })
        }
    }
二十五、用户注册
1、elementui的上传组件
  <el-upload drag action="#" :show-file-list="false" :auto-upload="false" :on-change="handlePreview" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
                    <img v-if="imageUrl" :src="imageUrl" class="avatar" />
                    <i class="el-icon-upload"></i>
                    <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                </el-upload>

(1)drag:是否可以拖拽上传,默认为true

(2)action:上传的服务器地址

(3)show-file-list:是否显示上传文件的列表

(4)on-change:上传文件发生改变时触发

(5)on-success:上传成功触发

(6)before-upload:上传之前触发

(7)img标签用来预览上传的图片

(8)i标签显示上传图标

(9)div标签显示上传文本

(10)auto-upload:自动上传

注意

(1)img标签的样式需要重新设置

(2)关于el-upload的before-upload事件的使用

①必须和auto-upload属性结合使用,当auto-upload属性值为true时,before-upload事件才会被触发

②将before-upload事件中的文件类型、文件大小的验证工作放到on-change事件中

 //图片预览
        handlePreview(file) {
            const isJPG = ((file.raw.type === 'image/jpeg') || (file.raw.type === 'image/png'));
            
            const isLt2M = file.size / 1024 / 1024 < 2;
           
            if (!isJPG) {
                this.$message.error('上传头像图片只能是 JPG/PNG 格式!');
                this.imageUrl = "";
            }
            if (!isLt2M) {
                this.$message.error('上传头像图片大小不能超过 2MB!');
                this.imageUrl = "";
            }
            if (isLt2M && isJPG) {
                this.imageUrl = URL.createObjectURL(file.raw);//将图片地址转变为可识别的地址
            }
        },

(3)上传成功后的处理:on-success事件

  //图片上传成功后
        handleAvatarSuccess(res, file) {
            this.imageUrl = URL.createObjectURL(file.raw);
        },

错题:

1、

被别人影响,带有缓存,data不定义:计算属性;

影响别人,在data中定义:监听器。

2、

props,data,计算属性:存放数据。

props:在子组件中不能改,(只读),不能通过this访问data中数据,data中可以通过this访问props。

3、

单行数据绑定:{{}}、v-text、v-html

单线数据流:父组件传递到子组件

4、

动态绑定:v-bind将data属性绑定到dom

双向绑定:v-model,只适用于input、textarea、select,将value绑定到指定的。

二十六、vue2的路由
1、基本概念
1.路由:router,从一个点到另一个点的决策过程
2.前端路由:地址与组件之间的对应关系(一个地址对应一个组件)
3.单页面程序:SPA,在一个页面中完成所有的操作
4.前端路由的模式:

(1)hash模式:当地址发生改变时,会在浏览器地址栏中出现#。本质是锚点。无法回到上一步。

(2)history模式:可以保存用户的访问记录,可以前进和后退、跳转到指定页面

5.前端路由的工作过程

浏览器输入网页地址时,浏览器做了哪些工作?

将域名提交解析器-找到ip-找到服务器-找到默认主页

(1)用户单击页面中的超链接,引起浏览器地址栏的变化

(2)当浏览器的地址栏发生改变时,前端路由器会拿到改变后的地址值

(3)在路由表中查找与地址值对应的组件

(4)找到对应组件后,在页面中渲染出来

2、路由的使用方法
1.vue-router:是vue官方提供的专用于vue框架的路由组件。通过该组件可以轻松实现单页面应用程序(SPA:single page application)
2.vue-router版本号

(1)vue2对应的版本:vue-router3

(2)vue3对应的版本:vue-router4

3.vue-router的安装
npm i vue-router@3.5.2
4.vue-router中的概念

(1)route:指的是单个路由(表示的是单条的路由信息)

(2)routes:复数,表示多个路由(路由表)

(3)router:路由器

5.vue-router的组成部分

(1)router-link:是路由的超链接组件。本质是a标签(封装了a标签)

(2)router-view:路由视图组件。组件渲染的出口(组件渲染的位置)

(3)vueRouter:路由器对象。监听路由地址变化,当路由地址发生改变时,会去路由表查找与地址对应的组件,并在组件在router-vue中渲染出来。

6.具体实现

(1)src/route/index.js:路由的配置文件

vue.use具有判断功能,vue.component没有判断功能

// 引入vue实例
import Vue from 'vue'

// 引入vue-router
import VueRouter from 'vue-router'

// 将vueRouter注册到vue中

Vue.use(VueRouter)

// 创建路由器对象
const router = new VueRouter({
    // 设置路由的模式
    mode: 'hash',
    // 定义路由表
    routes: [//数组中的每个元素是一个路由(route)
        {
            pash: '/url',
            component: '组件名'
        }
    ]
})

// 导出路由器
export default router

(2)在main.js文件中将路由器对象配置到vue实例中

import router from './route/index.js'

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
7.路由组件的动态加载(按需加载)

(1)箭头函数+import

   routes: [//数组中的每个元素是一个路由(route)
        {
            path: '/login',
            component: () => import('../components/LoginCom.vue')
        }
    ]

(2)箭头函数+require

    routes: [//数组中的每个元素是一个路由(route)
        {
            path: '/login',
            component: (resolve) => require(['@/components/LoginCom.vue'], resolve)
        }
    ]
8.路由时的页面重定向:redirect

页面重定向到login

    routes: [//数组中的每个元素是一个路由(route)
        {
            path: '/login',
            component: (resolve) => require(['@/components/LoginCom.vue'], resolve)
        },
        {
            path:'/',
            redirect:'/login'
        }
    ]
3、router-link的使用:超链接组件

1.属性:to,表示超链接的地址,和a标签的href属性作用类似

<router-link to="url">文本或图片</router-link>

作用:当用户单击文本或图片时,url对应的组件会被渲染到router-view中
 <div>
      <router-link to="/student">学生</router-link>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <router-link to="/teacher">老师</router-link>
    </div>

    <hr>
    <div>
      <router-view></router-view>
    </div>

2.给激活的a标签添加样式

<router-link active-class="active" >待付款</router-link>

.active {
    background-color: blanchedalmond;
}
4、嵌套路由
 {
            path: '/home',
            component: (resolve) => require(['@/components/homePage.vue'], resolve),
             children: [
                path:'子路径',
                component:'组件'
            ]
        },
5、命名路由

1.在路由表中给路由添加name属性

 {
     path: 'student',
     name: 'student',
     component: (resolve) => require(['@/views/Student.vue'], resolve)
     }

2.给路由命名后,router-link中的写法

<router-link :to="{name:名称}"></router-link>

<router-link :to="{ name: 'student' }">学生信息</router-link>

①:to="{name:名称}"名称必须带引号

②在路由表中不同路由name名称不能重复,若重名会渲染出第一个组件

6、路由传参

1.在路由表中给路由添加props属性

{
    path: 'exam',
    name: 'Exam',
    component: (resolve) => require(['@/views/Exam.vue'], resolve),
    props: () => ({ e_id: 'woniu1001' })
   },

2.在路由组件中接收数据

<template>
    <div>
        <p>考号:{{ e_id }}</p>
    </div>
</template>

<script>
export default {
    props: ['e_id']
}
</script>
二十七、声明式导航(标签导航)和编程式导航
1、声明式导航

在dom中使用超链接标签来实现的页面跳转即为声明式导航

(1)a标签:

<a href="url">文本</a>

(2)route-link标签

<router-link to:'url'>文本</router-link>
2、编程式导航

使用js代码实现页面的跳转

(1)原生js中

location.href='url'
location.assign('url'):跳转到指定的页面
history.back():后退
history.forword():前进
history.go(数字):跳转到指定页面

(2)在vue-router中:

①this.$router.push

  • 字符串地址
this.$router.push(url):跳转到指定位置

$router表示路由实例(路由器),push:向路由器中添加一条新的路由

  • 对象方式(path)
this.$router.push({path:'url'})
  • 对象方式(name)
this.$router.push({name:'路由名称'})
  • 对象方式(name)带参数
this.$router.push({name:'路由名称',params:{参数名:参数值}})
  • 对象方式(path)带参数
this.$router.push({path:'url',query:{参数名:参数值}})

②this.$router.replace:使用方法与push类似

会替换当前浏览器地址栏的地址,不会加入到浏览器的历史记录中,即无法后退

③this.$router.go:跳转到指定页面

3、 r o u t e 和 route和 routerouter的区别

$router是vueRouter对象即路由器,包含所有的路由信息

r o u t e 是当前正在跳转的路由对象即一条路由,可通过 route是当前正在跳转的路由对象即一条路由,可通过 route是当前正在跳转的路由对象即一条路由,可通过route获取当前路由的name、path、query、params等

二十八、动态路由匹配

通过动态传参实现路由匹配

1、params方式

(1)在路由表中

{
	path:'/url/:参数名',
	component:‘组件名‘
}

(2)route-link中

<router-link to="/url/参数值"></router-link>

(3)路由的跳转组件中接收

this.$route.params.参数名

(4)示例

route/index.js

  {
     path: 'teacher',
     name: 'teacher',
     component: (resolve) => require(['@/views/Teacher.vue'], resolve),
     children: [
         {
            path: 'exam/:e_id/:e_name',
            name: 'Exam',
            component: (resolve) => require(['@/views/Exam.vue'], resolve)       
           },
      ]
  }

teacher.vue

 <router-link to="/home/teacher/exam/s_1001/小李">学生信息</router-link>
 <router-view></router-view>

exam.vue

<template>
    <div>
        <p>考号:{{ this.$route.params.e_id }}</p>
        <p>考生:{{ this.$route.params.e_name }}</p>
    </div>
</template>
2、query方式

(1)在路由表中

{
	path:'/url',
	component:‘组件名‘
}

(2)route-link中

<router-link :to="{path:'url',query:{参数名:参数值}}"></router-link>

(3)路由的跳转组件中接收

this.$route.query.参数名

在query方式传值时,router-link中若采用path设置路径,参数必须写在query对象中,接收时必须通过query方式接收

<router-link :to="{ path: '/home/teacher/exam', query: { stu_id: '1001', stu_name: '小李' } }">学生信息</router-link>
 <p>考号:{{ this.$route.query.stu_id }}</p>
<p>考生:{{ this.$route.query.stu_name }}</p>

router-link若采用name方式进行路由跳转,参数可以写在params对象中,接收时通过this.$route.params.参数名

<router-link :to="{ name: 'Exam', params: { stu_id: '1002', stu_name: '小华' } }">学生信息</router-link>
 <p>考号:{{ this.$route.params.stu_id }}</p>
 <p>考生:{{ this.$route.params.stu_name }}</p>

参数可以写在query中,接收时通过this.$route.query.参数名

<router-link :to="{ name: 'Exam', query: { stu_id: '1003', stu_name: '小清' } }">学生信息</router-link>
<p>考号:{{ this.$route.query.stu_id }}</p>
<p>考生:{{ this.$route.query.stu_name }}</p>

(4)示例

{
     path: 'exam',
     name: 'Exam',
     component: (resolve) => require(['@/views/Exam.vue'], resolve)                
 }

route-link中

<router-link :to="{ path: '/home/teacher/exam', query: { stu_id: '1001', stu_name: '小李' } }">学生信息</router-link>

exam.vue

<template>
    <div>
        <p>考号:{{ this.$route.query.stu_id }}</p>
        <p>考生:{{ this.$route.query.stu_name }}</p>
    </div>
</template>
二十九、路由守卫
1、分类
(1)全局守卫

route/index.js

// 定义全局的路由守卫
// from从哪来,to到哪去,next回调函数即执行下一步操作

router.beforeEach((to, from, next) => {
    console.log("即将进行路由跳转");
    // 获取本地存储中的token
    let token = localStorage.getItem('Token')
    // 设置路由白名单:即不需要登陆验证的路由地址
    if (to.path == '/login') {//访问login不需要验证
        next()
    } else {
        if (token) {//存在token,即已经登陆过的用户
            next()
        } else {//未登陆的用户,跳转登录界面
            next('/login')
        }
    }
})
三十、vuex
1.什么是vuex

(1)专门用于vue项目状态管理的组件(也称:vue状态机)

(2)vue项目的状态:是vue项目中所有组件共享的一些变量

2.vuex的作用

用于管理vue项目中所有组件共享的变量(获取、修改–同步修改,异步修改)

3.使用场景

(1)不适用于小型vue项目:小型项目使用传值方式即可

(2)适用于大中型vue项目(SPA:单页面应用程序)

4.vuex五大对象
1.state对象:用于保存vue项目中共享的变量(状态属性)
const state = {
	属性名1:属性值1,
	属性名2:属性值2……
}
或
state:{
	属性名1:属性值1,
	属性名2:属性值2……
}

保存在state中的属性共享属性

在整个vue项目中,state对象有且只能有一个

2.mutations对象:用于同步修改state中属性值

(1)在vuex中只能通过mutations来修改state的属性值

(2)在mutations中定义了若干方法用于修改state属性的值

mutations:{
    方法1(state,played){
        同步修改state的值的代码
    }方法2(state,played){
        同步修改state的值的代码
    } 
}

方法的第一个参数:默认是state;第二个参数:在调用该方法时传过来的值

3.actions对象:用于异步修改state中的值

在actions中向服务器发起异步请求,将从服务器获取到的数据保存到state中(即修改state)

(1)在使用了vuex的项目中,若要在vuex中向服务器发起请求,那么请求代码只能放在action中

(2)在异步修改时,是actions向mutations提交修改的请求,然后由mutations取修改state的值。即actions不能直接修改state中的值。

actions:{
    方法1(context,played){
      	异步修改state的值的代码
    }
}

第一个参数:context代表项目的上下文信息,即通过它可以拿到mutations

4.getters对象:专门用于获取state中的属性值

(1)在vuex中提供了两个获取state属性值的方法

①通过state直接获取

this.$store.state.属性名

②通过getters对象中定义的方法获取

getters:{
    方法名(state){
      	return state.属性名
    }
}
5.modules对象:用于大型项目中同时管理多个state
modules:{
	标识名1:模块1的state,
	标识名2:模块2的state,
}
5、具体应用
1.安装
vue2使用vuex3:vuex@3
vue3使用vuex4:vuex@4
2.配置

(1)创建保存vuex的路径:src/store/index.js

// 引入vue实例
import Vue from 'vue';

// 引入vuex
import Vuex from 'vuex';

// 在vue中注册全局的vuex
Vue.use(Vuex)

// 创建store数据仓库,即vuex实例对象,用于保存项目中使用的变量
export default new Vuex.Store({
    state:{// 状态属性
    },
    mutations:{// 用于同步修改state属性
    },
    actions:{// 用于异步修改state属性
    },
    getters:{// 获取state属性值,可以将getters对象作为参数传递给方法(函数),在方法中可以通过getters.方法名()调用
    },
    modules:{// state状态的模块化管理
    }
})

(2)在main.js中配置vuex(项目的公共数据仓库)

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

(3)在组件中使用vuex

①访问vuex中的state

A.this.$store.state.属性名
B.this.$store.state.方法名(不需要带括号)

②调用mutations中的方法

 this.$store.commit('方法名',参数)

③在组件中调用actions中的方法

this.$store.dispatch('方法名')

在actions中调用mutations的方法

context.commit('方法名')
//headpage
   created() {
        this.$store.dispatch('getUserInfo')
    }

//store/index.js
export default new Vuex.Store({
    state: {// 状态属性
        userInfo: { },
    },
    mutations: {// 用于同步修改state属性
      
        changeUserInfo(state, played) {
            state.userInfo = played
        }
    },
    actions: {// 用于异步修改state属性
        async getUserInfo(context) {
            let result = await Vue.prototype.$api.users.getUserInfo()
            console.log(result);
            context.commit('changeUserInfo', result)
        }
    }

})

//user.js接口文件
  getUserInfo: (data) => instance.get('api/users/getUserInfo', { data })

3.示例
<template>
    <div>
        <h2>计数器</h2>
        <button @click="jian">-1</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <span>count:{{ count }}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <button @click="add">+1</button>
    </div>
</template>
<script>
export default {
 
    computed: {

        count() {
            return this.$store.state.count
            // return this.$store.getters.getCount
        }
    },
    methods: {
        add() {
            this.$store.commit('addCount')
        },
        jian() {
            this.$store.commit('subCount')

        }
    }
}
</script>
// 引入vue实例
import Vue from 'vue';

// 引入vuex
import Vuex from 'vuex';

// 在vue中注册全局的vuex
Vue.use(Vuex)

// 创建store数据仓库,即vuex实例对象,用于保存项目中使用的变量
export default new Vuex.Store({
    state: {// 状态属性
        count: 0,
        userInfo: {
            username: '',

        }
    },
    mutations: {// 用于同步修改state属性
        subCount(state) {
            return state.count -= 1
        },
        addCount(state) {
            return state.count += 1
        },
        changeUserInfo(state, played) {
            return state.userInfo.username = played
        }
    },
    actions: {// 用于异步修改state属性
    },
    getters: {// 获取state属性值
        getCount(state) {
            return state.count
        }

    },
    modules: {// state状态的模块化管理
    }
})
6、vuex状态机的工作流程

1.修改state的工作均由mutations完成

2.view要根据实际情况判断同步修改或异步修改

(1)同步修改:

view----->commit(‘方法’)---->mutations---->state---->view

(2)异步修改

view----->dispatch(‘方法’)---->actions---->commit(‘方法’)---->mutations---->state---->view

3.在vue中获取state的值

(1)直接获取:this. s t o r e . s t a t e . 属性名( 2 )通过 g e t t e r s 获取:在 v u e x 中定义 g e t t e r s 对象: t h i s . store.state.属性名 (2)通过getters获取:在vuex中定义getters对象:this. store.state.属性名(2)通过getters获取:在vuex中定义getters对象:this.store.getters.方法名

(3)getters兑现中的方法可以有业务逻辑

7、vuex的辅助函数:方便的访问state、mutations、actions、getters对象
(1)…mapState:用于解构state属性
引入:import { mapState } from 'vuex'

...mapState([''])
//dom
<p>年龄{{ age }}</p>

//script
  import { mapState } from 'vuex'
  computed: {
        ...mapState([ 'age'])
    }

//vuex
 state: {
        age: 23
    }
(2)…mapGetters:用来解构getters对象下的方法
引入:import { mapGetters } from 'vuex'

 ...mapGetters(['getAge'])
(3)…mapMutations:用来解构mutations对象下的方法
...mapMutations({
按钮的事件处理函数名:'vuex状态机mutations中定义的函数名'
})
参数在触发事件时传递
//dom
<button @click="add({ num: 2 })">+2</button>
<button @click="jian">-1</button>

//script
import { mapGetters, mapMutations, mapState } from 'vuex'

  methods: {
        ...mapMutations({
            add: 'addCount',
            jian: 'subCount'
        })
    }

//vuex
subCount(state) {
   state.count -= 1
},
addCount(state, played) {
    state.count += played.num
 }
(4)…mapActions:用于解构actions中定义的方法
<button @click="addOne(5)">2秒后+5</button>

methods: {
        ...mapMutations({
            add: 'addCount',
            jian: 'subCount'
        })
    }

export default new Vuex.Store({
    state: {// 状态属性
        count: 0,
    },
    mutations: {// 用于同步修改state属性

        asyncAdd(state,played) {
            state.count += played
        },
    },
    actions: {// 用于异步修改state属性
       
        asyncadd(context,k) {
            setTimeout(() => context.commit('asyncAdd',k), 2000)
        }
    }
})

8、vuex的模块化

在vue项目中,不同的模块有自己独立的store(子仓库)。在项目下有一个总的store来管理这些子仓库

实现过程

(1)各模块创建自己的store对象

const store对象名={
	namespaced:true//表示开启名称空间(当前对象下的state不能被共享,默认情况下state可共享)
}

(2)在项目的store中(总的数据仓库)使用modules属性管理整合各模块的store

const store = new Vuex.Store({
	state:{},
	getters:{},
	mutations:{},
	actions:{},
	modules:{
		key:子store对象名//key自定义键名
	}
})

(3)组件中的使用:请求store中的同步或异步方法时,需加上子store键名

//   访问state  
this.$store.state.模块名.属性名
//  访问getters   
this.$store.getters['模块名/getters方法名']
//访问同步方法
this.$store.commit('子模块键名/子模块中对应的mutations中的方法名',参数)
//访问异步方法
this.$store.dispatch('子模块键名/子模块中对应的actions中的方法名',参数)
//辅助函数
...mapGetters('模块名',['getters方法名'])
...mapState('模块名',['属性名'])
...mapMutations({'事件处理函数名''模块名/同步的方法名'})
...mapActions({'事件处理函数名''模块名/异步的方法名'})
//store/modules/counter.js
const CounterStore = {
    namespaced: true,
    state: {
        count: 1,
        info: '我是一个计数器'
    },
    getters: {
        getCount(state) {
            return state.count
        },
        getInfo(state) {
            return state.info
        }
    },
    mutations: {
        Increment(state, playod) {
            state.count += playod.num
        },
        Decrement(state, playod) {
            state.count -= playod.num
        },
        setInfo(state, playod) {
            state.info = playod.info
        }
    },
    actions: {
        asyncIncrement(context, playod) {
            setTimeout(() => {
                context.commit('Increment', playod)
            }, 2000);
        }
    }

}

export default CounterStore

//store/index.js
import CounterStore from './modules/counter'
export default new Vuex.Store({
    state: {// 状态属性},
    mutations: {// 用于同步修改state属性},
    actions: {// 用于异步修改state属性},
    getters: {// 获取state属性值},
    modules: {// state状态的模块化管理
        count: CounterStore
    }
})

//count.js组件中
<template>
    <div>
        <p>计数器:{{ getCount }}</p>
        <p>计数器信息:{{ getInfo }}</p>
        <br>
        <button @click="add({ num: 1 })">+1</button>
        <button @click="asyncAdd({ num: 1 })">2秒后+1</button>
    </div>
</template>

<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';

export default {
    computed: {
        ...mapGetters('count', ['getCount', 'getInfo']),
        ...mapState('count', ['count']) 
    },
    methods: {
        ...mapMutations({ add: 'count/Increment' }),
        ...mapActions({ asyncAdd: 'count/asyncIncrement' })
    }
}
</script>
9、vuex的持久化:将状态机中的数据保存到本地存储
(1)安装
npm install vuex-persistedstate
(2)store/index.js文件中进行配置
①引入
import createPersistedstate from 'vuex-persistedstate'
②添加plugins对象,通过createPersistedstate将state中的userInfo保存到本地存储中
plugins: [
        createPersistedstate({
            key: 'persistrenceUserInfo',
            paths: ['userInfo']
        })
    ]

//key为自定义的本地存储中的键名字
//paths表示state中的属性名
三十一、动态路由
1、静态路由:直接写入路由表的,在项目运行过程中,不会发生改变的。
2、动态路由:可任意通过addRouter()方法添加到路由表中某个子路由下的路由信息
(1)如何获取路由信息:即children
router.options.routes[3]

router:代表路由器(路由对象)
options:路由对象的属性,代表路由对象的所有信息
routes:代表路由表(数组)
3:数组下标,路由表中下标为3的路由
(2)何时加入动态路由:在前置的全局路由守卫中动态加入路由信息。
router.beforeEach(async (to, from, next) => {
    console.log("即将进行路由跳转");
    // 获取本地存储中的token
    let token = localStorage.getItem('Token')
    // 设置路由白名单:即不需要登陆验证的路由地址
    if (to.path == '/login') {//访问login不需要验证
        next()
    } else {
        if (token) {//存在token,即已经登陆过的用户
            next()
            // 获取用户信息
            let result = await Vue.prototype.$api.users.getUserInfo()

            // 获取路由信息
            let homeRoute = router.options.routes[3]

            // 遍历用户信息中的authMenu数组,将数组中的菜单信息加入到home
            result.authMenus.forEach((menu) => {
                menu.children.forEach((item) => {
                    homeRoute.children.push({
                        path: item.menuPath,
                        component: () => import(`@/views/${item.menuPath}.vue`)
                    })
                })
            })

            // 动态路由信息添加到路由表中
            router.addRoute(homeRoute)
            console.log("路由信息", router.getRoutes());
        } else {//未登陆的用户,跳转登录界面
            next('/login')
        }
    }
})
(3)在主页面的导航菜单中
 <el-submenu v-for="(item, index) in  getUserAuthMenus " :index="index.toString()" :key="index">
     <template slot="title">
         <i :class="item.menuIcon"></i>
         <span slot="title">{{ item.menuTitle }}</span>
      </template>
  <el-menu-item v-for="(i, x) in item.children" :key="x">
          <i :class="i.menuIcon"></i>
           <router-link :to="{ path: '/home/' + i.menuPath }"> {{ i.menuTitle }}</router-link>
    </el-menu-item>
 </el-submenu>
三十二、elementUi中下拉菜单的用法
<el-dropdown @command="handlerCommand">
        <span class="el-dropdown-link">
          {{ loginUserInfo.userType }}&nbsp;&nbsp;{{ loginUserInfo.userName }}
          <i class="el-icon-arrow-down el-icon--right"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item command="a">基本资料</el-dropdown-item>
          <el-dropdown-item command="b">更换密码</el-dropdown-item>
          <el-dropdown-item divided></el-dropdown-item>
          <el-dropdown-item command="c">退出</el-dropdown-item>  
        </el-dropdown-menu>
      </el-dropdown>

触发事件类型为:@command
与菜单项匹配的方式:每个'el-dropdown-item'定义command属性,属性值不能重复
在@command的事件响应函数中处理:
handlerCommand(command){  --->command形参接收的是'el-dropdown-item'的command属性值
      switch(command){
        case 'a':
          alert('你单击了基本资料')
          break
        case 'b':
           alert('你单击了修改密码')
           break
        case 'c':
          alert('你单击了退出')
          break
      }
    }
三十三、vue的自定义指令
1.定义:通过vue实例的directive函数定义
Vue.directive('指令名'{
	insert(e){//形参e表示使用当前指令的标签
		指令代码
	}
})
2.在main.js文件中引入自定义指令文件
import '@/directives/自定义指令文件'
3.在组件中使用:在指令名前加’v-’
4.示例
//myfocus.js
import Vue from 'vue';
Vue.directive('myfocus', {
    inserted(e) {
        e.focus()
    }
})

//main.js
import './directives/myfocus'

//组件中
<input type="text" v-myfocus>

三十四、scoped属性
1、作用

在vue组件的style标签中加入scoped属性,表示css样式只作用于当前组件,对其他组件没有影响,避免组件之间样式冲突

2、基本原理

(1)在style标签中使用scoped后,会自动给组件实例添加唯一标识,给dom元素添加"data-v-随机数"类名

(2)在style标签中使用scoped后,在css的属性选择器中自动添加唯一的[data-v-随机数]属性

3、样式穿透

在引用第三方的组件时,因为它使用scoped属性将组建的样式隔离起来,若需要修改第三方组件的局部样式同时又不想去掉scope属性,此时使用样式穿透

(1)原生css使用:>>>
div >>> .cla{
    color:red
}
(2)scss、sass、less使用/deep/
div /deep/ .cla{
    color:red
}
(3)::v-deep
div ::v-deep .cla{
    color:red
}
三十五、Vue.use和Vue.prototype的用法
1.vue.use和vue.prototype本质一样,Vue.use是对vue.prototype的封装
2.vue生态内的插件或组件通常使用Vue.use进行注册
3.vue生态外的插件通常使用Vue.prototype进行引用
三十六、vue的响应式原理
1、什么是响应式

当data中数据改变后,view会立即响应,反之亦然

2、响应式的实现原理(vue2中)

采用数据劫持的方式实现响应式

(1)在组件的data中定义的变量都会自动的带上getter和setter方法

(2)底层会调用Object.defineProperty实现对data中数据的劫持

(3)在数据劫持过程中采用了观察者模式(发布-订阅者模式)

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值