• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Vue的ajax请求、Vuex、Vue UI组件库

武飞扬头像
gh-xiaohe
帮助1

😹 作者: gh-xiaohe
😻 gh-xiaohe的博客
😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!!

🚏 Vue中的ajax请求

🚀 解决开发环境 Ajax 跨域问题

总结:

学新通

🚬 准备好测试的服务器

server1.js

const express = require('express')
const app = express()

app.use((request,response,next)=>{
	console.log('有人请求服务器1了');
	// console.log('请求来自于',request.get('Host'));
	// console.log('请求的地址',request.url);
	next()
})

app.get('/students',(request,response)=>{
	const students = [
		{id:'001',name:'tom',age:18},
		{id:'002',name:'jerry',age:19},
		{id:'003',name:'tony',age:120},
	]
	response.send(students)
})

app.listen(5000,(err)=>{
	if(!err) console.log('服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students');
})

server2.js

const express = require('express')
const app = express()

app.use((request,response,next)=>{
	console.log('有人请求服务器2了');
	next()
})

app.get('/cars',(request,response)=>{
	const cars = [
		{id:'001',name:'奔驰',price:199},
		{id:'002',name:'马自达',price:109},
		{id:'003',name:'捷达',price:120},
	]
	response.send(cars)
})

app.listen(5001,(err)=>{
	if(!err) console.log('服务器2启动成功了,请求汽车信息地址为:http://localhost:5001/cars');
})

启动

  • node server1.js
  • node server2.js

🚬 准备访问5000服务器 存在跨域问题

学新通

安装 axios

  • npm i axios
  • 引入 import axios from ‘axios’

app.vue

<template>
	<div>
		<button @click="getStudents">获取学生信息</button>
	</div>
</template>

<script>
	import axios from 'axios'
	export default {
		name:'App',
		methods: {
			getStudents(){
				axios.get('http://localhost:5000/students').then(
					// 成功
					response => {
						console.log('请求成功了',response.data)
					},
					// 失败
					error => {
						console.log('请求失败了',error.message)
					}
				)
			}
		},
	}
</script>

学新通

解决跨域

  • cors 不用前端人员做任何的事情
    • 写服务器(后端人员)的人,在服务器里面,给你返回响应的时候,加几个特殊的响应头
    • 不是随便的配置的,造成的后果,任何人都可以找你这台服务器要数据
  • jsonp 开发时使用微乎其微
    • 借助了 srcipt 便签里面 src 属性 不受同源策略限制
    • 前端人员需要写特殊的写法后端人员也需要配合你,并且只能解决 get请求
  • 代理服务器
    • 和所处的位置(前端)是一样的,协议名、主机名、端口号都保持一致
    • 服务器和服务器之间传递不用ajax,使用的是传统的http请求
      • nginx 代理服务器
      • vue-cli 开启一个代理服务器

🚬 配置代理方式一:

细节:

  • 代理服务器不是所有的请求,都转发给5000(不能灵活的配置走不走代理)
    • 代理服务器本事就有的数据,就不转发给5000
  • 不能配置多个代理

vue.config.js

 // 开启代理服务器
 devServer: {
   proxy: 'http://localhost:5000'
 }, 

app.vue

端口改成8080

<template>
	<div>
		<button @click="getStudents">获取学生信息</button>
	</div>
</template>

<script>
	import axios from 'axios'
	export default {
		name:'App',
		methods: {
			getStudents(){
				axios.get('http://localhost:8080/students').then(
					// 成功
					response => {
						console.log('请求成功了',response.data)
					},
					// 失败
					error => {
						console.log('请求失败了',error.message)
					}
				)
			}
		},
	}
</script>

学新通

🚬 配置代理方式二:

	开启代理服务器(方式二)
	devServer: {
   proxy: {
     '/atguigu': { // 请求前缀
       target: 'http://localhost:5000', // 请求地址
				pathRewrite:{'^/atguigu':''}, // 重写路径 key-value  key正则的匹配条件 把以atguigu 开头的变成 空字符串
       // ws: true, //用于支持websocket  默认true
       // changeOrigin: true //用于控制请求头中的host值 默认true
     },
     '/demo': {
       target: 'http://localhost:5001',
				pathRewrite:{'^/demo':''},
       // ws: true, //用于支持websocket
       // changeOrigin: true //用于控制请求头中的host值
     }
   }
 }

app.vue

<template>
	<div>
		<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/atguigu/students').then(
					response => {
						console.log('请求成功了',response.data)
					},
					error => {
						console.log('请求失败了',error.message)
					}
				)
			},
			getCars(){
				axios.get('http://localhost:8080/demo/cars').then(
					response => {
						console.log('请求成功了',response.data)
					},
					error => {
						console.log('请求失败了',error.message)
					}
				)
			}
		},
	}
</script>

学新通

🚄 2、github用户搜索案例

项目接口:https://api.github.com/search/users?q=xxx

🚬 1、静态编写

① 基础

app.vue

<template>
	<div id="app">
	<div class="container">
  <!--头部的搜索-->
		<section class="jumbotron">
		<h3 class="jumbotron-heading">Search Github Users</h3>
		<div>
			<input type="text" placeholder="enter the name you search"/>&nbsp;<button>Search</button>
		</div>
		</section>
  <!--List-->     
		<div class="row">
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		</div>
	</div>
	</div>
</template>

<script>
	export default {
		name:'App',
	}
</script>

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

把bootstop的样式引入

  • 方式一:src下创建文件夹assets(静态资源)/css 把 bootstrap.css 存在里面
  • 引入:
    • ① main.js 中引入 不推荐
    • 里面用到了第三方的样式,这些资源还不去使用,目前不用,不推荐assets方式
    • ② 根组件app.vue引入 import ‘./assets/css/bootstrao.css’
      • 此方式,脚手架会做一个非常严格的检查,有引入不存在的资源就会报错,没有使用也不可以
  • 方式二:在public下建立一个css文件夹 把 bootstrap 放入里面,在index.html页面中引入
  • 相对路径

