Vue2进阶知识总结

102 阅读12分钟

脚手架知识

分析脚手架

Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。

  1. 初始化步骤:

     第一步(仅第一次执行):全局安装@vue/cli。
    

    npm install -g @vue/cli 第二步:切换到你要创建项目的目录,然后使用命令创建项目 vue create xxxx 第三步:启动项目 npm run serve

  2. Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,

    请执行:vue inspect > output.js

  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:包版本控制文件

ref属性

作用:用于给节点打标识

读取方式:this.$refs.xxxxxx

示例:

<div>
	<h1 v-text="msg" ref="title"></h1>
	<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
	<School ref="sch"/>
</div>
methods: {
	showDOM(){
		console.log(this.$refs.title) //真实DOM元素
        //<h1>欢迎学习Vue!</h1>
		console.log(this.$refs.btn) //真实DOM元素
        //<button>点我输出上方的DOM元素</button>
		console.log(this.$refs.sch) //School组件的实例对象(vc)
        //VueComponent 
	}
}

props配置

作用:用于父组件给子组件传递数据

读取方式一: 只指定名称

props: ['name', 'age', 'setName']

读取方式二: 指定名称和类型

props: {
	name: String,
	age: Number,
	setNmae: Function
}

读取方式三: 指定名称/类型/必要性/默认值

props: {
	name: {
	    type: String, 
	    required: true, 
	    default:xxx
	}
}

mixin混入(合)

作用:mixin可以把多个组件中重复出现的属性和方法进行封装方便多次调用使用

示例:

创建一个mixin.js文件封装要混入的内容

export const hunhe = {
	methods: {
		showName(){
			alert(this.name)
		}
	},
	mounted() {
		console.log('你好啊!')
	},
}
export const hunhe2 = {
	data() {
		return {
			x:100,
			y:200
		}
	},
}

全局混入 --mixin

//main.js
import {hunhe,hunhe2} from './mixin'

Vue.mixin(hunhe)
Vue.mixin(hunhe2)

局部混入

<script>
	import {hunhe,hunhe2} from '../mixin'

	export default {
		name:'School',
		mixins:[hunhe,hunhe2]
	}
</script>

插件

Vue 插件是一个包含 install 方法的对象

通过 install 方法可以给 Vue 或 Vue 实例添加方法, 定义全局指令等

示例:

创建一个plugins.js文件封装插件

