vue基础学习 Vuex和Vue路由——part02

01Vuex

1.介绍

理解Vuex

概念:专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
简言之,Vuex可以实现多组件共享数据。
在这里插入图片描述

什么时候使用Vuex

  1. 多个组件依赖于同一状态
  2. 来自不同组件的行为需要变更同一状态

Vuex工作原理图

在这里插入图片描述

2.求和案例

2.1纯vue编写

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>

2.2搭建Vuex环境

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')

在这里插入图片描述
可以看到组件身上已经可以拿到store了
在这里插入图片描述

2.3使用Vuex编写

·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,
})

3.Vuex核心概念和API

3.1state、actions、mutations、getters

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>
......

在这里插入图片描述

3.2四个map方法的介绍与使用

mapState和mapGetters

在这里插入图片描述
在模板中一直使用$store.state.useValue来使用vuex中的值会很繁琐,我们可以在计算属性computed中来接收vuex中的值。
在这里插入图片描述
但这样在vuex中数据很多的情况下,就要写很多这样的代码,因此我们可以使用mapState方法:用于帮助我们映射state中的数据,getters中的数据也是如此,但是需要使用到mapGetters方法:用于帮助我们映射getters中的数据
在这里插入图片描述

mapMutations与mapActions

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> 

3.3多组件共享数据

案例效果:
在这里插入图片描述
·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
})

3.4模块化以及命名空间

目的:让代码可以更好地维护,多种数据分类更加明确
在这里插入图片描述
步骤
·修改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']),

·组件中读取geteers中的数据
在这里插入图片描述

//方式一:自己直接读取
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>  

02Vue路由

vue-router4对应vue3
vue-router3对应vue2

2.1vue-router的理解

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

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

2.2路由的理解

2.2.1什么是路由

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

2.2.2路由分类

后端路由
1)理解:value 是 function, 用于处理客户端提交的请求。
2 )工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
前端路由
1) 理解:value 是 component,用于展示页面内容。
2)工作过程:当浏览器的路径改变时, 对应的组件就会显示。

2.3路由的基本使用

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 active-class="active" to="/about">About</router-link>
<router-link active-class="active" to="/home">Home</router-link>
router-link的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplace
    push是追加历史记录
    replace是替换当前记录。
    路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>
    6.指定组件展示的位置

router-view指定组件的展示位置
<router-view></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属性获取到。

2.4多级路由

在这里插入图片描述
在这里插入图片描述

2.5参数

2.5.1路由的query参数

传递参数
在这里插入图片描述
接收参数
在这里插入图片描述
在这里插入图片描述
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'
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>

2.5.2路由的params参数

配置路由,声明接收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

2.5.3路由的props配置

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

{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	// props:true
	
	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props(route){
		return {
			id:route.query.id,
			title:route.query.title
		}
	}
	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>

2.5.4缓存路由组件

1.作用:让不展示的路由组件保持挂载,不被销毁。
2.具体编码:

<keep-alive include="News"> 
    <router-view></router-view>
</keep-alive>

<keep-alive :include="[componentA,componentB]"> 
    <router-view></router-view>
</keep-alive>

2.6编程时路由导航

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

<button @click="showPush">push前进</button>
//$router的两个API
showPush(){
this.$router.push({
	name:'xiangqing',
		params:{
			id:xxx,
			title:xxx
		}
  })  //配置项和<router-link>里的是一样的
}


this.$router.replace({
	name:'xiangqing',
		params:{
			id:xxx,
			title:xxx
		}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退,取决于传递进来的参数,正数为前进多少步,负数为后退多少步

2.7两个新的生命周期钩子

1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
1.activated路由组件被激活时触发。
2.deactivated路由组件失活时触发。

2.8路由守卫

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) {
}

2.9路由器的两种工作模式

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中间件来解决该问题,他可以区别前端路径和后端路径)

03Vue UI组件库

移动端常用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]

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值