② 拆组件

app.vue

  • 样式都是控制列表区的
<template>
	<div class="container">
		<Search/>
		<List/>
	</div>
</template>

<script>
	import Search from './components/Search'
	import List from './components/List'
	export default {
		name:'App',
		components:{Search,List}
	}
</script>

List.vue

<template>
      <!--List-->     
		<div class="row">
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		<div class="card">
			<a href="https://github.com/xxxxxx" target="_blank">
			<img src="https://cn.vuejs.org/images/logo.svg" style='width: 100px'/>
			</a>
			<p class="card-text">xxxxxx</p>
		</div>
		</div>
</template>

<script>
	export default {
		name:'List'
	}
</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>

Search.vue

  • 使用的是 bootstop 中的样式
<template>
      <!--头部的搜索-->
		<section class="jumbotron">
		<h3 class="jumbotron-heading">Search Github Users</h3>
		<div>
			<input type="text" placeholder="enter the name you search"/>&nbsp;<button>Search</button>
		</div>
		</section>
</template>

<script>
	export default {
		name:'Search'
	}
</script>

学新通

🚬 2、展示动态的数据和交互

Search.vue

  • 获取用户输入
  • 发送请求
  • 把数据通过全局事件总线的方式传递给list
<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"/>&nbsp;
			<button @click="searchUsers">Search</button>
		</div>
	</section>
</template>

<script>
	import axios from 'axios'
	export default {
		name:'Search',
		data() {
			return {
				keyWord:''
			}
		},
		methods: {
			searchUsers(){
				//请求前更新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('请求成功了',respense.data)
						//请求成功后更新List的数据
						this.$bus.$emit('updateListData',respense.data.items)
					},
					error => {
						console.log('请求失败了',error.message)
					}
				)
			}
		},
	}
</script>


学新通

main.js安装全局事件总线

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

List.vue

  • List接收数据 Search 传输数据
  • avatar_url 用户的头像地址 展示
  • html_url 每一个人的github主页 点击时实现跳转
  • login 用户的登录名 展示
<script>
	export default {
		name:'List',
		data() {
			return {
				users:[]
			}
		},
		mounted() {
			this.$bus.$on('updateListData',(dataObj)=>{
               console.log('我是List组件,收到数据:',dataObj)
				this.dataObj = dataObj // 存数据
			})
		},
	}
</script>>

学新通

<!--替换div中的数据-->
<template>
	<div class="row">
		<!-- 展示用户列表 -->
		<div class="card" v-for="user in info.users" :key="user.login">
			<a :href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/user.html_url" target="_blank">
				<img :src="https://blog.csdn.net/gh_xiaohe/article/details/user.avatar_url" style='width: 100px'/>
			</a>
			<p class="card-text">{{user.login}}</p>
		</div>
	</div>
</template>

学新通

🚬 3、完善功能

  • 一上来使用有欢迎词 list组件
  • 搜索没有加载出来时,限制正在加载中 list组件
  • error

List.vue

<template>
	<div class="row">
		<!-- 展示用户列表 -->
		<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
			<a :href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/user.html_url" target="_blank">
				<img :src="https://blog.csdn.net/gh_xiaohe/article/details/user.avatar_url" style='width: 100px'/>
			</a>
			<p class="card-text">{{user.login}}</p>
		</div>
		<!-- 展示欢迎词 -->
		<h1 v-show="info.isFirst">欢迎使用!</h1>
		<!-- 展示加载中 -->
		<h1 v-show="info.isLoading">加载中....</h1>
		<!-- 展示错误信息 -->
		<h1 v-show="info.errMsg">{{info.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}// 通过字面量的形式去合并对象,重名后面为主
			})
		},
	}
</script>

Search.vue

<script>
	import axios from 'axios'
	export default {
		name:'Search',
		data() {
			return {
				keyWord:''
			}
		},
		methods: {
			searchUsers(){
				//请求前更新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>

学新通

🚬 4、完整数据

App.vue

<template>
	<div class="container">
		<Search/>
		<List/>
	</div>
</template>

<script>
	import Search from './components/Search'
	import List from './components/List'
	export default {
		name:'App',
		components:{Search,List}
	}
</script>

List.vue

<template>
	<div class="row">
		<!-- 展示用户列表 -->
		<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
			<a :href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/user.html_url" target="_blank">
				<img :src="https://blog.csdn.net/gh_xiaohe/article/details/user.avatar_url" style='width: 100px'/>
			</a>
			<p class="card-text">{{user.login}}</p>
		</div>
		<!-- 展示欢迎词 -->
		<h1 v-show="info.isFirst">欢迎使用!</h1>
		<!-- 展示加载中 -->
		<h1 v-show="info.isLoading">加载中....</h1>
		<!-- 展示错误信息 -->
		<h1 v-show="info.errMsg">{{info.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}
			})
		},
	}
</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>

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"/>&nbsp;
			<button @click="searchUsers">Search</button>
		</div>
	</section>
</template>