export default {
	install(Vue,x,y,z){
		console.log(x,y,z)
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})

		//定义全局指令
		Vue.directive('fbind',{
			//指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			//指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			//指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		})

		//定义混入
		Vue.mixin({
			data() {
				return {
					x:100,
					y:200
				}
			},
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.hello = ()=>{alert('你好啊')}
	}
}

应用插件

//main.js
//引入插件
import plugins from './plugins'

//应用(使用)插件
Vue.use(plugins,1,2,3)

scoped样式

作用:使得CSS样式局部生效,不影响其他组件的样式

示例:

<template>
	<div class="demo">
	</div>
</template>
<style scoped>
	.demo{
		background-color: skyblue;
	}
</style>

浏览器本地存储

localStorage

方法用法说明
setItem()localStorage.setItem(key,value)将value存储到key字段
getItem()localStorage.setItem(key,value)获取指定key本地存储的值
removeItem()localStorage.removeItem(key)删除指定key本地存储的值
clear()localStorage.clear()清空本地存储的值

sessionStorage

方法用法说明
setItem()sessionStorage.setItem(key,value)将value存储到key字段
getItem()sessionStorage.setItem(key,value)从sessionStorage获取数据
removeItem()sessionStorage.removeItem(key)从sessionStorage删除保存的数据
clear()sessionStorage.clear()从sessionStorage删除所有保存的数据

组件自定义事件

绑定事件监听

<div class="app">
	<h1>{{msg}},学生姓名是:{{studentName}}</h1>

	<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
	<School :getSchoolName="getSchoolName"/>

	<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
	<!-- <Student @getStudentName="getStudentName" @demo="m1"/> -->

	<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
	<Student ref="student" @click.native="show"/>
</div
methods: {
	getSchoolName(name){
		console.log('App收到了学校名:',name)
	},
	getStudentName(name,...params){
		console.log('App收到了学生名:',name,params)
		this.studentName = name
	},
	m1(){
		console.log('demo事件被触发了!')
	},
	show(){
		alert(123)
	}
},
mounted() {
	this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
	// this.$refs.student.$once('getStudentName',this.getStudentName) //绑定自定义事件(一次性)
}

触发事件

触发组件实例身上的getStudentName事件
this.$emit('getStudentName',this.name,666,888,900)

解绑自定义事件

this.$off('atguigu') //解绑一个自定义事件
this.$off(['atguigu','demo']) //解绑多个自定义事件
this.$off() //解绑所有的自定义事件

使用this.$destroy() 销毁组件的实例后所有组件的实例的自定义事件会全都不奏效。

全局事件总线

  1. Vue 原型对象上包含事件处理的方法 (1)on(eventName,listener):绑定自定义事件监听(2on(eventName, listener): 绑定自定义事件监听 (2)emit(eventName, data): 分发自定义事件 (3)off(eventName):解绑自定义事件监听(4off(eventName): 解绑自定义事件监听 (4)once(eventName, listener): 绑定事件监听, 但只能处理一次

  2. 所有组件实例对象的原型对象的原型对象就是 Vue 的原型对象

(1) 所有组件对象都能看到 Vue 原型对象上的属性和方法 (2)Vue.prototype.$bus = new Vue(), 所有的组件对象都能看到$bus 这个属性对象

  1. 全局事件总线

(1)包含事件处理相关方法的对象(只有一个) (2)所有的组件都可以得到

指定事件总线对象

new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this
	},
})

绑定事件

this.$bus.$on('hello',(data)=>{
	console.log('我是School组件,收到了数据',data)
})

分发事件

//Student组件
methods: {
	sendStudentName(){
		this.$bus.$emit('hello',this.name)
	}
},

解绑事件

//School组件
beforeDestroy() {
	this.$bus.$off('hello')
}

消息订阅与发布

一、理解

  1. 这种方式的思想与全局事件总线很相似
  2. 它包含以下操作: (1) 订阅消息 --对应绑定事件监听 (2) 发布消息 --分发事件 (3) 取消消息订阅 --解绑事件监听
  3. 需要引入一个消息订阅与发布的第三方实现库: PubSubJS

二、使用 PubSubJS

  1. 在线文档: github.com/mroderick/P…
  2. 下载: npm install -S pubsub-js
  3. 相关语法 (1) import PubSub from 'pubsub-js' // 引入 (2) PubSub.subscribe(‘msgName’, functon(msgName, data){ }):订阅消息 (3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用 (4) PubSub.unsubscribe(token): 取消消息的订阅

示例:

//父组件
mounted() {
	this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
		console.log(this)
		// console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
	})
},
beforeDestroy() {
    //取消消息的订阅
	pubsub.unsubscribe(this.pubId)
},
//子组件
methods: {
	sendStudentName(){
		pubsub.publish('hello',666)
}

过度与动画

一、vue 动画的理解

  1. 操作 css 的 trasition 或 animation

  2. vue 会给目标元素添加/移除特定的 class

  3. 过渡的相关类名:

    ① xxx-enter-active: 指定显示的 transition

    ② xxx-leave-active: 指定隐藏的 transition

    ③ xxx-enter/xxx-leave-to: 指定隐藏时的样式

二、基本过渡动画的编码

  1. 在目标元素外包裹<transition name="xxx">
  2. 定义 class 样式 a) 指定过渡样式: transition b) 指定隐藏时的样式: opacity/其它

示例1:

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
        <!--appear 使用效果是:打开页面立马执行一次过来的动画-->
		<transition name="hello" appear>
			<h1 v-show="isShow">你好啊!</h1>
		</transition>
	</div>
</template>
<style scoped>
	h1{
		background-color: orange;
	}

	.hello-enter-active{
		animation: a 0.5s linear;
	}

	.hello-leave-active{
		animation: a 0.5s linear reverse;
	}

	@keyframes a {
		from{
			transform: translateX(-100%);
		}
		to{
			transform: translateX(0px);
		}
	}
</style>

