4.Vue中的Ajax
4.1. Vue脚手架配置代理
本案例需要下载axios库:npm install axios
vue.config.js
:
module.exports = { pages: { index: { entry: 'src/main.js', }, }, lintOnSave:false,
devServer: { proxy: { '/jojo': { target: 'http://localhost:5000', pathRewrite:{'^/jojo':''}, }, '/atguigu': { target: 'http://localhost:5001', pathRewrite:{'^/atguigu':''}, } } } }
|
src/App.vue
:
<template> <div id="root"> <button @click="getStudents">获取学生信息</button><br/> <button @click="getCars">获取汽车信息</button> </div> </template>
<script> import axios from 'axios' export default { name:'App', methods: { getStudents(){ axios.get('http://localhost:8080/jojo/students').then( response => { console.log('请求成功了',response.data) }, error => { console.log('请求失败了',error.message) } ) }, getCars(){ axios.get('http://localhost:8080/atguigu/cars').then( response => { console.log('请求成功了',response.data) }, error => { console.log('请求失败了',error.message) } ) } } } </script>
|
效果:

总结:
vue脚手架配置代理服务器:
4.2. GitHub用户搜索案例
public/index.html
:
<!DOCTYPE html> <html lang=""> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <div id="app"></div> </body> </html>
|
src/main.js
:
import Vue from 'vue' import App from './App.vue'
Vue.config.productionTip = false
new Vue({ el:"#app", render: h => h(App), beforeCreate(){ Vue.prototype.$bus = this } })
|
src/App.vue
:
<template> <div class="container"> <Search/> <List/> </div> </template>
<script> import Search from './components/Search.vue' import List from './components/List.vue'
export default { name:'App', components:{Search,List}, } </script>
|
src/components/Search.vue
:
<template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search" v-model="keyWord"/> <button @click="getUsers">Search</button> </div> </section> </template>
<script> import axios from 'axios' export default { name:'Search', data() { return { keyWord:'' } }, methods: { getUsers(){ //请求前更新List的数据 this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false}) axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功了') //请求成功后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error => { //请求后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } ) } } } </script>
|
src/components/List.vue
:
<template> <div class="row"> <!-- 展示用户列表 --> <div class="card" v-show="info.users.length" v-for="user in info.users" :key="user.id"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style='width: 100px'/> </a> <h4 class="card-title">{{user.login}}</h4> </div> <!-- 展示欢迎词 --> <h1 v-show="info.isFirst">欢迎使用!</h1> <!-- 展示加载中 --> <h1 v-show="info.isLoading">加载中...</h1> <!-- 展示错误信息 --> <h1 v-show="info.errMsg">{{errMsg}}</h1> </div> </template>
<script> export default { name:'List', data() { return { info:{ isFirst:true, isLoading:false, errMsg:'', users:[] } } }, mounted(){ this.$bus.$on('updateListData',(dataObj)=>{ //动态合并两个对象的属性 this.info = {...this.info,...dataObj} }) }, beforeDestroy(){ this.$bus.$off('updateListData') } } </script>
<style scoped> .album { min-height: 50rem; /* Can be removed; just added for demo purposes */ padding-top: 3rem; padding-bottom: 3rem; background-color: #f7f7f7; }
.card { float: left; width: 33.333%; padding: .75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; }
.card > img { margin-bottom: .75rem; border-radius: 100px; }
.card-text { font-size: 85%; } </style>
|
效果:

4.3. vue-resource
下载 vue-resource库:npm i vue-resource
src/main.js
:
import Vue from 'vue' import App from './App.vue'
import vueResource from 'vue-resource'
Vue.config.productionTip = false
Vue.use(vueResource)
new Vue({ el:"#app", render: h => h(App), beforeCreate(){ Vue.prototype.$bus = this } })
|
src/App.vue
:
<template> <div class="container"> <Search/> <List/> </div> </template>
<script> import Search from './components/Search.vue' import List from './components/List.vue'
export default { name:'App', components:{Search,List}, } </script>
|
src/components/Search.vue
:
<template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search" v-model="keyWord"/> <button @click="getUsers">Search</button> </div> </section> </template>
<script> export default { name:'Search', data() { return { keyWord:'' } }, methods: { getUsers(){ //请求前更新List的数据 this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false}) this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功了') //请求成功后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error => { //请求后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } ) } } } </script>
|
src/components/List.vue
:
<template> <div class="row"> <!-- 展示用户列表 --> <div class="card" v-show="info.users.length" v-for="user in info.users" :key="user.id"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style='width: 100px'/> </a> <h4 class="card-title">{{user.login}}</h4> </div> <!-- 展示欢迎词 --> <h1 v-show="info.isFirst">欢迎使用!</h1> <!-- 展示加载中 --> <h1 v-show="info.isLoading">加载中...</h1> <!-- 展示错误信息 --> <h1 v-show="info.errMsg">{{errMsg}}</h1> </div> </template>
<script> export default { name:'List', data() { return { info:{ isFirst:true, isLoading:false, errMsg:'', users:[] } } }, mounted(){ this.$bus.$on('updateListData',(dataObj)=>{ this.info = {...this.info,...dataObj} }) }, beforeDestroy(){ this.$bus.$off('updateListData') } } </script>
<style scoped> .album { min-height: 50rem; /* Can be removed; just added for demo purposes */ padding-top: 3rem; padding-bottom: 3rem; background-color: #f7f7f7; }
.card { float: left; width: 33.333%; padding: .75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; }
.card > img { margin-bottom: .75rem; border-radius: 100px; }
.card-text { font-size: 85%; } </style>
|
总结:
vue项目常用的两个Ajax库:
- axios:通用的Ajax请求库,官方推荐,效率高
- vue-resource:vue插件库,vue 1.x使用广泛,官方已不维护
4.4. slot插槽
4.4.1. 默认插槽
src/App.vue
:
<template> <div class="container"> <Category title="美食" > <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""> </Category>
<Category title="游戏" > <ul> <li v-for="(g,index) in games" :key="index">{{g}}</li> </ul> </Category>
<Category title="电影"> <video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video> </Category> </div> </template>
<script> import Category from './components/Category' export default { name:'App', components:{Category}, data() { return { games:['植物大战僵尸','红色警戒','空洞骑士','王国'] } }, } </script>
<style scoped> .container{ display: flex; justify-content: space-around; } </style>
|
src/components/Category.vue
:
<template> <div class="category"> <h3>{{title}}分类</h3> <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --> <slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot> </div> </template>
<script> export default { name:'Category', props:['title'] } </script>
<style scoped> .category{ background-color: skyblue; width: 200px; height: 300px; } h3{ text-align: center; background-color: orange; } video{ width: 100%; } img{ width: 100%; } </style>
|

4.4.2. 具名插槽
src/App.vue
:
<template> <div class="container"> <Category title="美食" > <img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt=""> <a slot="footer" href="http://www.atguigu.com">更多美食</a> </Category>
<Category title="游戏" > <ul slot="center"> <li v-for="(g,index) in games" :key="index">{{g}}</li> </ul> <div class="foot" slot="footer"> <a href="http://www.atguigu.com">单机游戏</a> <a href="http://www.atguigu.com">网络游戏</a> </div> </Category>
<Category title="电影"> <video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video> <template v-slot:footer> <div class="foot"> <a href="http://www.atguigu.com">经典</a> <a href="http://www.atguigu.com">热门</a> <a href="http://www.atguigu.com">推荐</a> </div> <h4>欢迎前来观影</h4> </template> </Category> </div> </template>
<script> import Category from './components/Category' export default { name:'App', components:{Category}, data() { return { games:['植物大战僵尸','红色警戒','空洞骑士','王国'] } }, } </script>
<style> .container,.foot{ display: flex; justify-content: space-around; } h4{ text-align: center; } </style>
|
src/components/Category.vue
:
<template> <div class="category"> <h3>{{title}}分类</h3> <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --> <slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot> <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot> </div> </template>
<script> export default { name:'Category', props:['title'] } </script>
<style scoped> .category{ background-color: skyblue; width: 200px; height: 300px; } h3{ text-align: center; background-color: orange; } video{ width: 100%; } img{ width: 100%; } </style>
|

4.4.3. 作用域插槽
src/App.vue
:
<template> <div class="container"> <Category title="游戏" > <template scope="taichi"> <ul> <li v-for="(g,index) in taichi.games" :key="index">{{g}}</li> </ul> </template> </Category>
<Category title="游戏" > <template scope="taichi"> <ol> <li v-for="(g,index) in taichi.games" :key="index" style="color: red;">{{g}}</li> </ol> </template> </Category>
<Category title="游戏" > <template scope="taichi"> <h4 v-for="(g,index) in taichi.games" :key="index">{{g}}</h4> </template> </Category> </div> </template>
<script> import Category from './components/Category' export default { name:'App', components:{Category} } </script>
<style> .container,.foot{ display: flex; justify-content: space-around; } h4{ text-align: center; } </style>
|
src/components/Category.vue
:
<template> <div class="category"> <h3>{{title}}分类</h3> <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --> <slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot> </div> </template>
<script> export default { name:'Category', props:['title'], data() { return { games:['植物大战僵尸','红色警戒','空洞骑士','王国'] } }, } </script>
<style scoped> .category{ background-color: skyblue; width: 200px; height: 300px; } h3{ text-align: center; background-color: orange; } video{ width: 100%; } img{ width: 100%; } </style>
|

总结:
插槽:
作用:让父组件可以向子组件==指定位置插入html结构==,也是一种组件间通信的方式,适用于==父组件== > ==子组件==
分类:默认插槽、具名插槽、作用域插槽
使用方式:
默认插槽:
父组件中: <Category> <div>html结构1</div> </Category> 子组件中: <template> <div> <slot>插槽默认内容...</slot> </div> </template>
|
具名插槽:
父组件中: <Category> <template slot="center"> <div>html结构1</div> </template>
<template v-slot:footer> <div>html结构2</div> </template> </Category> 子组件中: <template> <div> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
|
作用域插槽:
理解:==数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。==(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
具体编码:
父组件中: <Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category>
<Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
|
5.Vuex
5.1. 理解Vuex
5.1.1. Vuex是什么
- 概念:专门在 Vue 中实现集中式状态(数据)管理的一个 **Vue 插件**,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
- Vuex Github地址
- Vuex 官网


5.1.2. 什么时候使用Vuex
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
5.1.3. Vuex工作原理图

5.2. 求和案例
下载vuex
:npm i vuex
5.2.1. 使用纯vue编写
src/App.vue
:
<template> <div class="container"> <Count/> </div> </template> ue <script> import Count from './components/Count' export default { name:'App', components:{Count} } </script>
|
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{sum}}</h1> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementOdd">当前求和为奇数再加</button> <button @click="incrementWait">等一等再加</button> </div> </template>
<script> export default { name:'Count', data() { return { n:1, //用户选择的数字 sum:0 //当前的和 } }, methods: { increment(){ this.sum += this.n }, decrement(){ this.sum -= this.n }, incrementOdd(){ if(this.sum % 2){ this.sum += this.n } }, incrementWait(){ setTimeout(()=>{ this.sum += this.n },500) }, }, } </script>
<style> button{ margin-left: 5px; } </style>
|
效果:

5.2.2. 搭建Vuex环境
下载 Vuex:npm i vuex
创建src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {}
const mutations = {}
const state = {}
export default new Vuex.Store({ actions, mutations, state })
|
在src/main.js
中创建 vm 时传入store
配置项:
import Vue from 'vue' import App from './App.vue' import Vuex from 'vuex' import store from './store'
Vue.config.productionTip = false
Vue.use(Vuex)
new Vue({ el:"#app", render: h => h(App), store })
|
5.2.3. 使用Vuex编写
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{$store.state.sum}}</h1> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementOdd">当前求和为奇数再加</button> <button @click="incrementWait">等一等再加</button> </div> </template>
<script> export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { increment(){ this.$store.commit('ADD',this.n) }, decrement(){ this.$store.commit('SUBTRACT',this.n) }, incrementOdd(){ this.$store.dispatch('addOdd',this.n) }, incrementWait(){ this.$store.dispatch('addWait',this.n) }, }, } </script>
<style> button{ margin-left: 5px; } </style>
|
src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) }, }
const mutations = { ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value } }
const state = { sum:0 }
export default new Vuex.Store({ actions, mutations, state })
|
总结:
Vuex的基本使用:
初始化数据state
,配置actions
、mutations
,操作文件store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { jia(context,value){ context.commit('JIA',value) }, }
const mutations = { JIA(state,value){ state.sum += value } }
const state = { sum:0 }
export default new Vuex.Store({ actions, mutations, state, })
|
组件中读取vuex中的数据:$store.state.sum
组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)
或 $store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions
,即不写dispatch
,直接编写commit
5.3. getters配置项
src/Count.vue
:
<template> <div> <h1>当前求和为:{{$store.state.sum}}</h1> <h3>当前求和的10倍为:{{$store.getters.bigSum}}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementOdd">当前求和为奇数再加</button> <button @click="incrementWait">等一等再加</button> </div> </template>
<script> export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { increment(){ this.$store.commit('ADD',this.n) }, decrement(){ this.$store.commit('SUBTRACT',this.n) }, incrementOdd(){ this.$store.dispatch('addOdd',this.n) }, incrementWait(){ this.$store.dispatch('addWait',this.n) }, }, } </script>
<style> button{ margin-left: 5px; } </style>
|
src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) }, }
const mutations = { ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value } }
const state = { sum:0 }
const getters = { bigSum(){ return state.sum * 10 } }
export default new Vuex.Store({ actions, mutations, state, getters })
|
效果:

总结:
getters
配置项的使用:
概念:当state
中的数据需要经过加工后再使用时,可以使用getters
加工
在store.js
中追加getters
配置
... const getters = { bigSum(state){ return state.sum * 10 } }
export default new Vuex.Store({ ... getters })
|
组件中读取数据:$store.getters.bigSum
5.4. 四个map方法的使用
5.4.1. mapState与mapGetters
src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) }, }
const mutations = { ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value } }
const state = { sum:0, name:'JOJO', school:'尚硅谷', }
const getters = { bigSum(){ return state.sum * 10 } }
export default new Vuex.Store({ actions, mutations, state, getters })
|
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{sum}}</h1> <h3>当前求和的10倍为:{{bigSum}}</h3> <h3>我是{{name}},我在{{school}}学习</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementOdd">当前求和为奇数再加</button> <button @click="incrementWait">等一等再加</button> </div> </template>
<script> import {mapState,mapGetters} from 'vuex'
export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { increment(){ this.$store.commit('ADD',this.n) }, decrement(){ this.$store.commit('SUBTRACT',this.n) }, incrementOdd(){ this.$store.dispatch('addOdd',this.n) }, incrementWait(){ this.$store.dispatch('addWait',this.n) }, }, computed:{ // 借助mapState生成计算属性(数组写法) // ...mapState(['sum','school','name']), // 借助mapState生成计算属性(对象写法) ...mapState({sum:'sum',school:'school',name:'name'}),
...mapGetters(['bigSum']) } } </script>
<style> button{ margin-left: 5px; } </style>
|
效果:

总结:
mapState方法:用于帮助我们映射state
中的数据
computed: { ...mapState({sum:'sum',school:'school',subject:'subject'}), ...mapState(['sum','school','subject']), },
|
mapGetters方法:用于帮助我们映射getters
中的数据
computed: { ...mapGetters({bigSum:'bigSum'}),
...mapGetters(['bigSum']) },
|
5.4.2. mapActions与mapMutations
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{sum}}</h1> <h3>当前求和的10倍为:{{bigSum}}</h3> <h3>我是{{name}},我在{{school}}学习</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template>
<script> import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { // 借助mapActions生成:increment、decrement(对象形式) ...mapMutations({increment:'ADD',decrement:'SUBTRACT'}),
// 借助mapActions生成:incrementOdd、incrementWait(对象形式) ...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'}) }, computed:{ // 借助mapState生成计算属性(数组写法) // ...mapState(['sum','school','name']), // 借助mapState生成计算属性(对象写法) ...mapState({sum:'sum',school:'school',name:'name'}),
...mapGetters(['bigSum']) } } </script>
<style> button{ margin-left: 5px; } </style>
|
总结:
mapActions方法:用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数
methods:{ ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
...mapActions(['jiaOdd','jiaWait']) }
|
mapMutations方法:用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数
methods:{ ...mapMutations({increment:'JIA',decrement:'JIAN'}), ...mapMutations(['JIA','JIAN']), }
|
备注:mapActions
与mapMutations
使用时,若需要传递参数,则需要在模板中绑定事件时传递好参数,否则参数是事件对象
5.5. 多组件共享数据
src/App.vue
:
<template> <div class="container"> <Count/> <hr/> <Person/> </div> </template>
<script> import Count from './components/Count' import Person from './components/Person'
export default { name:'App', components:{Count,Person} } </script>
|
src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) }, }
const mutations = { ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value }, ADD_PERSON(state,value){ console.log('mutations中的ADD_PERSON被调用了') state.personList.unshift(value) } }
const state = { sum:0, name:'JOJO', school:'尚硅谷', personList:[ {id:'001',name:'JOJO'} ] }
const getters = { bigSum(){ return state.sum * 10 } }
export default new Vuex.Store({ actions, mutations, state, getters })
|
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{sum}}</h1> <h3>当前求和的10倍为:{{bigSum}}</h3> <h3>我是{{name}},我在{{school}}学习</h3> <h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template>
<script> import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { ...mapMutations({increment:'ADD',decrement:'SUBTRACT'}), ...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'}) }, computed:{ ...mapState(['sum','school','name','personList']),, ...mapGetters(['bigSum']) } } </script>
<style> button{ margin-left: 5px; } </style>
|
src/components/Person.vue
:
<template> <div> <h1>人员列表</h1> <h3 style="color:red">Count组件求和为:{{sum}}</h3> <input type="text" placeholder="请输入名字" v-model="name"> <button @click="add">添加</button> <ul> <li v-for="p in personList" :key="p.id">{{p.name}}</li> </ul> </div> </template>
<script> import {nanoid} from 'nanoid' export default { name:'Person', data() { return { name:'' } }, computed:{ personList(){ return this.$store.state.personList }, sum(){ return this.$store.state.sum } }, methods: { add(){ const personObj = {id:nanoid(),name:this.name} this.$store.commit('ADD_PERSON',personObj) this.name = '' } } } </script>
|
效果:

5.6. 模块化+命名空间
src/store/index.js
:
import Vue from 'vue'
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
Vue.use(Vuex)
export default new Vuex.Store({ modules:{ countAbout:countOptions, personAbout:personOptions, } })
|
src/store/count.js
:
export default{ namespaced:true, actions:{ addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) } }, mutations:{ ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value } }, state:{ sum:0, name:'JOJO', school:'尚硅谷', }, getters:{ bigSum(state){ return state.sum * 10 } } }
|
src/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('http://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){ console.log('mutations中的ADD_PERSON被调用了') state.personList.unshift(value) } }, state:{ personList:[ {id:'001',name:'JOJO'} ] }, getters:{ firstPersonName(state){ return state.personList[0].name } } }
|
src/components/Count.vue
:
<template> <div> <h1>当前求和为:{{sum}}</h1> <h3>当前求和的10倍为:{{bigSum}}</h3> <h3>我是{{name}},我在{{school}}学习</h3> <h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template>
<script> import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { ...mapMutations('countAbout',{increment:'ADD',decrement:'SUBTRACT'}), ...mapActions('countAbout',{incrementOdd:'addOdd',incrementWait:'addWait'}) }, computed:{ ...mapState('countAbout',['sum','school','name']), ...mapGetters('countAbout',['bigSum']), ...mapState('personAbout',['personList']) } } </script>
<style> button{ margin-left: 5px; } </style>
|
src/components/Person.vue
:
<template> <div> <h1>人员列表</h1> <h3 style="color:red">Count组件求和为:{{sum}}</h3> <h3>列表中第一个人的名字是:{{firstPersonName}}</h3> <input type="text" placeholder="请输入名字" v-model="name"> <button @click="add">添加</button> <button @click="addWang">添加一个姓王的人</button> <button @click="addPerson">随机添加一个人</button> <ul> <li v-for="p in personList" :key="p.id">{{p.name}}</li> </ul> </div> </template>
<script> import {nanoid} from 'nanoid' export default { name:'Person', data() { return { name:'' } }, 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 = '' }, addPerson(){ this.$store.dispatch('personAbout/addPersonServer') } }, } </script>
|
效果:

总结:
模块化+命名空间:
目的:让代码更好维护,让多种数据分类更加明确
修改store.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.personAbout.list
...mapState('countAbout',['sum','school','subject']),
|
开启命名空间后,组件中读取getters
数据:
this.$store.getters['personAbout/firstPersonName']
...mapGetters('countAbout',['bigSum'])
|
开启命名空间后,组件中调用dispatch
:
this.$store.dispatch('personAbout/addPersonWang',person)
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
|
开启命名空间后,组件中调用commit
:
this.$store.commit('personAbout/ADD_PERSON',person)
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
|
6.Vue Router路由管理
6.1 相关理解
6.1.1 vue-router的理解
- vue 的一个插件库,专门用来实现==SPA 应用==
6.1.2 对SPA应用的理解
- 单页 Web 应用(single page web application,SPA)
- 整个应用==只有一个完整的页面==
- 点击页面中的导航链接==不会刷新==页面,只会做页面的==局部更新==
- 数据需要通过ajax请求获取

6.1.3 路由的理解
- 什么是路由?
- 一个路由就是一组映射关系(key - value)
- key 为路径,value 可能是 function 或 componen
- 路由分类
- 后端路由:
- 理解:value 是 function,用于处理客户端提交的请求
- 工作过程:==服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据==
- 前端路由:
- 理解:value 是 component,用于展示页面内容
- 工作过程:==当浏览器的路径改变时,对应的组件就会显示==
6.2 基本路由
下载vue-router
:npm i vue-router
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../components/Home' import About from '../components/About'
export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
|
src/main.js
:
import Vue from 'vue' import App from './App.vue' import VueRouter from 'vue-router' import router from './router'
Vue.config.productionTip = false Vue.use(VueRouter)
new Vue({ el:"#app", render: h => h(App), router })
|
src/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> <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>
|
src/components/Home.vue
:
<template> <h2>我是Home组件的内容</h2> </template>
<script> export default { name:'Home' } </script>
|
src/components/About.vue
:
<template> <h2>我是About组件的内容</h2> </template>
<script> export default { name:'About' } </script>
|
总结:
安装vue-router
,命令:npm i vue-router
应用插件:Vue.use(VueRouter)
编写router配置项:
import VueRouter from 'vue-router'
import About from '../components/About' import Home from '../components/Home'
const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
export default router
|
实现切换(active-class
可配置高亮样式):
<router-link active-class="active" to="/about">About</router-link>
|
指定展示位:<router-view></router-view>
6.3. 几个注意事项
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹
- 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息
- 整个应用只有一个router,可以通过组件的
$router
属性获取到
修改上一节案例:
src/pages/Home.vue
:
<template> <h2>我是Home组件的内容</h2> </template>
<script> export default { name:'Home' } </script>
|
src/pages/About.vue
:
<template> <h2>我是About组件的内容</h2> </template>
<script> export default { name:'About' } </script>
|
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About'
export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
|
src/components/Banner.vue
:
<template> <div class="col-xs-offset-2 col-xs-8"> <div class="page-header"><h2>Vue Router Demo</h2></div> </div> </template>
<script> export default { name:'Banner' } </script>
|
src/App.vue
:
<template> <div> <div class="row"> <Banner/> </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> <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> import Banner from './components/Banner.vue' export default { name:'App', components:{Banner} } </script>
|
6.4. 多级路由
src/pages/Home.vue
:
<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> <router-view></router-view> </div> </div> </template>
<script> export default { name:'Home' } </script>
|
src/pages/News.vue
:
<template> <ul> <li>news001</li> <li>news002</li> <li>news003</li> </ul> </template>
<script> export default { name:'News' } </script>
|
src/pages/Message.vue
:
<template> <ul> <li> <a href="/message1">message001</a> </li> <li> <a href="/message2">message002</a> </li> <li> <a href="/message/3">message003</a> </li> </ul> </template>
<script> export default { name:'News' } </script>
|
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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 } ] } ] })
|
效果:

总结:
配置路由规则,使用children配置项:
routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message } ] } ]
|
跳转(要写完整路径):<router-link to="/home/news">News</router-link>
6.5. 路由的query参数
src/router.index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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/Detail.vue
:
<template> <ul> <li>消息编号:{{$route.query.id}}</li> <li>消息标题:{{$route.query.title}}</li> </ul> </template>
<script> export default { name:'Detail' } </script>
|
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> <hr/> <router-view></router-view> </div> </template>
<script> export default { name:'News', data(){ return{ messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'} ] } } } </script>
|
效果:

总结:
传递参数:
<!-- 跳转并携带query参数,to的字符串写法 --> <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> <!-- 跳转并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }">跳转</router-link>
|
接收参数:
$route.query.id $route.query.title
|
6.6. 命名路由
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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:[ { name:'xiangqing', 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="{ //使用name进行跳转 name:'xiangqing', query:{ id:m.id, title:m.title } }"> {{m.title}} </router-link> </li> </ul> <hr/> <router-view></router-view> </div> </template>
<script> export default { name:'News', data(){ return{ messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'} ] } } } </script>
|
总结:
命名路由:
作用:可以简化路由的跳转
如何使用:
给路由命名:
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' path:'welcome', component:Hello, } ] } ] }
|
简化跳转:
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
|
6.7. 路由的params参数
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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:[ { name:'xiangqing', path:'detail/:id/:title', component:Detail } ] } ] } ] })
|
src/pages/Message.vue
:
<template> <div> <ul> <li v-for="m in messageList" :key="m.id"> <!-- 跳转路由并携带params参数,to的字符串写法 --> <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`"> {{m.title}} </router-link> -->
<!-- 跳转路由并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:m.id, title:m.title } }"> {{m.title}} </router-link> </li> </ul> <hr/> <router-view></router-view> </div> </template>
<script> export default { name:'News', data(){ return{ messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'} ] } } } </script>
|
src/pages/Detail.vue
:
<template> <ul> <li>消息编号:{{$route.params.id}}</li> <li>消息标题:{{$route.params.title}}</li> </ul> </template>
<script> export default { name:'Detail' } </script>
|
总结:
配置路由,声明接收params
参数:
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', component:Detail } ] } ] }
|
传递参数:
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>
|
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
接收参数:
$route.params.id $route.params.title
|
6.8. 路由的props配置
src/pages/Message.vue
:
<template> <div> <ul> <li v-for="m in messageList" :key="m.id"> <router-link :to="{ name:'xiangqing', params:{ id:m.id, title:m.title } }"> {{m.title}} </router-link> </li> </ul> <hr/> <router-view></router-view> </div> </template>
<script> export default { name:'News', data(){ return{ messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'} ] } } } </script>
|
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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:[ { name:'xiangqing', path:'detail/:id/:title', component:Detail,
props($route){ return { id:$route.params.id, title:$route.params.title, } } } ] } ] } ] })
|
src/pages/Detail.vue
:
<template> <ul> <li>消息编号:{{id}}</li> <li>消息标题:{{title}}</li> </ul> </template>
<script> export default { name:'Detail', props:['id','title'] } </script>
|
总结:
作用:让路由组件更方便的收到参数
{ name:'xiangqing', path:'detail/:id', component:Detail,
props(route){ return { id:route.query.id, title:route.query.title } } }
|
6.9. 路由跳转的replace方法
src/pages/Home.vue
:
<template> <div> <h2>Home组件内容</h2> <div> <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> <router-view></router-view> </div> </div> </template>
<script> export default { name:'Home' } </script> 12345678910111213141516171819202122
|
总结:
- 作用:==控制路由跳转时操作浏览器历史记录的模式==
- 浏览器的历史记录有两种写入方式:
push
和replace
,其中push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
方式
- 开启
replace
模式:<router-link replace ...>News</router-link>
6.10. 编程式路由导航
src/components/Banner.vue
:
<template> <div class="col-xs-offset-2 col-xs-8"> <div class="page-header"> <h2>Vue Router Demo</h2> <button @click="back">后退</button> <button @click="forward">前进</button> <button @click="test">测试一下go</button> </div> </div> </template>
<script> export default { name:'Banner', methods:{ back(){ this.$router.back() }, forward(){ this.$router.forward() }, test(){ this.$router.go(3) } }, } </script>
|
src/pages/Message.vue
:
<template> <div> <ul> <li v-for="m in messageList" :key="m.id"> <router-link :to="{ name:'xiangqing', params:{ id:m.id, title:m.title } }"> {{m.title}} </router-link> <button @click="showPush(m)">push查看</button> <button @click="showReplace(m)">replace查看</button> </li> </ul> <hr/> <router-view></router-view> </div> </template>
<script> export default { name:'News', data(){ return{ messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'} ] } }, methods:{ showPush(m){ this.$router.push({ name:'xiangqing', query:{ id:m.id, title:m.title } }) }, showReplace(m){ this.$router.replace({ name:'xiangqing', query:{ id:m.id, title:m.title } }) } } } </script>
|
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' 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:[ { name:'xiangqing', path:'detail', component:Detail, props($route){ return { id:$route.query.id, title:$route.query.title, } } } ] } ] } ] })
|
src/pages/Detail.vue
:
<template> <ul> <li>消息编号:{{id}}</li> <li>消息标题:{{title}}</li> </ul> </template>
<script> export default { name:'Detail', props:['id','title'] } </script>
|
效果:

总结:
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
具体编码:
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()
|
6.11. 缓存路由组件
src/pages/News.vue
:
<template> <ul> <li>news001 <input type="text"></li> <li>news002 <input type="text"></li> <li>news003 <input type="text"></li> </ul> </template>
<script> export default { name:'News' } </script>
|
src/pages/Home.vue
:
<template> <div> <h2>Home组件内容</h2> <div> <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> <keep-alive include="News"> <router-view></router-view> </keep-alive> </div> </div> </template>
<script> export default { name:'Home' } </script>
|
效果:

总结:
作用:==让不展示的路由组件保持挂载,不被销毁==
具体编码:
//缓存一个路由组件 <keep-alive include="News"> //include中写想要缓存的组件名,不写表示全部缓存 <router-view></router-view> </keep-alive>
//缓存多个路由组件 <keep-alive :include="['News','Message']"> <router-view></router-view> </keep-alive>
|
6.12. activated和deactivated
`src/pages/News.vue`: 1 <template> <ul> <li :style="{opacity}">欢迎学习vue</li> <li>news001 <input type="text"></li> <li>news002 <input type="text"></li> <li>news003 <input type="text"></li> </ul> </template>
<script> export default { name:'News', data(){ return{ opacity:1 } }, activated(){ console.log('News组件被激活了') this.timer = setInterval(() => { this.opacity -= 0.01 if(this.opacity <= 0) this.opacity = 1 },16) }, deactivated(){ console.log('News组件失活了') clearInterval(this.timer) } } </script>
|
效果:

总结:
activated
和deactivated
是路由组件所独有的两个钩子,用于捕获路由组件的激活状态
- 具体使用:
activated
路由组件被激活时触发
deactivated
路由组件失活时触发
6.13. 路由守卫
6.13.1. 全局路由守卫
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' import News from '../pages/News' import Message from '../pages/Message' import Detail from '../pages/Detail'
const router = new VueRouter({ routes:[ { name:'guanyv', 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:'详情'}, props($route){ return { id:$route.query.id, title:$route.query.title, } } } ] } ] } ] })
router.beforeEach((to,from,next) => { console.log('前置路由守卫',to,from) if(to.meta.isAuth){ if(localStorage.getItem('school')==='atguigu'){ next() }else{ alert('学校名不对,无权限查看!') } }else{ next() } })
router.afterEach((to,from)=>{ console.log('后置路由守卫',to,from) document.title = to.meta.title || '硅谷系统' })
export default router
|
6.13.2. 独享路由守卫
src/router/index.js
:
import VueRouter from "vue-router";
import Home from '../pages/Home' import About from '../pages/About' import News from '../pages/News' import Message from '../pages/Message' import Detail from '../pages/Detail'
const router = new VueRouter({ routes:[ { name:'guanyv', path:'/about', component:About, meta:{title:'关于'} }, { name:'zhuye', path:'/home', component:Home, meta:{title:'主页'}, children:[ { name:'xinwen', path:'news', component:News, meta:{title:'新闻'}, beforeEnter(to,from,next){ console.log('独享路由守卫',to,from) if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') } } }, { name:'xiaoxi', path:'message', component:Message, meta:{title:'消息'}, children:[ { name:'xiangqing', path:'detail', component:Detail, meta:{title:'详情'}, props($route){ return { id:$route.query.id, title:$route.query.title, } } } ] } ] } ] })
router.afterEach((to,from)=>{ console.log('后置路由守卫',to,from) document.title = to.meta.title || '硅谷系统' })
export default router
|
6.13.3. 组件内路由守卫
src/pages/About.vue
:
<template> <h2>我是About组件的内容</h2> </template>
<script> export default { name:'About', //通过路由规则,离开该组件时被调用 beforeRouteEnter (to, from, next) { console.log('About--beforeRouteEnter',to,from) if(localStorage.getItem('school')==='atguigu'){ next() }else{ alert('学校名不对,无权限查看!') } }, //通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { console.log('About--beforeRouteLeave',to,from) next() } } </script>
|
总结:
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
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 }else{ document.title = 'vue_test' } })
|
独享守卫:
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') } }
|
组件内守卫:
beforeRouteEnter (to, from, next) {...},
beforeRouteLeave (to, from, next) {...},
|
6.14. 路由器的两种工作模式
- 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值
- hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
- hash模式:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
- history模式:
- 地址干净,美观
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
7.Vue UI组件库
7.1 常用UI组件库
7.1.1 移动端常用UI组件库
- Vant
- Cube UI
- Mint UI
7.1.2. PC端常用UI组件库
- Element UI
- IView UI
7.2. element-ui基本使用
安装 element-ui:npm i element-ui -S
src/main.js
:
import Vue from 'vue' import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
Vue.use(ElementUI)
new Vue({ el:"#app", render: h => h(App), })
|
src/App.vue
:
<template> <div> <br> <el-row> <el-button icon="el-icon-search" circle></el-button> <el-button type="primary" icon="el-icon-edit" circle></el-button> <el-button type="success" icon="el-icon-check" circle></el-button> <el-button type="info" icon="el-icon-message" circle></el-button> <el-button type="warning" icon="el-icon-star-off" circle></el-button> <el-button type="danger" icon="el-icon-delete" circle></el-button> </el-row> </div> </template>
<script> export default { name:'App', } </script>
|
7.3. element-ui按需引入
安装 babel-plugin-component:npm install babel-plugin-component -D
修改 babel-config-js
:
module.exports = { presets: [ '@vue/cli-plugin-babel/preset', ["@babel/preset-env", { "modules": false }] ], plugins: [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
|
src/main.js
:
import Vue from 'vue' import App from './App.vue'
import { Button,Row } from 'element-ui'
Vue.config.productionTip = false
Vue.component(Button.name, Button); Vue.component(Row.name, Row);
new Vue({ el:"#app", render: h => h(App), })
|
8.Vue 3
完结撒花~