<script>
	import axios from 'axios'
	export default {
		name:'Search',
		data() {
			return {
				keyWord:''
			}
		},
		methods: {
			searchUsers(){
				//请求前更新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>

main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

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

🚒 3、vue 项目中常用的 2 个 Ajax 库

🚬 axios 强力推荐

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

🚬 vue-resource(插件库)

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

安装

  • npm i vue-resource

引入插件

  • import VueResource from ‘vue-resource’

使用插件

  • vue.use(VueResource )

vm 和 vc 身上都多了 $http:(…)

学新通

Search.vue

<script>
	export default {
		methods: {
			searchUsers(){
				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>

🚤 4、Vue插槽solt

总结:

学新通

🚬 效果

① 基础

Category.vue

<template>
	<div class="category">
		<h3>xxxxxx分类</h3>
        <ul>
            <li>xxxx</li>
            <li>xxxx</li>
            <li>xxxx</li>
            <li>xxxx</li>
    	</ul>
	</div>
</template>

<script>
	export default {
		name:'Category'
	}
</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>

app.vue

<template>
	<div class="container">
		<Category/>
        <Category/>
        <Category/>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category}
	}
</script>

<style scoped>
	.container{
		display: flex;
		justify-content: space-around;
	}
</style>

学新通

② 实现

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
        <ul>
			<ul>
				<li v-for="(item,index) in listData" :key="index">{{item}}</li>
			</ul>
    	</ul>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['listData','title']
	}
</script>

app.vue

<template>
	<div class="container">
		<Category title="美食" :listData="foods"/>
        <Category title="游戏" :listData="games"/>
        <Category title="电影" :listData="films"/>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				foods:['火锅','烧烤','小龙虾','牛排'],
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
				films:['《教父》','《拆弹专家》','《你好,李焕英》','《流浪地球》']
			}
		},
	}
</script>

学新通

🚬 默认插槽

  • 改需求,如下效果

学新通

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="电影">
           <!--controls 控制 (可以控制播放)-->
			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
	</div>
</template>

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
	</div>
</template>


props:['title']

ok 实现

🚬 作用域插槽

  • 改需求,如下效果

学新通

① 基础

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<slot>我是默认的一些内容</slot>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category}
</script>

app.vue

<template>
	<div class="container">
		<Category title="游戏">
			<ul>
				<li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li>
			</ul>
		</Category>
		<Category title="游戏">
			<ul>
				<li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li>
			</ul>
		</Category>
		<Category title="游戏">
			<ul>
				<li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li>
			</ul>
		</Category>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title'],
		data() {
			return {
				games:['红色警戒','穿越火线','劲舞团','超级玛丽']
			}
		},
	}
</script>

学新通

② 数据不在app组件中 在 Category 组件中

  • 插槽也就不需要了

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<ul>
			<li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li>
		</ul>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title'],
		data() {
			return {
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
			}
		},
	}
</script>

app.vue

<template>
	<div class="container">
		<Category title="游戏"></Category>
		<Category title="游戏"></Category>
		<Category title="游戏"></Category>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

同样实现

学新通

③ 需求:数据是一样的,数据的结构根据使用者的结构来定义

  • games 的作用域,games 在 Category 中,app中如何使用

  • 插槽提供了便捷的方式,插槽绑定 games,就传给了插槽的使用者

  • 使用者如何拿到,使用template标签 属性scope=“名字随意”,就可以收到数据

  • 第一次使用数据是无序列表

  • 第二次使用数据是有序列表

  • 第二次使用数据每一个都是 h4 标题

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<slot :games="games" msg="hello">我是默认的一些内容</slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title'],
		data() {
			return {
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
			}
		},
	}
</script>

app.vue

<template>
	<div class="container">

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

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

		<Category title="游戏">
           <!-- 结构赋值 -->
			<template slot-scope="{games}">
				<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
			</template>
		</Category>

	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
	}
</script>

ok 实现

🚏 Vue中vuex 重要

总结:


学新通

理解:

全局事件总线

  • 此时组件太多了,而且数据共享

学新通

vuex 共享

学新通

🚀 1、Vuex

🚬 1、什么时候使用Vuex

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

🚬 2、案例纯粹的vue

① 基础结构

Count.vue

<template>
	<div>
		<h1>当前求和为:????</h1>
		<select >
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<button> </button>
		<button>-</button>
		<button>当前求和为奇数再加</button>
		<button>等一等再加</button>
	</div>
</template>

<script>
	export default {
		name:'Count',
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

app.vue

<template>
	<div>
		<Count/>
	</div>
</template>

<script>
	import Count from './components/Count'
	export default {
		name:'App',
		components:{Count},
	}
</script>

学新通

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

学新通

🚬 3、Vuex 工作原理图

学新通

🚬 4、搭建Vuex环境

🚭 步骤
  • 1、npm i vuex
    • vue3成为默认版本的同时,vuex也更新到了4版本
    • npm i vuex 安装的是vuex4
    • vuex的4版本,只能在vue3中使用
      • vue2中,要用vuex的3版本 npm i vuex@3
      • vue3中,要用vuex的4版本
  • 2、Vue.use(Vuex)
  • 3、store 管理
    • Actions
    • Mutations
    • State
  • 4、vc 看的见 store
🚭 创建store两种方式:

方式一:src下创建个文件夹vuex,创建一个store.js

方式二:文件夹交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,
})
🚭 引入Store
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store/index'

//关闭Vue的生产提示
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)


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

注意:引入vuex的位置

  • 要在index.js中,如果在main.js中不会报错
    • 还需要引入vue,并且要在最上方

学新通

  • 成功
    学新通

🚬 5、vuex实现案例(vuex的基本使用)

  • 简单易懂
// 1、准备state——用于存储数据
const state = {
	sum:0 //当前的和
}

// 2、准备actions——用于响应组件中的动作
const actions = {
	jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	}
}
// 3、准备mutations——用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum  = value
	}
}
// 引用
<h1>当前求和为:{{$store.state.sum}}</h1>

this.$store.dispatch('jiaWait',this.n)

// 如果 Actions 中没有业务逻辑时,可以直接调用Mutations
this.$store.commit('JIA',this.n) // 和 Mutations 对话 名字是大写的

