目录
11.activated和deactivated生命周期钩子
(一)Vuex
安装:npm i vuex@3
1.原理
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态
- store是Vuex的核心,包含着大部分的状态(数据),是响应式存储
- state用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源”而存在
- 更改store状态的唯一途径就是通过调用commit提交mutations
- actions可以包含任何异步操作,再提交mutations
- mutations 用于修改数据
- actions 用于逻辑处理、后台数据发送接收等异步操作
2.搭建Vuex环境
(1)安装store
在src文件夹下配置store文件夹,创建index.js文件,写入
// 该文件用于创建Vuex中最核心的store
// 引入Vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)
// 准备actions 用于逻辑处理、后台数据发送接收等异步操作
const actions = {
add(context, value) {
// console.log('actions收到了数据', context, value)
context.commit('ADD', value)
},
addWait(context, value) {
setTimeout(() => {
context.commit('ADD', value)
}, 1000)
}
}
// 准备mutations 用于修改数据
const mutations = {
ADD(context, value) {
context.sum += value
},
JIAN(context, value) {
context.sum -= value
}
}
// 准备state 用于存储数据
const state = {
sum: 0
}
// 创建store
const store = new Vuex.Store({
actions,
mutations,
state
})
// 暴露
export default store
注意:必须在Vue使用插件vuex后才能初始化store,故将vue.use(vuex)放在index.js中,不放在main.js中,不然会报错
(2)在组件中获取和使用store内的状态
computed: {
sum() {
return this.$store.state.sum
}
},
methods: {
increase() {
this.$store.dispatch('add', this.n)
},
decrease() {
this.$store.commit('JIAN', this.n)
},
increaseWait() {
this.$store.dispatch('addWait', this.n)
}
}
可以通过计算属性获取状态;通过commit直接更改状态,如果需要进行异步操作,则使用dispatch
3.getters配置项
用于加工state内的数据
// 准备getters 用于加工state内的数据
const getters = {
bigSum(state) {
return state.sum * 10 + 7
}
}
getters内函数的参数是state
4.mapXXX
(1)mapState
作用:借助mapState生成计算属性,从State中读取数据
做中大型项目时,为了便于获取state中的数据,减少代码冗余量,故采用计算属性,但是自己一个一个的配置计算属性过于繁复了,vuex的mapState可以为state中的数据自动配置计算属性,生成一个对象
computed: {
// 借助mapState生成计算属性,从State中读取数据(对象方法)
...mapState({ sum: 'sum', mingzi: 'name' })
// 借助mapState生成计算属性,从State中读取数据(数组方法)
...mapState(['sum', 'name']),
}
mapState为数据自动配置计算属性,生成一个对象,使用扩展运算符将对象打开即可直接放入计算属性中;
数组方法生成的计算属性只能和数据同名;
(2)mapGetters
作用:借助mapGetters生成计算属性,从getters中读取数据
computed: {
// 借助mapGetters生成计算属性,从getters中读取数据(对象方法)
...mapGetters({daSum:'bigSum'}),
// 借助mapGetters生成计算属性,从getters中读取数据(数组方法)
...mapGetters(['bigSum'])
}
和mapState大致相同
(3)mapActions
作用:借助mapActions生成函数,用于联系actions:即调用包含$store.dispatch()的函数
<button @click="increaseOdd(n)">奇数时加</button>
<button @click="increaseWait(n)">等1s再加</button>
methods: {
// 借助mapActions生成函数,用于联系actions:即调用包含$store.dispatch()的函数 (对象写法)
...mapActions({ increaseOdd: 'jiaOdd', increaseWait: 'jiaWait' }),
// 借助mapActions生成函数,用于联系actions:即调用包含$store.dispatch()的函数 (数组写法)
...mapActions(['jiaOdd', 'jiaWait']),
},
需要传递参数时,在模板绑定事件时就传入,否则默认的参数就是事件对象
(4)mapMutations
作用:借助mapMutations生成函数,用于联系mutations:即调用包含$store.commit()的函数
<button @click="increase(n)">加</button>
<button @click="decrease(n)">减</button>
methods: {
// 借助mapMutations生成函数,用于联系mutations:即调用包含$store.commit()的函数 (对象写法)
...mapMutations({ increase: 'JIA', decrease: 'JIAN' }),
// 借助mapMutations生成函数,用于联系mutations:即调用包含$store.commit()的函数 (数组写法)
...mapMutations(['JIA', 'JIAN']),
},
需要传递参数时,在模板绑定事件时就传入,否则默认的参数就是事件对象
5.多组件共享数据
组件之间共享数据只需要直接在$store.state里直接获取即可,使用mapState、mapGetters、mapMutations、mapActions可实现快速共享state内数据、共享函数等。
6.Vuex模块化+命名空间
(1)将store文件写为模块化形式
// 模块化 命名空间namespace
// 关于计数的配置
const countOptions = {
// 开启命名空间
namespaced: true,
actions: {...},
mutations: {...},
state: {...},
getters: {...}
}
// 关于人信息的配置
const personOptions = {
// 开启命名空间
namespaced: true,
actions: {...},
mutations: {...},
state: {...},
getters: {...}
}
// 创建store
const store = new Vuex.Store({
// 模块化
modules: {
// countOptions:countOptions 可简写
countOptions,
personOptions
}
})
(2)组件读取state数据
computed: {
// 直接写法
sum() {
return this.$store.state.countOptions.sum
},
// 命名空间
...mapState('countOptions', ['sum', 'name']),
...mapState('personOptions', ['personList']),
},
(3)组件读取getters数据
computed: {
// 直接写法
bigSum() {
return this.$store.getters['countOptions/bigSum']
},
// mapGetters
...mapGetters('countOptions', ['bigSum'])
},
getters内数据都是以 命名空间/数据名 的形式存储
(4)组件读取actions数据
methods: {
// 直接写法
increase() {
this.$store.commit('countOptions/ADD', this.n)
},
// mapMutations简写
...mapMutations('countOptions', { increase: 'ADD', decrease: 'JIAN' }),
},
(5)组件读取mutations数据
methods: {
// 直接写法
increase() {
this.$store.dispatch('countOptions/ADD', this.n)
},
// mapActions简写
...mapActions('countOptions', { increaseOdd: 'addOdd', increaseWait: 'addWait' }),
},
(二)vue-router
1.初步理解
(1)SPA应用
- 单页Web 应用(single page web application,SPA)
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据需要通过ajax请求获取
(2)vue-router
专门用于实现SPA应用功能的一个组件库
下载:npm i vue-router@3
(3)路由
[1]什么是路由?
- 一个路由就是一组key-value的映射关系
- key为路径,value为VueComponent或者function
[2]分类
后端路由:
1)理解: value是function,用于处理客户端提交的请求。
2)工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处
理请求,返回响应数据
前端路由:
1)理解: value是component,用于展示页面内容
2)工作过程:当浏览器的路径改变时,对应的组件就会显示
2.路由的基本使用
(1)安装路由
新建文件夹router,在里面编写配置路由项
// 该文件专门用于创建整个应用的路由器
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
}
]
})
(2)在组件中引入
<div id="guide">
<ul>
<!-- 借助router-link标签实现路由切换;效果与<a>类似 -->
<li><router-link to="/about" active-class="active">about</router-link></li>
<li><router-link to="/home" active-class="active">home</router-link></li>
</ul>
</div>
<div id="main">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
active-class="active"实现链接被点击后添加上指定类
(3)一些注意点
- 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹
- 通过切换隐藏了的路由组件,默认是被销毁掉的,需要的时候再去挂载
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个router,可以通过组件的$router 属性获取到
3.嵌套(多级)路由
多级路由配置方法如下:
// 配置路由
routes: [
{
path: '/about',
component: About,
// 多级路由;路径无需多写一个斜杠,vue会自动添加
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message
}
]
}
]
组件内调用:
<!-- 路径要完整写出 -->
<Li><router-link to="/about/news" active-class="active">News</router-link></Li>
<Li><router-link to="/about/message" active-class="active">Message</router-link></Li>
<router-view></router-view>
4.命名路由
作用:可以简化路由的跳转
在配置中写上name配置项
routes: [
{
name: 'about',
path: '/about',
component: About,
children: [
{
name: 'news',
path: 'news',
component: News
},
...
简化:
<router-link :to="{name='msgdetail'}"></router-link>
5.路由的query参数
利用嵌套路由向子组件传数据:
<li v-for="m in msg" :key="m.id">
<!-- 路由传参 方法1 字符串方式-->
<router-link :to="`/about/message/msgdetail?id=${m.id}&name=${m.name}`">{{ m.name }}</router-link>
</li>
<li v-for="m in msg" :key="m.id">
<!-- 路由传参 方法2 对象方式-->
<router-link :to="{
path: '/about/message/msgdetail',
query: {
id: m.id,
name: m.name
}
}">{{ m.name }}</router-link>
</li>
使用对象方式传输参数更加直观方便修改
子组件内接收
<!-- 使用query参数传数据 接收-->
<p>消息编号:{{ $route.query.id }}</p>
<p>消息名称:{{ $route.query.name }}</p>
6.路由的params参数
向子组件传数据的另一种方式,与query类似
在配置项内完善path传参路径
children: [
{
name: 'msgdetail',
// path: 'msgdetail',
// 必须使用占位符声明接收params参数
path: 'msgdetail/:id/:name',
component: MsgDetail
}
]
当params参数可传可不传时,在后面加一个问号就可以解决错误:/:id?
<!-- 路由传参 params参数 方法1 字符串方式 -->
<router-link :to="`/about/message/msgdetail/${m.id}/${m.name}`">{{ m.name }}</router-link>
<!-- 路由传参 params参数 方法2 对象方式-->
<router-link :to="{
// 必须使用路径名字,不能直接使用路径
name: 'msgdetail',
params: {
id: m.id,
name: m.name
}
}">{{ m.name }}</router-link>
子组件内接收:
<!-- 使用params参数传数据 接收-->
<p>消息编号:{{ $route.params.id }}</p>
<p>消息名称:{{ $route.params.name }}</p>
7.props配置项
在路由文件内声明,利用props接收数据,可以避免路由组件内的代码繁复
// props写法1:值为对象,该对象内的所有key-value以props方式传给该组件,但值不能改变
props: {
a: 1, b: 2
},
// props写法2:值为布尔值,props为真则允许所有传入路由组件的params参数以props形式传给该组件
props: true,
// props写法3:值为函数,自动接收$route参数
props($route) {
return {
id: $route.query.id,
name: $route.query.name
}
}
第三种使用最为广泛。
8.router-link的replace属性
(1)作用:控制路由跳转时操作浏览器历史记录的模式
(2)浏览器的历史记录有两种写入方式:分别为 push 和 replace,push 是追加历史记录,replace 是替换当前记录。路由跳转时候默认为push
(3)如何开启replace模式: <router-link replace .......>News</router-link>
9.编程式路由导航
即不借助router-link实现路由导航
(1)$router.push方法
想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL;相当于router-link
pushShow(m) {
// 作用与 router-link相同
this.$router.push({
path: '/about/message/msgdetail',
query: {
id: m.id,
name: m.name
}
})
}
(2)$router.replace方法
替换当前位置,即在导航时不会向 history 添加新记录
replaceShow(m) {
this.$router.replace({
name: 'msgdetail',
params: {
id: m.id,
name: m.name
}
})
}
也可以直接在传递给 router.push中增加一个属性 replace: true
(3)back()和forward()方法
和网页自带的前进后退功能相同
// 前进一步
this.$router.forward()
// 后退一步
this.$router.back()
(4)$router.go方法
正数为前进n步;负数为后退n步
// 正数为前进n步;负数为后退n步
this.$router.go(2)
10.缓存路由组件
切换组件时,原先的组件会被销毁,缓存路由组件可以让没有显示的组件仍保持挂载状态,即该组件的临时数据仍会被保持
<keep-alive include="Message">
<router-view></router-view>
</keep-alive>
include参数可以指定缓存的路由组件,必须写组件name;
需要多个路由组件同时缓存时,以数组形式写入: :include = "['Message','MsgDetail']"
不写include参数则表示所有路由组件都不会被销毁
11.activated和deactivated生命周期钩子
这两个生命周期是路由组件独有的,当该组件keep-alive后,切入该组件和切出该组件时这两个生命周期就会被触发。
activated() {
console.log("Message组件来了");
this.timeNo = setInterval(() => {
console.log("计时ing")
}, 500);
},
deactivated() {
console.log("Message组件走了");
clearInterval(this.timeNo)
console.log("计时over");
}
12.路由守卫**
作用:对路由进行权限控制
(1)全局路由守卫
部分路由:
{
// 命名路由
name: 'news',
path: 'news',
// 路由元信息,添加isAuthenticity属性方便前置守卫判断是否需要进行权限控制
meta: { isAuth: true, title: '新闻' },
component: News
},
{
name: 'message',
path: 'message',
component: Message,
meta: { isAuth: true, title: '消息' },
children: [
{
name: 'msgdetail',
path: 'msgdetail',
meta: { title: '消息详情' },
component: MsgDetail
}
]
}
meta即路由元信息,是一个对象,可以自行往里面添加信息用于判断当前路由是否需要进行路由守卫的权限控制
[1]全局前置路由守卫
// 1.全局前置路由守卫——初始化和每次路由切换前执行
// 用于组件访问前拦截进行身份验证之类的功能
router.beforeEach((to, from, next) => {
console.log("全局前置路由守卫", to, from)
if (to.meta.isAuth) { // 判断是否需要进行权限控制
if (localStorage.getItem('stuName') === 'csq') {
next() // 跳过
} else {
alert('该学生名无权限访问!')
}
} else {
next()
}
});
可用于实现组件访问前拦截进行身份验证之类的功能
[2]全局后置路由守卫
// 2.全局后置路由守卫——初始化和每次路由切换后执行
// 可用于根据组件实时更改网页title
router.afterEach((to, from) => {
console.log("后置路由守卫", to, from)
document.title = to.meta.title || '学生系统'
});
可用于根据组件实时更改网页title,前置守卫就不能实现该功能
(2)独享路由守卫
用于对指定路由进行权限控制,写在路由内部,就无需使用meta进行验证了
path: 'news',
beforeEnter: (to, from, next) => {
console.log("独享路由守卫", to, from);
if (localStorage.getItem('stuName') === 'csq') {
next() // 跳过
} else {
alert('该学生名无权限访问!')
}
},
component: News
(3)组件内路由守卫
通过路由规则进入离开时才会被调用
// 组件内路由守卫
// 通过路由规则进入组件时被调用
beforeRouteEnter(to, from, next) {
console.log("进入时", to, from);
next()
},
// 通过路由规则离开组件时被调用
beforeRouteLeave(to, from, next) {
console.log("离开前", to, from);
next()
}
13.hash模式和history模式
(1)hash模式:
- 对于一个url来说:#及其后面的内容就是hash值
- hash值不会包含在HTTP请求中,即: hash值不会带给服务器
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
(2)history模式
- 地址干净,美观
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
14.路由重定向
等用到了再回来补,不知道这个具体用处是什么QAQ
(三)总结
Vue2居然大致学完了!好神奇,走走停停终于还是完成了。然后就要马上找个项目开始实战练习!再摸摸vue3,补一补webpack、typescript之类的!power!!!