示例2:

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group name="hello" appear>
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">hello!</h1>
		</transition-group>
	</div>
</template>
<style scoped>
	h1{
		background-color: orange;
	}
	/* 进入的起点、离开的终点 */
	.hello-enter,.hello-leave-to{
		transform: translateX(-100%);
	}
	.hello-enter-active,.hello-leave-active{
		transition: 0.5s linear;
	}
	/* 进入的终点、离开的起点 */
	.hello-enter-to,.hello-leave{
		transform: translateX(0);
	}
</style>

示例3:

集成animate.css动画库

安装:npm install animate.css

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group 
			appear
			name="animate__animated animate__bounce" 
			enter-active-class="animate__swing"
			leave-active-class="animate__backOutUp"
		>
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">hello!</h1>
		</transition-group>
	</div>
</template>

<script>
	import 'animate.css'
	export default {
		name:'Test',
		data() {
			return {
				isShow:true
			}
		},
	}
</script>

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

配置代理服务器

创建vue.config.js文件编写以下内容:

module.exports = {
  pages: {
    index: {
      //入口
      entry: 'src/main.js',
    },
  },
	lintOnSave:false, //关闭语法检查
	//开启代理服务器(方式一)
	/* devServer: {
    proxy: 'http://localhost:5000'
  }, */
	//开启代理服务器(方式二)
	devServer: {
    proxy: {
      '/demo': {
        target: 'http://localhost:5001',
				pathRewrite:{'^/demo':''},
        // ws: true, //用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
      }
    }
  }
}

vue 项目中常用的 2 个 Ajax 库

axios:通用的 Ajax 请求库, 官方推荐,使用广泛。

vue-resource:vue 插件库, vue1.x 使用广泛,官方已不维护。

插槽

父组件向子组件传递带数据的标签,当一个组件有不确定的结构时, 就需要使用slot 技术。

注意:插槽内容是在父组件中编译后, 再传递给子组件的。

分类

  • 默认插槽
  • 具名插槽
  • 作用域插槽

一、默认插槽

示例:

<!--App.vue-->
<template>
	<div class="container">
	<Test>
        <h1>
            我是一级标题!
        </h1>
    </Test>
    <Test>
        <h2>
            我是二级标题!
        </h2>
    </Test>
	</div>
</template>
<!--Test.vue-->
<template>
	<div class="test">
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
	</div>
</template

二、具名插槽

示例:

<!--App.vue-->
<template>
	<div class="container">
		<Test>
			<img slot="center" src="" alt="">
			<a slot="footer" href="">美食</a>
		</Test>

		<Test>
			<ul slot="center">
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
			<div class="foot" slot="footer">
				<a href="">单机游戏</a>
				<a href="">网络游戏</a>
			</div>
		</Test>

		<Test>
			<video slot="center" controls src=""></video>
			<template v-slot:footer>
				<div class="foot">
					<a href="">经典</a>
					<a href="">热门</a>
					<a href="">推荐</a>
				</div>
				<h4>欢迎前来观影</h4>
			</template>
		</Test>
	</div>
</template>
<!--Test.vue-->
<template>
	<div class="test">
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
		<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
	</div>
</template>

三、作用域插槽

子组件的数据固定,标签体渲染内容结构样式不定

<!--App.vue-->
<template>
	<div class="container">

		<Test title="游戏">
			<template scope="a">
				<ul>
					<li v-for="(g,index) in a.games" :key="index">{{g}}</li>
				</ul>
			</template>
		</Test>

		<Test title="游戏">
			<template scope="{games}">
				<ol>
					<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
				</ol>
			</template>
		</Test>

		<Test title="游戏">
			<template slot-scope="{games}">
				<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
			</template>
		</Test>
        
	</div>
</template>
<!--Test.vue-->
<template>
	<div class="test">
		<h3>{{title}}分类</h3>
		<slot :games="games" msg="hello">我是默认的一些内容</slot>
	</div>
</template>

vuex

一、理解vuex

(1)vuex 是什么

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

(2)什么时候使用 Vuex

​ ① 多个组件依赖于同一状态

​ ② 来自不同组件的行为需要变更同一状态

(3)Vuex 工作原理图