// 拓展:Actions可以有多个,一个处理不过来
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		console.log('处理了一些事情--jiaOdd')
		context.dispatch('demo1',value)
	},
	demo1(context,value){
		console.log('处理了一些事情--demo1')
		context.dispatch('demo2',value)
	},
	demo2(context,value){
		console.log('处理了一些事情--demo2')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},

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('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			},
			incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			},
		},
		mounted() {
			console.log('Count',this)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

index.js

  • 开发中推荐
  • actions 中小写
  • mutations 中大写
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions——用于响应组件中的动作
const actions = {
   // context 上下文
   // 此时没有意义,拿过来就转发
	/*jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	},*/
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations——用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum  = value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	}
}
//准备state——用于存储数据
const state = {
	sum:0 //当前的和
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
})

🚬 6、Vuex开发者工具使用

学新通
学新通

🚭 问题一:

问什么在Actions 中给的是 context(上下文),明明只有一个commint操作,为啥不直接给一个commint就可以了

  • 如果给你commint 那么就没有退路可严,只能commint向下操作
  • 但是Actions可以有多个,一个业务逻辑完成不了,通过上下文可以继续向下调用
// 拓展:Actions可以有多个,一个处理不过来
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		console.log('处理了一些事情--jiaOdd')
		context.dispatch('demo1',value)
	},
	demo1(context,value){
		console.log('处理了一些事情--demo1')
		context.dispatch('demo2',value)
	},
	demo2(context,value){
		console.log('处理了一些事情--demo2')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
🚭 问题二:

context.state.sum = value // 同样可以实现,但是开发者失效了,不建议(开发者工具捕获的是Mutations中数据的变化)

	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			//context.commit('JIA',value)
            context.state.sum  = value // 同样可以实现,但是开发者失效了,不建议(开发者工具捕获的是Mutations中数据的变化)
		}
	},
🚭 问题三:

为什么要把业务逻辑写在 actions(书写业务逻辑)中,可以写在方法中,同样可以实现,看组件就非常的直观

  • 原因:比如不是加操作,而是发票的报销,传递的是发票号,非常非常复杂的发票报销判断逻辑,
    • 难不成所有的人的都要写复杂的逻辑判断,自己发请求去链接服务器验证发票的真伪,非常冗余,可复用性高

🚄 2、Store中的配置项

🚬 1、Store中的配置项getters

① 让sum的值放大十倍展示

  • 日后的逻辑可能很复杂,而且可能不止使用一个getters
<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<h3>当前求和放大10倍为:{{$store.state.sum * 10}}</h3>
	</div>
</template>
  • 计算属性实现
  • 计算属性不能跨组件使用,只能自己组件使用
<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<h3>当前求和放大10倍为:{{dahe}</h3>
	</div>
</template>

<script>
	export default {
		name:'Count',
		data() {},
		computed: {
      	dahe(){
              return this.$store.state.sum * 10
          }
  	}
	}
</script>
  • getters

store/index.js

//准备getters——用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

Count.vue

<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<h3>当前求和放大10倍为:{{$store.getters.bigSum}}</h3>
	</div> 
</template>

学新通

🚬 2、Store中的配置项_mapState与mapGetters

① 需求

  • 展示我自己在自学,学习前端,学习方式,和学习内容是State中的数据

store/index.js

//准备state——用于存储数据
const state = {
	sum: 0, //当前的和
	school: '自学',
	subject: '前端'
} 

Count.vue

<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<h3>当前求和放大10倍为:{{$store.state.sum * 10}}</h3>
		<h3>我自己在{{$store.state.school}},学习{{$store.state.subject}}</h3>
	</div>
</template>

简化想要实现的效果

  • 把$store.state 省略不写
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我自己在{{school}},学习{{subject}}</h3>
	</div>
</template>

② 实现

  • 计算属性,就自己组件使用
<script>
		computed:{
			//靠程序员自己亲自去写计算属性
           /* 是在 state 中取数据 */
			sum(){
				return this.$store.state.sum
			},
			school(){
				return this.$store.state.school
			},
			subject(){
				return this.$store.state.subject
			},
           /* 是在 getters 中取数据 */
			bigSum(){
				return this.$store.getters.bigSum
			}
		},
	}
</script>
  • 使用Vuex中的==…mapState== 和 …mapGetters来实现
  • 需要引入
<script>
   	import {mapState,mapGetters} from 'vuex'
   	// obj对象里面 ...obj1一个对象什么意思,把obj1里面的每一组key-value都展开放入对象obj中
		computed:{
			/* 是在 state 中取数据 */
			//借助mapState生成计算属性,从state中读取数据。(对象写法)
			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),

			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject']),

			/* ******************************************************************** */
           /* 是在 getters 中取数据 */
			//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
			// ...mapGetters({bigSum:'bigSum'})
			
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters(['bigSum'])

		},
	}
</script>


学新通
学新通

🚬 3、Store中的配置项_mapActions与mapMutations.mp4

优化:actions

  • 需要引入

Count.vue

<template>
	<div>
		<button @click="increment"> </button>
		<button @click="decrement">-</button>
		<button @click="incrementOdd">当前求和为奇数再加</button>
		<button @click="incrementWait">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
		methods: {
			//程序员亲自写方法
           /* commit */
			increment(){
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			},

           /* dispatch */
			ncrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			},
		},
	}
</script>

优化

<template>
	<div>
		<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'
		methods: {

           /* commit */
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'JIA',decrement:'JIAN'}),

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
			// ...mapMutations(['JIA','JIAN']),// 名称要一致

           /* dispatch */
			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
			// ...mapActions(['jiaOdd','jiaWait'])// 名称要一致
		},
	}
</script>

🚬 4、多组件共享数据

在写一个组件Person展示人员的信息

  • 需要在vuex中展示的信息sum、persons
  • count 组件读取sum、persons,进行展示
  • person 组件 读取sum、persons,进行展示
  • 添加一个人信息

① 基础

store/index.js

//准备state——用于存储数据
const state = {
	sum:0, //当前的和
	school:'尚硅谷',
	subject:'前端',
	personList:[
		{id:'001',name:'张三'}
	]
}

Person

