概念:专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
简言之,Vuex可以实现多组件共享数据。
src/component/Count.vue
<template>
<div>
<h2>当前求和为:{{sum}}</h2>
<select v-model.number="number">
<!-- value里的值是字符串形式,使用 :value 引号里的值就当成js表达式解析了 -->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="handleAdd">+</button>
<button @click="handleDown">-</button>
<button @click="handleOdd">当前求和为奇数再加</button>
<button @click="handleWait">等一等再加</button>
</div>
</template>
<script>
export default {
name: 'Count',
data() {
return {
sum:0,
number:1,
}
},
methods: {
handleAdd(){
this.sum =this.sum+this.number
},
handleDown(){
if(this.sum!=0){
this.sum = this.sum-this.number
}else{
alert("和已经为0了,不能在进行减法操作了!")
}
},
handleOdd(){
if(this.sum%2==0){
return
}else{
this.sum = this.sum+this.number
}
},
handleWait(){
setTimeout(()=>{
this.sum =this.sum+this.number
},1000)
},
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
src/App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from "./components/Count.vue";
export default {
name: 'App',
components: {
Count,
}
}
</script>
<style>
</style>
1.安装npm i vuex@3
注意:vue2中要用vuex的3版本,vue3中要用vuex的4版本
2.创建src/store/index.js
文件,该文件用来创建Vuex中最为核心的store
import vue from'vue'
// 导入vuex
import Vuex from 'vuex'
// 使用vuex
vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
3.在src/main.js
中创建 vm 时传入store配置项:
//脚手架已安装vue,不用自己安装vue
import Vue from 'vue'
//引入app组件,它是所有组件的父组件
import App from './App.vue'
// 引入创建的store实例
import store from './store'
Vue.config.productionTip = false
// 创建vue实例对象——vm
new Vue({
// 将App组件放入容器中
render: h => h(App),
store,
beforeCreate(){
Vue.prototype.$bus = this //安装全局事件总线
}
}).$mount('#app')
·src/component/Count.vue
<template>
<div>
<h2>当前求和为:{{ $store.state.sum }}</h2>
<select v-model.number="number">
<!-- value里的值是字符串形式,使用 :value 引号里的值就当成js表达式解析了 -->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="handleAdd">+</button>
<button @click="handleDown">-</button>
<button @click="handleOdd">当前求和为奇数再加</button>
<button @click="handleWait">等一等再加</button>
</div>
</template>
<script>
export default {
name: 'Count',
data() {
return {
number:1,
}
},
methods: {
handleAdd(){
// this.$store.dispatch('add',this.number)
// 没有逻辑的可直接调用commit
this.$store.commit('ADD',this.number)
},
handleDown(){
// this.$store.dispatch('down',this.number)
// 没有逻辑的可直接调用commit
this.$store.commit('DOWN',this.number)
},
handleOdd(){
this.$store.dispatch('odd',this.number)
},
handleWait(){
this.$store.dispatch('wait',this.number)
},
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
·src/store/index.js
import vue from'vue'
// 导入vuex
import Vuex from 'vuex'
// 使用vuex
vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = {
// add(context,value){
// context.commit('ADD',value)
// },
// down(context,value){
// context.commit('DOWN',value)
// },
odd(context,value){
if(context.state.sum % 2 != 0){
context.commit('ADD',value)
}
},
wait(context,value){
setTimeout(()=>{
context.commit('ADD',value)
},1500)
}
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum+=value
},
DOWN(state,value){
state.sum-=value
}
}
//准备state对象——保存具体的数据
const state = {
sum:0,
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
state
1.Vuex管理的状态(数据)对象
2.它应该是唯一的
let state = {
xxx:initValue,
}
actions
1.值为一个对象,包含多个响应用户动作的回调函数
2.通过 commit( )来触发 mutation 中函数的调用, 间接更新 state
3.如何触发actions中的回调
在组件中使用
$store.dispatch('对应的action回调名')
4.可以包含异步代码(定时器、ajax等等)
const actions = {
zzz({commit,state...},data1){
commit('yyy',{data1})
}
}
mutations
1.值是一个对象,包含多个直接更新 state 的方法
2.谁能调用 mutations 中的方法?如何调用?
·在 action 中使用:commit('对应的 mutations 方法名') 触发
3 mutations 中方法的特点:不能写异步代码、只能单纯的操作 state
const mutations = {
yyy(state,{data1}){
//更新state的某个属性
}
}
getters配置项
1.想要把state中的数据加工后再使用,就可以在getters中加工数据,相当于computed计算属性。
值为一个对象,包含多个用于返回数据的函数。
//srv/store.index.js
......
const getters = {
bigSum(state){
return state.sum*10
}
}
......
// 创建并暴露store
export default new Vuex.Store({
......
getters
})
//src/component/Count.vue
......
<h3>放大十倍后的sum:{{ $store.getters.bigSum }}</h3>
......
在模板中一直使用$store.state.useValue
来使用vuex中的值会很繁琐,我们可以在计算属性computed中来接收vuex中的值。
但这样在vuex中数据很多的情况下,就要写很多这样的代码,因此我们可以使用mapState方法:用于帮助我们映射state中的数据
,getters中的数据也是如此,但是需要使用到mapGetters方法:用于帮助我们映射getters中的数据
。
mapMutations:用于帮助我们生成与mutations对话的方法,即包含$store.commit(xxx)的函数
mapActions:用于帮助我们生成与actions方法对话的方法,即包含$store.dispatch(xxx)的函数
备注:mapActions与mapMutations使用时,若需要传递参数,需要在模板中绑定事件时传递好参数,否则参数是事件对象
·src/component/Count.vue
<template>
<div>
<h2>当前求和为:{{ sum}}</h2>
<h3>我在:{{school}}学习:{{subject }}</h3>
<h3>放大十倍后的sum:{{ bigSum }}</h3>
<select v-model.number="number">
<!-- value里的值是字符串形式,使用 :value 引号里的值就当成js表达式解析了 -->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="handleAdd(number)">+</button>
<button @click="handleDown(number)">-</button>
<button @click="handleOdd(number)">当前求和为奇数再加</button>
<button @click="handleWait(number)">等一等再加</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex'
export default {
name: 'Count',
data() {
return {
number:1,
}
},
computed:{
// 对象写法
//...mapState({he:'sum',xuexiao:'school',kemu:'subject'}),
// 数组写法:生成的计算属性名和从vuex中读取出来的名是一样的
// ...mapState({sum:'sum',school:'school',subject:'subject'}),
...mapState(['sum','school','subject']),
...mapGetters(['bigSum'])
},
methods: {
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({handleAdd:'ADD',handleDown:'DOWN'}),
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)两处方法名同名时
// ...mapMutations(['ADD','DOWN']), //此时方法名handleAdd需要改为ADD,handleDown需要改为DOWN
// 借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions({handleOdd:'odd',handleWait:'wait'}),
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)两处方法名同名时
// ...mapActions(['odd','wait']), //此时方法名handleOdd需要改为odd,handleWait需要改为wait
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
案例效果:
·src/component/Count.vue
<template>
<div>
<h2>当前求和为:{{ sum}}</h2>
<h3>我在:{{school}}学习:{{subject }}</h3>
<h3>放大十倍后的sum:{{ bigSum }}</h3>
<select v-model.number="number">
<!-- value里的值是字符串形式,使用 :value 引号里的值就当成js表达式解析了 -->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="handleAdd(number)">+</button>
<button @click="handleDown(number)">-</button>
<button @click="handleOdd(number)">当前求和为奇数再加</button>
<button @click="handleWait(number)">等一等再加</button>
<h3 style="color: red;">Person组件中人名一共有:{{num}}个</h3>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex'
export default {
name: 'Count',
data() {
return {
number:1,
}
},
computed:{
// 对象写法
...mapState({he:'sum',xuexiao:'school',kemu:'subject'}),
// 数组写法:生成的计算属性名和从vuex中读取出来的名是一样的
// ...mapState({sum:'sum',school:'school',subject:'subject'}),
...mapState(['sum','school','subject']),
...mapGetters(['bigSum']),
num(){
return this.$store.state.personList.length
}
},
methods: {
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({handleAdd:'ADD',handleDown:'DOWN'}),
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)两处方法名同名时
// ...mapMutations(['ADD','DOWN']), //此时方法名handleAdd需要改为ADD,handleDown需要改为DOWN
// 借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions({handleOdd:'odd',handleWait:'wait'}),
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)两处方法名同名时
// ...mapActions(['odd','wait']), //此时方法名handleOdd需要改为odd,handleWait需要改为wait
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
·src/component/Person.vue
<template>
<div>
<input placeholder="请输入人名" v-model="name">
<button @click="addName">添加</button>
<ul><li v-for="p in personList" :key="p.id">{{ p.name }}</li></ul>
<h3 style="color:red">Count组件中的求和为:{{sum}}</h3>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex'
import {nanoid} from 'nanoid'
export default {
name: 'Person',
data() {
return {
name:''
}
},
computed:{
...mapState(['personList','sum']),
},
methods:{
addName(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('ADDPER',personObj)
this.name=''
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
·src/store/index.js
import vue from'vue'
// 导入vuex
import Vuex from 'vuex'
// 使用vuex
vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = {
// add(context,value){
// context.commit('ADD',value)
// },
// down(context,value){
// context.commit('DOWN',value)
// },
odd(context,value){
if(context.state.sum % 2 != 0){
context.commit('ADD',value)
}
},
wait(context,value){
setTimeout(()=>{
context.commit('ADD',value)
},1500)
}
}
//准备mutations对象——修改state中的数据
const mutations = {
ADD(state,value){
state.sum+=value
},
DOWN(state,value){
state.sum-=value
},
ADDPER(state,person){
state.personList.unshift(person)
}
}
const getters = {
bigSum(state){
return state.sum*10
}
}
//准备state对象——保存具体的数据
const state = {
sum:0,
school:'XUT',
subject:'前端',
personList:[{
id:1,name:'神里绫华'
}]
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
目的:让代码可以更好地维护,多种数据分类更加明确
步骤
·修改store/index.js
const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
·组件中读取state中的数据
//方式一:直接读取
this.$store.state.countAbout.sum
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
//方式一:自己直接读取
this.$store.getters['countAbout/bigSum']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
·组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('countAbout/odd',this.n)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
·组件中调用commit
//方式一:自己直接commit
this.$store.commit('countAbout/ADD',this.n)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
·案例
·src/store/index.js
import Vue from 'vue'
// 导入vuex
import Vuex from 'vuex'
import axios from 'axios'
import { nanoid } from 'nanoid'
// 使用vuex
Vue.use(Vuex)
// Count组件的配置项
const countAbout = {
namespaced: true,
actions: {
odd(context, value) {
if (context.state.sum % 2 != 0) {
context.commit('ADD', value)
}
},
wait(context, value) {
setTimeout(() => {
context.commit('ADD', value)
}, 1500)
}
},
mutations: {
ADD(state, value) {
console.log("ADD被调用了")
state.sum += value
},
DOWN(state, value) {
state.sum -= value
},
},
getters: {
bigSum(state) {
return state.sum * 10
}
},
state: {
sum: 0,
school: 'XUT',
subject: '前端',
}
}
// Person组件的配置项
const personAbout = {
namespaced: true,
actions: {
addPerNing(context, value) {
if (value.name.indexOf('凝') === 0) {
context.commit('ADDPER', value)
} else {
alert('姓氏不为凝,添加不了!')
}
},
addPerServe(context){
axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(res=>{
context.commit('ADDPER',{id:nanoid(),name:res.data})
},
error =>{
alert(error.message)
})
}
},
mutations: {
ADDPER(state, person) {
state.personList.unshift(person)
}
},
getters: {
firstPersonName(state) {
return state.personList[0].name
}
},
state: {
personList: [{
id: 1, name: '神里绫华'
}]
}
}
// 创建并暴露store
export default new Vuex.Store({
modules: {
countAbout,
personAbout,
}
})
·src/component/Count.vue
<template>
<div>
<h2>当前求和为:{{sum}}</h2>
<h3>我在:{{school}}学习:{{subject }}</h3>
<h3>放大十倍后的sum:{{ bigSum }}</h3>
<select v-model.number="number">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="handleAdd(number)">+</button>
<button @click="handleDown(number)">-</button>
<button @click="handleOdd(number)">当前求和为奇数再加</button>
<button @click="handleWait(number)">等一等再加</button>
<h3 style="color: red;">Person组件中人名一共有:{{personList.length}}个</h3>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex'
export default {
name: 'Count',
data() {
return {
number:1,
}
},
computed:{
// 数组写法:生成的计算属性名和从vuex中读取出来的名是一样的
...mapState('countAbout',['sum','school','subject']),
...mapState('personAbout',['personList']),
...mapGetters('countAbout',['bigSum']),
},
methods: {
...mapMutations('countAbout',{handleAdd:'ADD',handleDown:'DOWN'}),
...mapActions('countAbout',{handleOdd:'odd',handleWait:'wait'}),
},
mounted(){
console.log('count',this.$store)
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
·src/compoonent/Person.vue
<template>
<div>
<input placeholder="请输入人名" v-model="name">
<button @click="addName">添加</button>
<button @click="addNing">添加姓氏为凝的人名</button>
<button @click="addServe">从服务器添加人名</button>
<ul><li v-for="p in personList" :key="p.id">{{ p.name }}</li></ul>
<h3>列表中第一个人名:{{firstPersonName}}</h3>
<h3 style="color:red">Count组件中的求和为:{{sum}}</h3>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name: 'Person',
data() {
return {
name:''
}
},
computed:{
sum(){
return this.$store.state.countAbout.sum
},
personList(){
return this.$store.state.personAbout.personList
},
firstPersonName(){
return this.$store.getters['personAbout/firstPersonName']
}
},
methods:{
addName(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('personAbout/ADDPER',personObj)
this.name=''
},
addNing(){
const personNing = {id:nanoid(),name:this.name}
this.$store.dispatch('personAbout/addPerNing',personNing)
this.name = ''
},
addServe(){
this.$store.dispatch('personAbout/addPerServe')
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button {
margin-left: 5px;
}
</style>
vue-router4对应vue3
vue-router3对应vue2
vue 的一个插件库,专门用来实现 SPA 应用
理解SPA应用
后端路由
1)理解:value 是 function, 用于处理客户端提交的请求。
2 )工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
前端路由
1) 理解:value 是 component,用于展示页面内容。
2)工作过程:当浏览器的路径改变时, 对应的组件就会显示。
1.安装路由vue-router
npm i vue-router
2.导入并应用插件
import VueRouter from 'vue-router'
Vue.use(VueRouter)
3.编写router配置项
//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
//暴露router
export default router
4.在main.js中引入定义的router文件
import routers from './router'
new Vue({ // 将App组件放入容器中 render: h => h(App), router:routers }).$mount('#app')
5.实现点击导航栏不同的栏目来切换 active-class可配置高亮样式
Vue中借助router-link标签实现路由的切换
router-link的replace属性
push
和replace
,push
是追加历史记录replace
是替换当前记录。push
replace
模式:News
router-view指定组件的展示位置
小案例
src/router/index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},{
path:'/home',
component:Home
}
]
})
main.js
//脚手架已安装vue,不用自己安装vue
import Vue from 'vue'
//引入app组件,它是所有组件的父组件
import App from './App.vue'
import VueRouter from 'vue-router'
import routers from './router'
Vue.config.productionTip = false
Vue.use(VueRouter) //应用插件
// 创建vue实例对象——vm
new Vue({
// 将App组件放入容器中
render: h => h(App),
router:routers
}).$mount('#app')
App.vue
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- 原始html中我们使用a标签实现页面的跳转 -->
<!-- <a class="list-group-item active" href="./about.html">About</a> -->
<!-- <a class="list-group-item" href="./home.html">Home</a> -->
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<br>
<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',
components: {
}
}
</script>
<style>
</style>
注意点
1.路由组件通常存放在pages
文件夹,一般组件通常存放在components
文件夹。
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的$route
属性,里面存储着自己的路由信息。
4.整个应用只有一个router,可以通过组件的$router
属性获取到。
// 该文件专门用于创建整个应用的路由器
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'
export default new VueRouter({
routes: [
{
path: '/about',
component: About,
},{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
path:'message',
component:Message,
children:[
{
path:'detail',
component:Detail
}
]
}
]
}
]
})
src/pages/Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/Message/Detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path: '/home/message/detail',
query: {
id: m.id,
title: m.title
}
}">
{{ m.title }}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data() {
return {
messageList: [
{ id: '001', title: '消息001' }, { id: '002', title: '消息002' }, { id: '003', title: '消息003' }]
}
}
}
</script>
src/pages/Detail.vue
<template>
<ul>
<li>编号:{{ $route.query.id }}</li>
<li>内容:{{ $route.query.title }}</li>
</ul>
</template>
<script>
export default {
name:'Detail',
mounted(){
console.log('Detail',this)
}
}
</script>
命名路由
在编码时path太长,可以使用name来定位。
使用
//给路由命名
export default new VueRouter({
routes: [
{
name:'guanyu',
path: '/about',
component: About,
},{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
path:'message',
component:Message,
children:[
{
name:'content',
path:'detail',
component:Detail
}
]
}
]
}
]
})
简化跳转
//不简化写法
<router-link class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
//简化写法
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
name: 'content',
query: {
id: m.id,
title: m.title
}
}">
{{ m.title }}
</router-link>
配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/001/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
问题1
:如何配置params可传可不传:
如果路由中配置了params的占位,但是却没有传递参数,这样就会造成路径出现问题。解决方案就是在占位的时候带一个问号?,代表该params参数可传可不传。path:‘detail/:id?’
接收参数
$route.params.id
$route.params.title
作用:让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
props(route){
return {
id:route.params.id,
title:route.params.title
}
}
}
src/router/index.js
// 该文件专门用于创建整个应用的路由器
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'
import Details from '../pages/Details'
export default new VueRouter({
routes: [
{
name: 'guanyu',
path: '/about',
component: About,
}, {
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News,
children: [{
path: 'details',
component: Details,
// props:{id:1,title:'你好'}
props(route) {
return {
nid: route.query.nid,
ntitle: route.query.ntitle
}
}
}]
},
{
path: 'message',
component: Message,
children: [
{
name: 'content',
path: 'detail/:id/:title',
component: Detail,
// props:true
props(route) {
return {
id: route.params.id,
title: route.params.title
}
}
}
]
}
]
}
]
})
src/pages/Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<router-link :to="`/home/message/Detail/${m.id}/${m.title}`">{{ m.title }}</router-link>
<!-- 跳转路由并携带query参数,to的对象写法 -->
<!-- <router-link :to="{
name: 'content',
params: {
id: m.id,
title: m.title
}
}">
{{ m.title }}
</router-link> -->
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data() {
return {
messageList: [
{ id: '001', title: '消息001' }, { id: '002', title: '消息002' }, { id: '003', title: '消息003' }]
}
}
}
</script>
src/pages/Detail.vue
<template>
<ul>
<li>编号:{{ id }}</li>
<li>内容:{{ title }}</li>
</ul>
</template>
<script>
export default {
name:'Detail',
props:['id','title'],
mounted(){
console.log('Detail',this)
}
}
</script>
1.作用:让不展示的路由组件保持挂载,不被销毁。
2.具体编码:
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<keep-alive :include="[componentA,componentB]">
<router-view></router-view>
</keep-alive>
1.作用:不借助
实现路由跳转,让路由跳转更加灵活
2.编码:
<button @click="showPush">push前进</button>
//$router的两个API
showPush(){
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
}) //配置项和里的是一样的
}
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退,取决于传递进来的参数,正数为前进多少步,负数为后退多少步
1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
1.activated
路由组件被激活时触发。
2.deactivated
路由组件失活时触发。
1.作用:对路由进行权限控制(比如等径淘宝网站,未登录时查看不了个人中心)
2.分类:全局守卫、独享守卫、组件内守卫
3.route身上的meta 属性:路由元信息(程序员自定义的一些信息)
全局守卫:(分为全局前置路由守卫和全局后置路由守卫)
const router = new VueRouter({
routes:[ {
name: 'guanyu',
path: '/about',
component: About,
meta:{isAuth:true,title:'关于'}
},
{......}]
})
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
独享守卫(某一个路由所独享的)
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
}
}else{
next()
}
}
组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
1.对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2.hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
3. hash模式:
1).地址中永远带着#号,不美观 。
2).若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3).兼容性较好。
4.history模式:
1).地址干净,美观 。
2).兼容性和hash模式相比略差。
3).应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。(node.js中可以使用connect-histpry-api-fallback中间件来解决该问题,他可以区别前端路径和后端路径)
移动端常用UI组件库
(Vant)[https://youzan.github.io/vant]
(Cube UI)[https://didi.github.io/cube-ui]
(Mint UI)[http://mint-ui.github.io]
PC端常用UI组件库
(Element UI)[https://element.eleme.cn]
(IView UI)[https://www.iviewui.com]