二、Vuex 核心概念和 API

(1)state

  1. vuex 管理的状态对象
  2. 它应该是唯一的
  3. 示例代码:
const state = {
	sum:0 
}
<!--组件访问 State 中数据的第一种方式:-->
<h1>当前求和为:{{$store.state.sum}}</h1>
// 1. 从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'

// 2. 将全局数据,映射为当前组件的计算属性
computed: {
...mapState(['sum'])
}

(2)mutations

  1. 值是一个对象,包含多个直接更新 state 的方法
  2. 谁能调用 mutations 中的方法?如何调用? 在 action 中使用:commit('对应的 mutations 方法名') 触发
  3. mutations 中方法的特点:不能写异步代码、只能单纯的操作 state
  4. 示例代码:
const mutations = {
    ADD(state,value){
		console.log('mutations中的ADD被调用了')
		state.sum += value
	},
}
//触发 mutations 的第一种方式
methods: {
	increment(){
		this.$store.commit('ADD',this.n)
	},
}
//触发 mutations 的第二种方式

// 1. 从 vuex 中按需导入 mapMutations 函数
import { mapMutations } from 'vuex'

// 2. 将指定的 mutations 函数,映射为当前组件的 methods 函数
methods: {
...mapMutations(['ADD'])
}

(3)actions

  1. 值为一个对象,包含多个响应用户动作的回调函数
  2. 通过 commit( )来触发 mutation 中函数的调用, 间接更新 state
  3. 如何触发 actions 中的回调? 在组件中使用: $store.dispatch('对应的 action 回调名') 触发
  4. 可以包含异步代码(定时器, ajax 等等)
  5. 示例代码:
const actions = {
    addWait(context,value){
		console.log('actions中的addWait被调用了')
		setTimeout(()=>{
			context.commit('ADD',value)
		},500)
	}
}

const mutations = {
    ADD(state,value){
		console.log('mutations中的ADD被调用了')
		state.sum += value
	},
}
//触发 actions 的第一种方式
methods: {
	incrementWait(){
	this.$store.dispatch('addWait',this.n)
	},
}
//触发 actions 的第二种方式

// 1. 从 vuex 中按需导入 mapActions 函数
import { mapActions } from 'vuex'

// 2. 将指定的 actions 函数,映射为当前组件的 methods 函数
methods: {
...mapActions(['addWait'])
}

(4)getters

  1. 值为一个对象,包含多个用于返回数据的函数
  2. 如何使用?—— $store.getters.xxx
  3. 示例代码:
const getters={
    showSum:(state)=>{
        return '当前最新的总数量是【'+ state.sum +'】'
    }
}

使用 getters 的第一种方式:

this.$store.getters.showNum

使用 getters 的第二种方式:

import { mapGetters } from 'vuex'
computed: {
...mapGetters(['showNum'])
}

(5) modules

  1. 包含多个 module
  2. 一个 module 是一个 store 的配置对象
  3. 与一个组件(包含有共享数据)对应

示例:

store/count.js

//求和相关的配置
export default {
	namespaced:true,
	actions:{
		addWait(context,value){
			setTimeout(()=>{
				context.commit('ADD',value)
			},500)
		}
	},
	mutations:{
		ADD(state,value){
			state.sum += value
		},
	},
	state:{
		sum:0, //当前的和
	},
	getters:{
		bigSum(state){
			return state.sum*10
		}
	}
}

store/person.js

//人员管理相关的配置
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
	namespaced:true,
	actions:{
		addPersonWang(context,value){
			if(value.name.indexOf('王') === 0){
				context.commit('ADD_PERSON',value)
			}else{
				alert('添加的人必须姓王!')
			}
		},
		addPersonServer(context){
			axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
				response => {
					context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
				},
				error => {
					alert(error.message)
				}
			)
		}
	},
	mutations:{
		ADD_PERSON(state,value){
			state.personList.unshift(value)
		}
	},
	state:{
		personList:[
			{id:'001',name:'张三'}
		]
	},
	getters:{
		firstPersonName(state){
			return state.personList[0].name
		}
	},
}

index.js

//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
	modules:{
		countAbout:countOptions,
		personAbout:personOptions
	}
})