<template>
	<div>
		<h1>人员列表</h1>
		<input type="text" placeholder="请输入名字" v-model="name">
		<button>添加</button>
		<ul>
			<li v-for="p in personList" :key="p.id">{{p.name}}</li>
		</ul>
	</div>
</template>

<script>
	export default {
		name:'Person',
		computed:{
			personList(){
				return this.$store.state.personList
			}
		}
	}
</script>

学新通

② 实现多组件共享数据

store/index.js

//准备mutations——用于操作数据(state)
const mutations = {
	ADD_PERSON(state, value) {
		console.log('mutations中的ADD_PERSON被调用了')
		state.personList.unshift(value)
	}
}

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>

count.vue

<template>
	<div>
		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
   </div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data() {
		},
		computed:{
			...mapState(['sum','school','subject','personList']),
			...mapGetters(['bigSum'])
		},

	}
</script>

person.vue

<template>
	<div>
		<h3 style="color:red">Count组件求和为:{{sum}}</h3>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
	export default {
		name:'Person',
		data() {
		},
		computed:{
			sum(){
				return this.$store.state.sum
			}
		}
	}
</script>

学新通
(VUE入门.assets/image-20220808220828347.png)]

🚒 3、Vuex的模块编码 命名空间

  • action、mutations、state、getters中如果有多个模块(订单、人员、求和),都放在一个显得臃肿,更容易造成git中的版本控制冲突
    • 把不同分类的action、mutations、state、getters放在不同的位置

🚬 ① 原始臃肿的写法方式

store/index.js

//准备actions——用于响应组件中的动作
const actions = {
	jiaOdd(context, value) {
		console.log('actions中的jiaOdd被调用了')
		if (context.state.sum % 2) {
			context.commit('JIA', value)
		}
	},
	jiaWait(context, value) {
		console.log('actions中的jiaWait被调用了')
		setTimeout(() => {
			context.commit('JIA', value)
		}, 500)
	}
}
//准备mutations——用于操作数据(state)
const mutations = {
	JIA(state, value) {
		console.log('mutations中的JIA被调用了')
		state.sum  = value
	},
	JIAN(state, value) {
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	},
	ADD_PERSON(state, value) {
		console.log('mutations中的ADD_PERSON被调用了')
		state.personList.unshift(value)
	}
}
//准备state——用于存储数据
const state = {
	sum: 0, //当前的和
	school: '自学',
	subject: '前端',
	personList: [
		{ id: '001', name: '张三' }
	]
}
//准备getters——用于将state中的数据进行加工
const getters = {
	bigSum(state) {
		return state.sum * 10
	}
}

🚬 ② 改进: store/index.js

// 求和相关的配置
const countOptions = {
    // 书写完后countAbout 名才你能被 computed 中的 ...mapState 认识
    // ...mapState('countAbout',['sum','school','subject']), 不书写  countAbout 不被认识
    namespaced:true,
	actions:{
		jiaOdd(context, value) {
			console.log('actions中的jiaOdd被调用了')
			if (context.state.sum % 2) {
				context.commit('JIA', value)
			}
		},
		jiaWait(context, value) {
			console.log('actions中的jiaWait被调用了')
			setTimeout(() => {
				context.commit('JIA', value)
			}, 500)
		}
	},
	mutations:{
		JIA(state, value) {
			console.log('mutations中的JIA被调用了')
			state.sum  = value
		},
		JIAN(state, value) {
			console.log('mutations中的JIAN被调用了')
			state.sum -= value
		}
	},
	state:{
		sum: 0, //当前的和
		school: '自学',
		subject: '前端',
	},
	getters:{
		bigSum(state) {
			return state.sum * 10
		}
	},
}

// 人员管理相关的配置
const personOptions = {
    namespaced:true,
	actions:{},
	mutations:{
		ADD_PERSON(state, value) {
			console.log('mutations中的ADD_PERSON被调用了')
			state.personList.unshift(value)
		}
	},
	state:{
		personList: [
			{ id: '001', name: '张三' }
		]
	},
	getters:{},
}

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

学新通

Count.vue

① 非简写

<template>
	<div>
		<h1>当前求和为:{{countAbout.sum}}</h1>
		<h3>当前求和放大10倍为:{{countAbout.bigSum}}</h3>
		<h3>我在{{countAbout.school}},学习{{countAbout.subject}}</h3>
		<h3 style="color:red">Person组件的总人数是:{{personAbout.personList.length}}</h3>

	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			// 进行了模块化编码 state中有数据countOptions和 personOptions。(数组写法)
			...mapState('countAbout','personAbout'),
		}
	}
</script>

② 简写

  • 开启命名空间:namespaced:true,
  • …mapState、…mapMutations、…的简写方式
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</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, //用户选择的数字
			}
		},
		computed:{
			// 进行了模块化编码 state中有数据countOptions和 personOptions。(数组写法)
			...mapState('countAbout',['sum','school','subject']),
			...mapState('personAbout',['personList']),
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters('countAbout')
		},
		methods: {
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		}
	}
</script>

学新通

🚬 ③ 改进:把store/index.js拆分

store/count.js

//求和相关的配置
export default {
	namespaced:true,
	actions:{
		jiaOdd(context,value){
			console.log('actions中的jiaOdd被调用了')
			if(context.state.sum % 2){
				context.commit('JIA',value)
			}
		},
		jiaWait(context,value){
			console.log('actions中的jiaWait被调用了')
			setTimeout(()=>{
				context.commit('JIA',value)
			},500)
		}
	},
	mutations:{
		JIA(state,value){
			console.log('mutations中的JIA被调用了')
			state.sum  = value
		},
		JIAN(state,value){
			console.log('mutations中的JIAN被调用了')
			state.sum -= value
		},
	},
	state:{
		sum:0, //当前的和
		school:'尚硅谷',
		subject:'前端',
	},
	getters:{
		bigSum(state){
			return state.sum*10
		}
	},
}

store/person.js

//人员管理相关的配置
export default {
	namespaced:true,
	actions:{
		addPersonWang(context,value){
			if(value.name.indexOf('王') === 0){
				context.commit('ADD_PERSON',value)
			}else{
				alert('添加的人必须姓王!')
			}
		}
	},
	mutations:{
		ADD_PERSON(state,value){
			console.log('mutations中的ADD_PERSON被调用了')
			state.personList.unshift(value)
		}
	},
	state:{
		personList:[
			{id:'001',name:'张三'}
		]
	},
	getters:{
		firstPersonName(state){
			return state.personList[0].name
		}
	},
}

store/index.js 引入

import countOptions from './count'
import personOptions from './person'

🚬 练习Actions调用Backend API 后端API

store/count.js

//人员管理相关的配置
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
	namespaced:true,
	actions:{
		addPersonWang(context,value){
			if(value.name.indexOf('王') === 0){
				context.commit('ADD_PERSON',value)
			}else{
				alert('添加的人必须姓王!')
			}
		},
		addPersonServer(context){
			axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
				response => {
					context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
				},
				error => {
					alert(error.message)
				}
			)
		}
	}
}
  • 之后正常引用

🚤 4、Vue路由Route

总结:


学新通

理解:

学新通
学新通

实现什么功能:

SPA(single page web application)应用,单页面应用

🚬 1、基本路由

🚭 路由的基本使用

① 基础

  • 样式都是引入的bootstarp的样式

  • public/index.html

  • 	<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
    

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">
          <a class="list-group-item active" href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/about.html">About</a>
          <a class="list-group-item" href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/home.html">Home</a>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <h2>我是About的内容</h2>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>


	export default {
		name:'App',
	}
</script>

  • 样式都是引入的bootstarp的样式

    • public/index.html

    • 	<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
      
  • src/main.js 使用

    • //引入Vue
      import Vue from 'vue'
      //引入App
      import App from './App.vue'
      
      //关闭Vue的生产提示
      Vue.config.productionTip = false
      
      //创建vm
      new Vue({
      	el:'#app',
      	render: h => h(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">
          <a class="list-group-item active" href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/about.html">About</a>
          <a class="list-group-item" href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/home.html">Home</a>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            此处到底展示什么组件,得看用户点击的是哪个导航项
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

Home.vue

<template>
	<h2>我是Home的内容</h2>
</template>

<script>
	export default {
		name:'Home'
	}
</script>

About.vue

<template>
	<h2>我是About的内容</h2>
</template>

<script>
	export default {
		name:'About'
	}
</script>

学新通

③ 使用vue-router

  • 安装 npm i vue-router@3

    • vue-router 4 只能在 vue 3中使用

    • vue-router 3 才能在 vue 2中使用

  • 引入vue-router,mian.js

    • 并且引入了路由器

    • //引入VueRouter
      import VueRouter from 'vue-router'
      //引入路由器
      import router from './router'
      
      //应用插件
      Vue.use(VueRouter)
      
      //创建vm
      new Vue({
      	el:'#app',
      	render: h => h(App),
      	router:router
      })
      
    • 使用vue-router需要创建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
      		}
      	]
      })
      

    学新通

    ④ 实现单页面应用

  • 找到导航区,切换路径

  • 需要路由提供的特殊的标签router-link引入,不是href 而是 to=“/home”router-view制定位置呈现

  • router-link 最终转换成的是 a 标签,如果是按钮等等跳转,后面 编程式路由导航

app.vue

<template>
  <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="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/about.html">About</a> -->
          <!-- <a class="list-group-item" href="/link/to?link=https://blog.csdn.net/gh_xiaohe/article/details/home.html">Home</a> -->

			<!-- Vue中借助router-link标签实现路由的切换 -->
            <!-- active-class该元素被激活时的样式 -->
		<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>

学新通

🚭 路由的注意点

1、路由组件和一般组件

  • 路由组件:不需写组价标签,靠路由规则,匹配出来,由路由器来进行渲染的组件。 pages放置路由组件
  • 一般组件:亲自书写组件,是一般组件。components

学新通

2、在频繁的切换时

  • 路由组件被销毁了

学新通

3、路由组件身上多了两个人,$route 和 $ routers、

  • route:配置信息

学新通

  • routers: 整个应用的路由器只有一个,800个路由规则也只有一个路由器

🚬 2、嵌套(多级)路由

效果

学新通

只关注Home中里面的内容

Home.vue

  • 路径要加上一级路由的路径
//原来
<template>
		<h2>Home组件内容</h2>
</template>

// 最新

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

Message.vue

<template>
	<div>
		<ul>
			<li>
				<a href="/link/to?link=https://blog.csdn.net/message1">message001</a>&nbsp;&nbsp;
			</li>
			<li>
				<a href="/link/to?link=https://blog.csdn.net/message2">message002</a>&nbsp;&nbsp;
			</li>
			<li>
				<a href="/link/to?link=https://blog.csdn.net/message/3">message003</a>&nbsp;&nbsp;
			</li>
		</ul>
	</div>
</template>

<script>
	export default {
		name:'Message'
	}
</script>

News.vue

<template>
	<ul>
		<li>news001</li>
		<li>news002</li>
		<li>news003</li>
	</ul>
</template>

<script>
	export default {
		name:'News'
	}
</script>

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'

//创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news',
					component:News,
				},
				{
					path:'message',
					component:Message,
				}
			]
		}
	]
})

🚬 3、路由传参

🚭 路由接收参数之一:query参数

效果

学新通

① 基础

Message.vue

  • 展示Detail组件把消息带过去,展示的都是Detail组件,但是展示的数据是不同的
<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
                <a href="/link/to?link=https://blog.csdn.net/message1">{{m.title}}</a>
			</li>
		</ul>
		<hr>
		<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>