调用方式:

直接调用

computed:{
	personList(){
		return this.$store.state.personAbout.personList
	},
	sum(){
		return this.$store.state.countAbout.sum
	},
	firstPersonName(){
		return this.$store.getters['personAbout/firstPersonName']
	}
},
 methods: {
	add(){
		const personObj = {id:nanoid(),name:this.name}
		this.$store.commit('personAbout/ADD_PERSON',personObj)
		this.name = ''
	},
	addWang(){
		const personObj = {id:nanoid(),name:this.name}
		this.$store.dispatch('personAbout/addPersonWang',personObj)
		this.name = ''
	},
	addPersonServer(){
		this.$store.dispatch('personAbout/addPersonServer')
	}
 }

map映射方式

computed:{
	//借助mapState生成计算属性,从state中读取数据。(数组写法)
	...mapState('countAbout',['sum']),
	...mapState('personAbout',['personList']),
	//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
	...mapGetters('countAbout',['bigSum'])
},
methods: {
	//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
	...mapMutations('countAbout',{increment:'ADD'}),
	//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
	...mapActions('countAbout',{incrementWait:'addWait'})
    //<button @click="incrementWait(n)">等一等再加</button>n为传递的参数
}

vue-router

一、相关理解

(1) vue-router 的理解

​ vue 的一个插件库,专门用来实现 SPA 应用

(2) 对 SPA 应用的理解

  1. 单页 Web 应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  4. 数据需要通过 ajax 请求获取。

(3)路由的理解

  1. 什么是路由?

    • 一个路由就是一组映射关系(key - value)
    • key 为路径, value 可能是 function 或 component
  2. 路由分类

    后端路由:

    ① 理解:value 是 function, 用于处理客户端提交的请求。 ② 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。

    前端路由: ① 理解:value 是 component,用于展示页面内容。 ② 工作过程:当浏览器的路径改变时, 对应的组件就会显示。

二、基本路由

编写使用路由的 3 步

  1. 定义路由组件

    <template>
    	<h2>我是About的内容</h2>
    </template>
    
    <script>
    	export default {
    		name:'About'
    	}
    </script>
    
  2. 注册路由

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

    //引入Vue
    import Vue from 'vue'
    //引入App
    import App from './App.vue'
    //引入VueRouter
    import VueRouter from 'vue-router'
    //引入路由器
    import router from './router'
    
    //关闭Vue的生产提示
    Vue.config.productionTip = false
    //应用插件
    Vue.use(VueRouter)
    
    //创建vm
    new Vue({
    	el:'#app',
    	render: h => h(App),
    	router:router
    })
    
    <template>
      <div>
        <div class="row">
          <div class="col-xs-offset-2 col-xs-8">
            <div class="page-header"><h2>Vue Router Demo</h2></div>
          </div>
        </div>
        <div class="row">
          <div class="col-xs-2 col-xs-offset-2">
            <div class="list-group">
    					<!-- Vue中借助router-link标签实现路由的切换 -->
    					<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
              <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
            </div>
          </div>
          <div class="col-xs-6">
            <div class="panel">
              <div class="panel-body">
    						<!-- 指定组件的呈现位置 -->
                <router-view></router-view>
              </div>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    	export default {
    		name:'App',
    	}
    </script>
    
    

    三、嵌套(多级)路由

    示例:

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

    四、路由的query参数

    示例:

    <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>-->
    	
    	<!-- 跳转路由并携带query参数,to的对象写法 -->
    	<router-link :to="{
    		path:'/home/message/detail',
    		query:{
    		id:m.id,
    		title:m.title
    		}
    	}">
    		{{m.title}}
    	</router-link>
    </li>
    
    <!--接受query参数-->
    <template>
    	<ul>
    		<li>消息编号:{{$route.query.id}}</li>
    		<li>消息标题:{{$route.query.title}}</li>
    	</ul>
    </template>
    

    五、命名路由

    {
    	path:'message',
    	component:Message,
    	children:[
    		{
               // 路由命名
    			name:'xiangqing',
    			path:'detail',
    			component:Detail,
    		}
    	]
    }
    
    <!-- 跳转路由并携带query参数,to的对象写法 -->
    <router-link :to="{
    	name:'xiangqing',
    	query:{
    		id:m.id,
    		title:m.title
    	}
    }">
    	{{m.title}}
    </router-link>
    

    六、路由的params参数

    {
    	path:'message',
    	component:Message,
    	children:[
    		{
    			name:'xiangqing',
    			path:'detail/:id/:title',
    			component:Detail,
    		}
    	]
    }
    
    <!-- 跳转路由并携带params参数,to的对象写法 -->
    <router-link :to="{
    	name:'xiangqing',
    	params:{
    		id:m.id,
    		title:m.title
    	}
    }">
    	{{m.title}}
    </router-link>
    
    <!--接收params参数-->
    <template>
    	<ul>
    		<li>消息编号:{{$route.params.id}}</li>
    		<li>消息标题:{{$route.params.title}}</li>
    	</ul>
    </template>
    

    七、路由的props配置

    {
    	path:'message',
    	component:Message,
    	children:[
    		{
    			name:'xiangqing',
    			path:'detail',
    			component:Detail,
    			//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
    			// props:{a:1,b:'hello'}
    
    			//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
    			// props:true
    
    			//props的第三种写法,值为函数
    			props($route){
    				return {
    					id:$route.query.id,
    					title:$route.query.title,
    					a:1,
    					b:'hello'
    				}
    			}
    
    		}
    	]
    }
    
    <template>
    	<ul>
    		<li>消息编号:{{id}}</li>
    		<li>消息标题:{{title}}</li>
    	</ul>
    </template>
    
    <script>
    	export default {
    		name:'Detail',
    		props:['id','title']
    	}
    </script>
    

    八、router-link的replace属性

    1. 作用

      控制路由跳转时操作浏览器历史记录的模式。

    2. 两种写入方式

      • push:追加历史记录。(默认设置)
      • replace:替换当前记录。

    理解:

    初始浏览记录:/a=>/b

    在B页面通过replace跳转到C页面后的浏览记录:/a=>/c

    在B页面通过push跳转到C页面后的浏览记录:/a=>/b=>/c

    示例:

    <ul class="nav nav-tabs">
    	<li>
    		<router-link replace class="list-group-item" active-class="active" to="/home/news">News</router-link>
    	</li>
    	<li>
    		<router-link replace class="list-group-item" active-class="active" to="/home/message">Message</router-link>
    	</li>
    </ul>
    

    九、编程式路由导航

    相关 API:

    1. this.$router.push(path或{}): 相当于点击路由链接(可以返回到当前路由界面)
    2. this.$router.replace(path或{}): 用新路由替换当前路由(不可以返回到当前路由界面)
    3. this.$router.back(): 请求(返回)上一个记录路由
    4. this.$router.forward(): 请求下一个记录路由
    5. this.$router.go(-1): 请求(返回)上一个记录路由
    6. this.$router.go(1): 请求下一个记录路由

    十、缓存路由组件

    keep-alive 组件能缓存路由组件使得切换的时候路由不被销毁

    示例:

    <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>
    			<!-- 缓存多个路由组件 -->
                <!--include填的参数是组件名-->
    			<!-- <keep-alive :include="['News','Message']"> -->
    				
    			<!-- 缓存一个路由组件 -->
    			<keep-alive include="News">
    				<router-view></router-view>
    			</keep-alive>
    		</div>
    	</div>
    </template>
    

    十一、两个新的生命周期钩子

    activated()当组件被激活时触发、deactivated()当组件被失活时触发

    当使用了keep-alive缓存组件时,无法在beforeDestroy()钩子函数清除定时器可以使用上面的两个钩子函数完成实现。

    示例:

    activated() {
    	console.log('News组件被激活了')
    	this.timer = setInterval(() => {
    		console.log('@')
    		this.opacity -= 0.01
    		if(this.opacity <= 0) this.opacity = 1
    	},16)
    },
    deactivated() {
    	console.log('News组件失活了')
    	clearInterval(this.timer)
    }
    

    十二、全局路由守卫

    示例:

    // 该文件专门用于创建整个应用的路由器
    import VueRouter from "vue-router";
    //引入组件
    import About from "../pages/About";
    import Home from "../pages/Home";
    import News from "../pages/News";
    import Message from "../pages/Message";
    import Detail from "../pages/Detail";
    
    //创建并暴露一个路由器
    const router = new VueRouter({
      routes: [
        {
          name: "guanyu",
          path: "/about",
          component: About,
          meta: { title: "关于" },
        },
        {
          name: "zhuye",
          path: "/home",
          component: Home,
          meta: { title: "主页" },
          children: [
            {
              name: "xinwen",
              path: "news",
              component: News,
              meta: { isAuth: true, title: "新闻" },
            },
            {
              name: "xiaoxi",
              path: "message",
              component: Message,
              meta: { isAuth: true, title: "消息" },
              children: [
                {
                  name: "xiangqing",
                  path: "detail",
                  component: Detail,
                  meta: { isAuth: true, title: "详情" },
                },
              ],
            },
          ],
        },
      ],
    });
    
    //使用全局前置路由守卫判断权限
    //全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
    router.beforeEach((to, from, next) => {
      console.log("前置路由守卫", to, from);
      if (to.meta.isAuth) {
        //判断是否需要鉴权
        if (localStorage.getItem("school") === "gd") {
          next();
        } else {
          alert("学校名不对,无权限查看!");
        }
      } else {
        next();
      }
    });
    
    //使用全局后置路由守卫设置网页的title
    //全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
    router.afterEach((to, from) => {
      console.log("后置路由守卫", to, from);
      document.title = to.meta.title || "系统";
    });
    
    export default router;
    

    十三、独享路由守卫

    示例:

    // 该文件专门用于创建整个应用的路由器
    import VueRouter from 'vue-router'
    
    //创建并暴露一个路由器
    const router =  new VueRouter({
    	routes:[
    		{
    			name:'zhuye',
    			path:'/home',
    			component:Home,
    			meta:{title:'主页'},
    			children:[
    				{
    					name:'xinwen',
    					path:'news',
    					component:News,
    					meta:{isAuth:true,title:'新闻'},
    					beforeEnter: (to, from, next) => {
    						console.log('独享路由守卫',to,from)
    						if(to.meta.isAuth){ //判断是否需要鉴权
    							if(localStorage.getItem('school')==='dg'){
    								next()
    							}else{
    								alert('学校名不对,无权限查看!')
    							}
    						}else{
    							next()
    						}
    					}
    				},
    				
    			]
    		}
    	]
    })
    
    export default router
    

    十四、组件内路由守卫

    示例:

    <script>
    	export default {
    		name:'About',
    		//通过路由规则,进入该组件时被调用
    		beforeRouteEnter (to, from, next) {
    			console.log('About--beforeRouteEnter',to,from)
    			if(to.meta.isAuth){ //判断是否需要鉴权
    				if(localStorage.getItem('school')==='dg'){
    					next()
    				}else{
    					alert('学校名不对,无权限查看!')
    				}
    			}else{
    				next()
    			}
    		},
    
    		//通过路由规则,离开该组件时被调用
    		beforeRouteLeave (to, from, next) {
    			console.log('About--beforeRouteLeave',to,from)
    			next()
    		}
    	}
    </script>
    

    十五、history模式与hash模式

    hash模式

    ​ hash模式是基于锚点和 onhashchange 事件实现的,将锚点的值作为路由地址,当地址发生变化时触发onhashchange事件,根据路径决定页面上呈现的内容。因为向服务器发送请求不会带#后面的内容,因此修改#后面的内容不会触发浏览器的刷新,不会去请求服务器。

    hash模式示例: http://localhost:8080/#/home

    history模式: history模式是基于Html5中的 **history Api:**history.pushState(IE10以上才支持)和 history.replaceState方法实现的。由于改变了地址, 刷新时会按照修改后的地址请求后端,需要后端配置处理, 将地址访问做映射, 否则会404。

    history模式示例: http://localhost:8080/home

    ​ 注意:history.push 和 history.pushState方法的区别是,history.pushState方法不会请求服务器,只会改变导航栏的地址并记录历史。

    vue修改路由模式

    const router =  new Router({
      mode: 'history', // require service support
      routes: [
        ......
      ]
    })