Detail.vue

<template>
	<ul>
		<li>消息编号:???</li>
		<li>消息标题:???</li>
	</ul>
</template>

<script>
	export default {
		name:'Detail',
		mounted() {
			console.log(this.$route)
		},
	}
</script>

② 展示

Message.vue

<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带query参数,to的字符串写法 -->
				<router-link :to="/home/message/detail">{{m.title}}</router-link>&nbsp;&nbsp;
				</router-link>
			
			</li>
		</ul>
		<hr>
		<router-view></router-view>
	</div>
</template>

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

学新通

③ 实现

Message.vue

  • 传递参数
<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带query参数,to的字符串写法 -->
                <!-- :绑定 ""引号里面的内容当成js去解析,是模板字符串,还混着${}js变量 -->
				<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->

				<!-- 跳转路由并携带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>

Detal.vue

  • 接收传递的参数
<template>
	<ul>
		<li>消息编号:{{$route.query.id}}</li>
		<li>消息标题:{{$route.query.title}}</li>
	</ul>
</template>

实现

🚭 命名路由 name

router/index.js

  • name 属性
  • 作用:在跳转的时候可以简化一些编码
// 该文件专门用于创建整个应用的路由器
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:[
		{
			name:'guanyu',
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news',
					component:News,
				},
				{
					path:'message',
					component:Message,
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
						}
					]
				}
			]
		}
	]
})

Message.vue

// 不配置名字  path
<template>
			<li v-for="m in messageList" :key="m.id">

				<!-- 跳转路由并携带query参数,to的对象写法 -->
				<router-link :to="{
					path:'/home/message/detail',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{{m.title}}
				</router-link>

			</li>
</template>


// 路由配置名字  name
<template>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带query参数,to的对象写法 -->
				<router-link :to="{
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{{m.title}}
				</router-link>
			</li>
</template>
🚭 路由接收参数之二:params参数
  • 如果使用的是params参数,就必须使用name,不能使用path 报错

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>&nbsp;&nbsp; -->

				<!-- 跳转路由并携带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>

router/index.js

//创建并暴露一个路由器
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:'xiangqing',
							path:'detail/:id/:title',
							component:Detail,
						}
					]
				}
			]
		}
	]
})

Detail.vue

<template>
	<ul>
		<li>消息编号:{{$route.params.id}}</li>
		<li>消息标题:{{$route.params.title}}</li>
	</ul>
</template>
🚭 路由props配置
  • Detail 如何 读取出来别人传递过来的参数
  • props三种写法

router/index.js

//创建并暴露一个路由器
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:'xiangqing',
							path:'detail',
							component:Detail,

			// props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
			// props:{a:1,b:'hello'}

			// props的第二种写法,值为布尔值,
           // 若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
			// props:true

							//props的第三种写法,值为函数
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title
								}
							}
						}
					]
				}
			]
		}
	]
})

第三种写法:简写过程,结构赋值和结构赋值的连续写法

							// 1
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title,
								}
							}
							// 2 结构赋
							props({query}){
								return {query.id,query.title}
							}
							// 3 结构赋值的连续写法 语义化不明确,不是十分推荐
							props({query:{id,title}}){
								return {id,title}
							}

Detail.vue

<template>
	<ul>
		<li>消息编号:{{$route.params.id}}</li>
		<li>消息标题:{{$route.params.title}}</li>
	</ul>
</template>

--------------------------------------------------------------------

<template>
	<ul>
		<li>消息编号:{{id}}</li>
		<li>消息标题:{{title}}</li>
	</ul>
</template>

<script>
	export default {
		name:'Detail',
		props:['id','title'],
	}
</script>
🚭 router-link的replace属性
  • 路由对浏览器记录的影响
  • 结构:栈的结构 push 压栈
    • 指针默认指向最上面的结构 默认 push 模式
    • 替换当前栈顶的那一条 replace 模式

app.vue

<!-- replace的简写模式 替换栈顶的数据-->
<router-link replace class="list-group-item" active-class="active" to="/about">About</router-link>
<!-- replace的非模式 -->
<router-link :replace =true class="list-group-item" active-class="active" to="/home">Home</router-link>

🚬 4、编程式路由导航

  • 不书写router-link 实现路由的跳转 转换成a标签
  • 需求一:实现用按钮来实现路由的跳转
  • 需求二:过几秒之后自动实现跳转

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>&nbsp;&nbsp; -->

				<!-- 跳转路由并携带params参数,to的对象写法 -->
				<router-link :to="{
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{{m.title}}
				</router-link>
				<button @click="pushShow(m)">push查看</button>
				<button @click="replaceShow(m)">replace查看</button>
			</li>
		</ul>
		<hr>
		<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'}
				]
			}
		},
		methods: {
			pushShow(m){
				this.$router.push({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			},
			replaceShow(m){
				this.$router.replace({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			}
		},
	}
</script>

学新通
学新通

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() // 后退
				// console.log(this.$router)
			},
			forward(){
				this.$router.forward() // 前进
			},
			test(){
				this.$router.go(3) // 前进3步 -4后退四步
			}
		},
	}
</script>

学新通

🚭 缓存路由组件

News.vue

<template>
	<ul>
		<li>news001</li>
		<li>news002</li>
		<li>news003</li>
	</ul>
</template>

<script>
	export default {
		name:'News'
	}
</script>

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>
      <!-- 缓存多个路由组件 -->
      <!-- <keep-alive :include="['News','Message']"> -->

      <!-- 缓存一个路由组件 -->
      <!-- include 包含哪个组件,不书写全部组件 组件名 -->
      <keep-alive include="News">
        <router-view></router-view>
      </keep-alive>
    </div>
  </div>
</template>

学新通

🚬 5、两个新的声明周期钩子

  • 案例:在news组件上面展示一串数据,颜色透明度发生改变
    • 生命周期中书写过

News.vue

<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
			}
		},
		beforeDestroy() {
			console.log('News组件即将被销毁了')
			clearInterval(this.timer)
		}, 
		mounted(){
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		}, 
	}
</script>

问题:news组件没有被销毁,定期器一直调用,引出两个新的生命周期钩子,路由组件独有

  • activated,激活
  • deactivated,取消激活(失活)
  • News组件出现在你面前激活,消失在你面前失活
<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
			}
		},
		/* beforeDestroy() {
			console.log('News组件即将被销毁了')
			clearInterval(this.timer)
		}, */
		/* mounted(){
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		}, */
		activated() {
			console.log('News组件被激活了')
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		},
		deactivated() {
			console.log('News组件失活了')
			clearInterval(this.timer)
		},
	}
</script>

🚬 6、路由守卫

🚭 全局守卫
  • meta 标识是否需要进行权限的校验,路由元信息

  • meta:{isAuth:true,title:‘新闻’}

    • isAuth:true 是否授权
    • title:‘新闻’ 路由对应的title标题,点击时切换
  • 保护路由的权限

  • 设置权限

学新通

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'

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'}
				},
				{
					name:'xiaoxi',
					path:'message',
					component:Message,
					meta:{isAuth:true,title:'消息'},
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							meta:{isAuth:true,title:'详情'},

							//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
							// props:{a:1,b:'hello'}

							//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
							// props:true

							//props的第三种写法,值为函数
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title,
									a:1,
									b:'hello'
								}
							}

						}
					]
				}
			]
		}
	]
})

//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{ // 在每一次路由切换之前调用函数
   // to 去哪 from 来自于哪 next 放行
	console.log('前置路由守卫',to,from)
   //方式一: 路径	
// if(to.path === '/home/news' || to.path === '/home/messgae'){
   //方式二: 名字	
	// if(to.name === 'xinwen' || to.name === 'xiaoxi'){
// 方式三:推荐
	if(to.meta.isAuth){ // 判断是否需要鉴权
		if(localStorage.getItem('school')==='atguigu'){
			next()
		}else{
			alert('学校名不对,无权限查看!')
		}
	}else{
		next()
	}
})

// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
// 没有 next
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || '硅谷系统'
})

export default router

学新通

🚭 独享路由守卫
  • 某一个路由单独响应的路由守卫
  • 没有后置路路由守卫

对新闻路由进行限制

router/index.js

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'},
					beforeEnter: (to, from, next) => {
						console.log('独享路由守卫',to,from)
						if(to.meta.isAuth){ //判断是否需要鉴权
							if(localStorage.getItem('school')==='atguigu'){
								next()
							}else{
								alert('学校名不对,无权限查看!')
							}
						}else{
							next()
						}
					}
				},
				{
					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,
									a:1,
									b:'hello'
								}
							}

						}
					]
				}
			]
		}
	]
})


//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || '硅谷系统'
})

export default router
🚭 组件内路由守卫
  • beforeRouteEnter 当路由进入之前
  • beforeRouteLeave 当路由离开之前

about 组件 实现组件内路由守卫

about.vue

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

		//通过路由规则,离开该组件时被调用
		beforeRouteLeave (to, from, next) {
			console.log('About--beforeRouteLeave',to,from)
			next()
		}
	}
</script>

🚬 7、路由器的两种工作模式

router/index.js

const router =  new VueRouter({
	mode:'history',  // 两个值 一个 history 一个 hash
	routes:[
	]
})
🚭 hash模式
  • http://localhost:8080/#/home/message
    • 路径中带 # 号
    • 从# 号开始到结尾,路径值都叫hash值
  • 特点:不会随着http请求发给服务器
🚭 history模式
  • http://localhost:8080/home/message

区别:

  • hash 兼容性好
    • hash 在部署服务器时,没有问题,后面的路径当做hash值,不会带给服务器
  • history 兼容性略差
    • history 在部署服务器时,单页面点击可用,刷新输错,原因:路径后面当做资源,没有这个资源

history 模式解决404问题

  • 需要后端工程师的帮助

connect-history-api-fallback 在node JS中解决 history 模式404 的问题

服务器中 server

  • 安装:npm i connect-history-api-fallback

  • 引入:const history = require(‘connect-history-api-fallback’);

  • 应用:在app之前

  • app.use(history())
    app.use(express.static(__dirname '/static'))
    

解决

🚏 Vue UI 组件库

🚀 1、移动端常用 UI 组件库

1、Vant https://youzan.github.io/vant

2、Cube UI https://didi.github.io/cube-ui

3、Mint UI http://mint-ui.github.io

4、NutUI https://nutui.jd.com/#/

🚄 2、PC 端常用 UI 组件库

1、Element UI https://element.eleme.cn

2、IView UI https://www.iviewui.com

  • 高度定制化的UI,不适合适用于UI组件库

完整引入 ElementUI组件库

ElementUI 中的组件全不都注册成全局组件,所有的样式,所有的组件

main.js

//完整引入
//引入ElementUI组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';


//关闭Vue的生产提示
Vue.config.productionTip = false

//应用ElementUI
Vue.use(ElementUI);

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
})

学新通

按需引入 ElementUI组件库

  • 安装npm install babel-plugin-component -D // -D 开发依赖

  • 将 .babelrc 修改为 新版本名字为 babel.config.js

  • module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset',
    		["@babel/preset-env", { "modules": false }],
      ],
    	plugins:[
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    

学新通

main.js

//按需引入
import { Button,Row,DatePicker } from 'element-ui';

//关闭Vue的生产提示
Vue.config.productionTip = false

//应用ElementUI
Vue.component(Button.name, Button); // 
Vue.component(Row.name, Row);
Vue.component(DatePicker.name, DatePicker);

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
})

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbckee
系列文章
更多 icon
同类精品
更多 icon
继续加载