文章目录
配置代理
96集:首先,找到文件,然后cmd启动
得到服务器1的地址 http://localhost:5000/students
得到服务器2的地址 http://localhost:5001/cars
下载库npm i axios
引入库import axios from 'axios'
一开始出现了一个错误,跨域:请求发送了,服务器收齐并返回,但是我们获取不到(代理服务器 )
配置好之后要重新启动npm run serve
- 代理服务器不会把所有的请求都转发给5000,当8080服务器本身就具有请内的内容的时候,public文件中内容是8080服务器所有的
- 不能灵活的控制走不走代理服务器
- 只能配置一个代理,只能转发给5000请求
- 修改了代理服务器,一定要重新开启npm run serve!!!
方法一:
Vue.config.js
里面的内容是在这里找到的 链接https://cli.vuejs.org/config/#devserver-proxy
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
//开启代理服务器(是因为服务器1 student的端口号时是5000)
devServer: {
proxy: 'http://localhost:5000'
}
})
App.vue
<template>
<div><button @click="getStudents">获取学生信息</button></div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
methods: {
getStudents() {
//请求之后返回的是promise值
axios.get("http://localhost:8080/students").then(
response => {
console.log("请求成功了", response.data);
},
error => {
console.log("请求失败了", error.message);
}
);
},
},
};
</script>
方法二:
可以配置多个代理,且可以灵活的控制请求是否走代理
配置略微繁琐,请求资源时必须加前缀
1.关于vue中proxy代理的一些理解 !!!加深理解
2.关于axios介绍和使用–面试题
axios的另一篇教程
跨域
-
Axios是一个基于promise的HTTP库,是目前最流行的ajax封装库之一,用于很方便地实现ajax请求的发送。
-
支持的功能:
从浏览器发出 XMLHttpRequests请求。
支持 Promise API。
能拦截请求和响应。
能转换请求和响应数据。
取消请求。
可以使用axios里面配置参数
document.getElementById("btn1").onclick = function(){
axios({
method:"GET",
url:"http://localhost:3000/posts/1"
}).then(response=>{
console.log(response);
})
};
也可以直接像APP中一样,使用axios.get方法
点我查看axios请求方法: get,post,put,patch,delete
get:获取数据
post:提交数据(表单提交+文件上传)
put:更新数据(所有数据推送到后端)
patch:更新数据(只将修改的数据推送到后端)
delete:删除数据
Vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
//开启代理服务器(是因为服务器1 student的端口号时是5000)
/*devServer: {
proxy: 'http://localhost:5000'
}*/
devServer: {
proxy: {
// '^/api': {
// target: 'http://localhost:5000',//接口地址
// // ws: true,//如果代理webSocket配置这个参数
// // changeOrigin: true//是否跨域
// },
// '^/foo': {
// target: '<other_url>'
// }
'/atguigu':{
target: 'http://localhost:5000',
pathRewrite:{'^/atguigu':''},
ws: true,//用于支持websocket
changeOrigin: false//用于控制请求头中的host值
},
'/demo':{
target: 'http://localhost:5001',
pathRewrite:{'^/demo':''},
ws: true,//用于支持websocket
changeOrigin: false
},
}
}
})
APP.vue
<template>
<div><button @click="getStudents">获取学生信息</button>
<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>
跨域
github案例
- 对所有的进行拆分,记得引入bootstrap.css的写法
- search进行发送请求
- 把search中的数据在list中显示(全局事件总线/消息订阅与发布/交给app )
- 遍历 / 显示加载中
- 遍历 / 显示加载中
App.vue
<template>
<div class="container">
<Search />
<List />
</div>
</template>
<script>
import List from "./components/List.vue";
import Search from "./components/Search.vue";
export default {
name: "App",
components: {
List,
Search,
},
};
</script>
<style >
</style>
Search.vue
<template>
<div>
<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="searchUsers">Search</button>
</div>
</section>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "Search",
data() {
return {
keyWord: "",
};
},
methods: {
searchUsers() {
//请求前更新List数据(发送数据)
this.$bus.$emit('updateListInfo',{isFirst:false,isLoading:true,errMsg:'',users:[]});
//怎么在字符串里引用关键字变量,不会将变量认错成字符串(模板字符串)
axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
(response) => {
//查看收到的数据是什么样的(下面的item就是这么写出来的,还有List组件data中的内容也是靠这个判断的)
console.log("请求成功了", response.data);
//不需要加载中那些花里胡哨,只传这一句也是可以的
// this.$bus.$emit('updateListInfo',{users:response.data.items})
//请求成功后更新List数据
this.$bus.$emit('updateListInfo',{isLoading:false,errMsg:'',users:response.data.items});
},
(error) => {
//请求失败后更新List数据
console.log("请求失败了", {isLoading:false,errMsg:error.message,users:[]});
}
);
},
},
};
</script>
<style>
</style>
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="user.html_url" target="_blank">
<img :src="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("updateListInfo", (isFirst, isLoading, errMsg, users) => {
console.log("我是List组件,收到了数据");
this.isFirst = isFirst;
this.isLoading = isLoading;
this.errMsg = errMsg;
this.users = users;
});*/
//接收数据
this.$bus.$on("updateListInfo", (dataObj) => {
console.log("我是List组件,收到了数据");
console.log(dataObj);
// this.info = dataObj; 没写的isFirst属性丢了
this.info = { ...this.info, ...dataObj };//es6语法拼串
});
},
};
</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: 0.75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: 0.75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
Vue-resource
npm i vue-resource
main.js
import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
const vm = new Vue({
el: '#app',
render: h => h(App),
//全局事件总线
beforeCreate() {
Vue.prototype.$bus=this
},
})
插槽
让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件 >>> 子组件
默认插槽
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.vue";
export default {
name: "App",
components: {
Category,
},
data(){
return{
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['教父','拆弹专家','你好','盖茨比'],
}
},
};
</script>
<style >
.container{
display: flex;
justify-content: space-around;
}
img{
width:100%;
}
video{
width:100%;
}
</style>
category.vue
<template>
<div class="category">
<h3>{{title}}</h3>
<!-- 定义一个插槽 (挖个坑,等着组件的使用者填空) -->
<slot>我是一个默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:"Category",
props:['title']
}
</script>
<style>
.category{
background-color: rgb(69, 155, 216);
width:200px;
height:300px;
}
h3{
background-color: orange;
text-align: center;
}
</style>
具名插槽
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 slot="footer"> 两种都可以 -->
<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.vue";
export default {
name: "App",
components: {
Category,
},
data() {
return {
foods: ["火锅", "烧烤", "小龙虾", "牛排"],
games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
films: ["教父", "拆弹专家", "你好", "盖茨比"],
};
},
};
</script>
<style >
.container {
display: flex;
justify-content: space-around;
}
img {
width: 100%;
}
video {
width: 100%;
}
.foot {
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
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>
.category{
background-color: rgb(69, 155, 216);
width:200px;
height:300px;
}
h3{
background-color: orange;
text-align: center;
}
</style>
作用域插槽
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
App.vue
<template>
<div class="container">
<!-- App使用Category组件,atguigu收到的是Category传来的所有,所以atguigu是一个对象 -->
<Category title="游戏">
<template scope="atguigu">
<ul>
<li v-for="(g, index) in atguigu.liuyue" :key="index">{{ g }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="{liuyue}">
<!-- es6解构赋值 把atguigu写成liuyue-->
<ol>
<li v-for="(g, index) in liuyue" :key="index">{{ g }}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<!-- 写成scope也可以-->
<template slot-scope="{ msg }">
<h4>
<li v-for="(g, index) in msg" :key="index">{{ g }}</li>
</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from "./components/Category.vue";
export default {
name: "App",
components: {
Category,
},
};
</script>
<style >
.container {
display: flex;
justify-content: space-around;
}
img {
width: 100%;
}
video {
width: 100%;
}
.foot {
display: flex;
justify-content: space-around;
}
h4 {
text-align: center;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{ title }}</h3>
<!-- 把games传给插槽的使用者 -->
<slot :liuyue="games" msg="DF">我是一个默认值,当使用者没有传递具体结构时,我会出现1</slot>
</div>
</template>
<script>
export default {
name: "Category",
props: ["title"],
data() {
return {
games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
};
},
};
</script>
<style>
.category {
background-color: rgb(69, 155, 216);
width: 200px;
height: 300px;
}
h3 {
background-color: orange;
text-align: center;
}
</style>
Vuex
vuex理解
专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
什么时候使用Vuex
1.多个组件依赖于同一状态
2.来自不同组件的行为需要变更同一状态
求和案例_纯vue版本
Count.vue
<template>
<div>
<h1>{{ sum }}</h1>
<select v-model="n">
<!-- 第一种方法:加上冒号,就当成纯粹的js表达式解析,就是数字
不加冒号,就是字符串,所以加减法的时候3+2="32"(字符串)
第二种方法: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 {
sum: 0, //当前的和
n: 1, //用户选择的数字
};
},
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: auto 5px;
}
</style>
App.vue
<template>
<div >
<Count/>
</div>
</template>
<script>
import Count from "./components/Count.vue";
export default {
name: "App",
components: {
Count,
},
};
</script>
<style >
</style>
Vuex环境
任何一个组件,都可以调用dispatch和Commit,他们都在store身上,Actions,Mutations,State都是对象{}
Vue2==>vuex3
Vue3==>vuex4
首先下载npm i vuex@3
index.js
与components同级创建一个store文件夹,里面有index.js
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
const actions={}
//准备mutations,用于操作数据(state)
const mutations={}
//准备state,用于存储数据
const state={}
//创建store
const store=new Vuex.Store({
actions:actions,//对象的key和保存对应值的变量重名了,可以简写
mutations:mutations,
state:state,
})
//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的
main.js
import Vue from 'vue'
import App from './App.vue'
import store from "./store"
//引入Vuex
//import Vuex from 'vuex'
Vue.config.productionTip=false;
//Vue.use(Vuex)
new Vue({
el:"#app",
render:h=>h(App),
store,
})
- 直接引入store就行,不需要写store下的index.js
- 注掉并将Vue.use(Vuex)写在index文件是因为在引入store的时候,我们需要先使用Vuex,而且import不论写的顺序在哪,引入store一定是先执行的,所以为了能使用store,我们将Vue.use(Vuex)写在store中,这样就是先使用Vuex,再引入store。
求和案例_Vuex版本
对外暴露
// 对外暴露实例
export default new Vuex.Store()
App.vue
<template>
<div >
<Count/>
</div>
</template>
<script>
import Count from "./components/Count.vue";
export default {
name: "App",
components: {
Count,
},
};
</script>
main.js
import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入Vuex
import Vuex from 'vuex'
import store from './store/index'
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
const vm = new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
},
store,
})
index.js
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
//content代表上下文
const actions = {
/* jia(context, value) {
console.log("action中的jia被调用了");
console.log(context, value);//可以查看一下输出的上下文是什么,以及在哪里找到sum
context.commit('JIA', value)
},
jian(context, value) {
console.log("action中的jian被调用了");
console.log(context, value);
context.commit('JIAN', value)
},*/
jiaOdd(context, value) {
console.log("action中的jiaOdd被调用了");
if (context.state.sum%2)
context.commit('JIA', value)
},
jiawait(context, value) {
console.log("action中的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
const store = new Vuex.Store({
actions: actions,//对象的key和保存对应值的变量重名了,可以简写
mutations: mutations,
state: state,
})
//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的
Count.vue
<template>
<div>
<h1>{{ $store.state.sum }}</h1>
<select v-model="n">
<!-- 第一种方法:加上冒号,就当成纯粹的js表达式解析,就是数字
不加冒号,就是字符串,所以加减法的时候3+2="32"(字符串)
第二种方法: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.dispatch("jia", this.n);
this.$store.commit("JIA", this.n);
},
decrement() {
this.$store.commit("JIAN", this.n);
},
incrementOdd() {
/* if (this.$store.state.num % 2) {
this.$store.dispatch("jia", this.n);
}*/
this.$store.dispatch("jiaOdd", this.n);
},
incrementWait() {
setTimeout(() => {
this.$store.dispatch("jiawait", this.n);
}, 500);
},
mounted() {
console.log(this);//直接可以查看store,进而判断属性方法如何调用
},
},
};
</script>
<style>
button {
margin: auto 5px;
}
</style>
Getters和四种map方法
…mapState({
FloorList:(state)=>{return state.home.FloorList}
})
Count.vue
<template>
<div>
<!-- <h1>当前求和为:{{ $store.state.sum }}</h1> -->
<h1>当前求和为:{{ he }}</h1>
<h1>当前求和放大10倍:{{ bigSum }}</h1>
<!-- <h3>我在{{ $store.state.school }},学习{{$store.state.subject}}</h3> -->
<h3>我在{{ xuexiao }},学习{{ subject }},这是第二种写法</h3>
<select v-model="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 { mapGetters, mapState, mapMutations, mapActions } from "vuex";
export default {
name: "Count",
data() {
return {
n: 1, //用户选择的数字
};
},
methods: {
/* increment() {
// this.$store.dispatch("jia", this.n);
this.$store.commit("JIA", this.n);
},
decrement() {
this.$store.commit("JIAN", this.n);
},
*/
/* 方法不写小括号,不代表不传参数,而是默认的事件对象event
所以使用时,上面的方法要加参数
借助mapMutations生成对应的方法,方法会调用commit去联系mutations(对象写法)
*/
...mapMutations({ increment: "JIA", decrement: "JIAN" }),
/*数组写法,但是记得上面的方法名字也要改成JIA,否则错误
...mapMutations(['JIA','JIAN']),
*/
/* incrementOdd() {
this.$store.dispatch("jiaOdd", this.n);
},
incrementWait() {
setTimeout(() => {
this.$store.dispatch("jiawait", this.n);
}, 500);
}
借助mapActions生成对应的方法,方法会调用dispatch去联系actions(对象写法)
*/
...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiawait" }),
// ...mapActions(['jiaOdd','jiawait'])
},
computed: {
/*这是程序员自己亲自去写的计算属性
he() {
return this.$store.state.sum;
},*/
/*1. mapState是一个对象,computed也是一个对象,我们在{}中再写{},是错的
解决方法,里面写成...{} es6语法,把每一对key value都展开
借助mapState生成计算属性,从State中读取属性
你给他起啥名字,就调用哪个名字(key)值
*/
...mapState({ he: "sum", xuexiao: "school", subject: "subject" }),
/*2.借助mapState生成计算属性(数组写法)保证调用的名字和属性的名字一致
...mapState(['sum','school','name']), */
...mapGetters(["bigSum"]),
},
mounted() {
const x = mapState({ he: "sum", xuexiao: "school", subject: "subject" });
console.log("我是验证时候的x,通过查看,我是一个对象", x);
},
};
</script>
<style>
button {
margin: auto 5px;
}
</style>
index.js
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
//content代表上下文
const actions = {
/* jia(context, value) {
console.log("action中的jia被调用了");
console.log(context, value);//可以查看一下输出的上下文是什么,以及在哪里找到sum
context.commit('JIA', value)
},
jian(context, value) {
console.log("action中的jian被调用了");
console.log(context, value);
context.commit('JIAN', value)
},*/
jiaOdd(context, value) {
console.log("action中的jiaOdd被调用了");
if (context.state.sum%2)
context.commit('JIA', value)
},
jiawait(context, value) {
console.log("action中的jiawait被调用了");
setTimeout(()=>{
context.commit('JIA', value)
},500);
},
}
//准备mutations,用于操作数据(state)
const mutations = {
JIA(state, value) {
console.log("mutations中的JIA被调用了",value);
state.sum += value;
},
JIAN(state, value) {
console.log("mutations中的JIAN被调用了");
state.sum -= value;
},
}
//准备state,用于存储数据
const state = {
sum: 0, //当前的和
school:'尚硅谷',
subject:"计网",
}
//准备getters,用于将state中的数据进行加工
const getters={
bigSum(state){
return state.sum*10;
}
}
//创建store
const store = new Vuex.Store({
actions: actions,//对象的key和保存对应值的变量重名了,可以简写
mutations: mutations,
state: state,
getters,
})
//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的
main.js 和 App.vue同上
在项目中的应用
1.首先在api中写请求接口
在仓库中,可以新建一个模块文件夹,引入接口,四步,暴露出来
在index.vue中发起请求 dispatch
多组件共享数据
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>
index.js
就是加了一个ADD_PERSON方法和PersonList属性
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
const actions = {
jiaOdd(context, value){
console.log("actions+jiaOdd被调用了");
if(state.sum%2){
context.commit("JIA", value);
}
},
jiawait(context, value){
console.log("actions+jiawait被调用了");
context.commit("JIA", value);
}
}
//准备mutations,用于操作数据state
const mutations = {
JIA(context, value) {
console.log("mutations中的JIA被调用了");
state.sum += value
},
JIAN(context, value){
console.log("mutations中的JIAN被调用了");
state.sum-=value
},
ADD_PERSON(state, value) {
console.log("mutations中的value(personObj)被调用了");
state.personList.unshift(value)
}
}
//准备state,用于存储数据
const state = {
sum: 0,
personList:[
{id:'001',name:'JOJO'}
],
}
const getters={
bigSum(state){
return state.sum*10
}
}
//创建store
export default new Vuex.Store({
actions,
mutations,
state,
getters,
})
Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h1>{{ bigSum }}</h1>
<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
<select v-model="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 { mapGetters, mapState, mapMutations, mapActions } from "vuex";
export default {
name: "Count",
data() {
return {
n: 1,
};
},
methods: {
...mapMutations({ increment: "JIA", decrement: "JIAN" }),
...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiawait" }),
},
computed: {
...mapState([ "sum","personList" ]),
...mapGetters(["bigSum"]),
},
};
</script>
<style>
button {
margin: 5px;
}
</style>
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 $store.state.personList" :key="p.id">{{p.name}}</li>
</ul>
</div>
</template>
<script>
import {nanoid} from "nanoid"
export default {
name:"Person",
data(){
return{
name:"",
}
},
computed:{
sum(){
return this.$store.state.sum
}
},
methods:{
add(){
const personObj={id:nanoid(),name:this.name}
this.$store.commit("ADD_PERSON",personObj)
this.name=''
}
}
}
</script>
<style>
</style>
Vue模块化+命名空间
让代码更好维护,让多种数据分类更加明确
main.js
import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入Vuex
import Vuex from 'vuex'
import store from './store/index'
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
const vm = new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
},
store,
})
App.vue
<template>
<div>
<Count />
<Person />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Person from "./components/Person.vue";
export default {
name: "App",
components: {
Count,
Person,
},
};
</script>
index.js
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
import axios from 'axios'
import {nanoid} from 'nanoid'
Vue.use(Vuex)
//求和相关的配置
const countOptions = {
//开启命名空间
namespaced: true,
actions: {
jiaOdd(context, value) {
console.log("action中的jiaOdd被调用了");
if (context.state.sum % 2)
context.commit('JIA', value)
},
jiawait(context, value) {
console.log("action中的jiawait被调用了");
setTimeout(() => {
context.commit('JIA', value)
}, 500);
},
},
mutations: {
JIA(state, value) {
console.log("mutations中的JIA被调用了", value);
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: {
addPersonWang(context, value) {
//value是person对象,可以log查看一下
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中的value(personObj)被调用了");
state.personList.unshift(value)
}
},
state: {
personList: [{ id: "001", name: "张三" }],
},
getters: {
//这个地方的state是自己的state,而不是总的state
firstPersonName(state) {
return state.personList[0].name;
}
},
}
//创建store
export default new Vuex.Store({
modules: {
countAbout: countOptions,
personAbout: personOptions,
}
})
person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 class="cnt">Count组件的求和为{{ sum }}</h3>
<h3>列表中第一个人的名字是:{{ firstPersonName }}</h3>
<input type="text" placeholder="请输入名字" v-model="name" />
<button @click="add">添加</button>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPersonServer">添加一个人,名字随机</button>
<ul>
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
name: "Person",
data() {
return {
name: "",
};
},
computed: {
//这两种写法都可以
// ...mapState(["personList","sum"]),
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 };
console.log(personObj);
this.$store.commit("personAbout/ADD_PERSON", personObj);
this.name = "";
},
addWang() {
//commit联系mutation,dispatch联系actions
const personObj = { id: nanoid(), name: this.name };
this.$store.dispatch("personAbout/addPersonWang", personObj);
this.name = "";
},
addPersonServer(){
this.$store.dispatch("personAbout/addPersonServer");
}
},
};
</script>
<style>
</style>
Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h1>当前求和放大10倍:{{ bigSum }}</h1>
<h3>我在{{ school }},学习{{ subject }},这是第二种写法</h3>
<h3 class="cnt">下方组件的总人数是:{{ personList.length }}</h3>
<select v-model="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 { mapGetters, mapState, mapMutations, mapActions } from "vuex";
export default {
name: "Count",
data() {
return {
n: 1, //用户选择的数字
};
},
methods: {
...mapMutations("countAbout", { increment: "JIA", decrement: "JIAN" }),
...mapActions("countAbout", {
incrementOdd: "jiaOdd",
incrementWait: "jiawait",
}),
},
computed: {
//未开启命名空间 ...mapState({ countAbout:"countAbout",personAbout:"personAbout" }),
...mapState("countAbout", ["sum", "school", "subject"]),
...mapState("personAbout", ["personList"]),
...mapGetters("countAbout", ["bigSum"]),
},
mounted() {
// const x = mapState({ he: "sum", xuexiao: "school", subject: "subject" });
// console.log("我是验证时候的x,通过查看,我是一个对象", x);
console.log(this.$store);
},
};
</script>
<style>
button {
margin: auto 5px;
}
.cnt {
background-color: rgb(236, 153, 154);
width: 300px;
}
</style>
单独拆分
也可以吧person和count单独拆分成.js
注意写法上
修改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读取:
...mapState('countAbout',['sum','school','subject']),
开启命名空间后,组件读取state数据
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch:
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
开启命名空间后,组件中调用commit:
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
路由
-
什么是路由?
- 一个路由就是一组映射关系(key - value)
- key 为路径,value 可能是 function 或 component(组件)
-
后端路由:
- 理解:value 是 function,用于处理客户端提交的请求
- 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
-
前端路由:
- 理解:value 是 component(组件),用于展示页面内容
- 工作过程:当浏览器的路径改变时,对应的组件就会显示
路由基本使用
首先把index.html中的bootstrap.css使用打开
注意新建一个router文件夹,把index.js放入
新建一个pages文件夹,把About和Home放入
在路由配置中是routes,component,而不是 routers,components,
下载vue-router:
npm i vue-router@3
main.js
import Vue from 'vue'
//引入vue-router
import VueRouter from 'vue-router'
import App from './App.vue'
//引入路由文件
import router from './router'
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)
const vm = new Vue({
el: '#app',
render: h => h(App),
router,
})
bootstrap.css
App.vue
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
这个地方是有一个标题内容的
</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>
About.vue
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name:"About",
}
</script>
<style>
</style>
Home.vue
<template>
<h2>我是Home的内容</h2>
</template>
<script>
export default {
name:"Home",
}
</script>
<style>
</style>
index.js
//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
//创建并暴露一个路由器
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
},
]
})
一般组件,路由组件
-
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
-
挂载和销毁在整个过程中都是不断重复的
-
每个组件都有自己的$route属性,里面存储着自己的路由信息
-
整个应用只有一个router,可以通过组件的$router属性获取到
-
一般组件写在components中,路由组件写在pages中
router和route
$router:进行编程式导航的路由跳转
this.$router.push|this.$router.replace
$route:可以获取路由的信息|参数
this.$route.path
this.$route.params|query
this.$route.meta
嵌套路由(多级路由)
main.js
直接使用上面的即可
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,
//二级路由(home的子路由)
children:[
{
path: "news",//二级路由不要加/了
component: News,
},
{
path: "message",
component: Message,
},
]
},
]
})
Home.vue
<template>
<div class="panel-body">
<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>
</div>
</template>
<script>
export default {
name:"Home",/*
beforeDestroy(){
console.log("home组件即将被销毁了");
},
mounted(){
console.log("home组件挂载完毕",this);
},*/
}
</script>
<style>
</style>
About.vue
用不到,和上面的一样即可
Message.vue
<template>
<div>
<ul>
<li><a href="/message1">message001</a> </li>
<li><a href="/message2">message002</a> </li>
<li><a href="/message/3">message003</a> </li>
</ul>
</div>
</template>
<script>
export default {
name: "Message",
};
</script>
<style>
</style>
News.vue
<template>
<div>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</div>
</template>
<script>
export default {
name: "News",
};
</script>
<style>
</style>
App.vue
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<Banner/>
</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>
import Banner from "./components/Banner.vue"
export default {
name: "App",
components:{
Banner,
}
};
</script>
Banner.vue
<template>
<div class="page-header"><h2>Vue Router Demo</h2></div>
</template>
<script>
export default {
name:"Banner",
}
</script>
<style>
</style>
路由的重定向
- path中的内容指的是,我在URL路径中输入的信息,只要我输入*,就会跳转到指定是路径
- path中的参数还可以是/
- redirect中指的就是要跳转的地址
- 重定向的目标可以是一个对象,一个路由地址,一个命名的路由或者一个方法
- 1.路由地址
2、重定向的目标也可以是一个对象,包含路由地址:
const router = new Router({
routes: [
{ path: '*',
redirect: {path:'/home'}
}
]
})
3、重定向的目标也可以是
一个命名的路由:
const router = new Router({
routes: [
{ path: '*',
redirect: {name:'Home'}
}
]
})
4、重定向的目标可以是一个方法,动态返回重定向目标:
const router = new Router({
routes: [
{ path: '*',
redirect:(to)=>{
//to目标路由对象,就是访问的路径的路由信息
if(to.path==='/123'){
return '/about';
}else if(to.path=='/456'){
return '/document'
}else{
return '/home';
}
}
}
]
})
路由跳转传参query+路由命名
跳转的时候带着参数过去
通过这个,才可以把问号??替换掉
这种写法,一点不打扰路由的配置
Detail.vue
<template>
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</template>
<script>
export default {
name:"Detail",
mounted(){
//得到组件相关的路由信息
console.log("我是detail中输出的",this.$route);
}
}
</script>
<style>
</style>
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: [
//一级路由
{
name:"guanyu",
path: '/about',
component: About
},
{
path: '/home',
component: Home,
//二级路由(home的子路由)
children:[
{
path: "news",//二级路由不要加/了
component: News,
},
{
path: "message",
component: Message,
children:[
{ name:"xiangqing",
path:'detail',
component:Detail,
}
]
},
]
},
]
})
先把detail中的内容,使用 <router-view></router-view>
给呈现出来
先使用普通的路由,再使用特殊的
Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<!--跳转路由携带query参数,to的字符串写法 模板字符串混着js的东西 -->
<!-- <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', //(要用单引号,双引号会出错,因为外面有一个双引号了
//name:'xiangqing',//找到她的名字,呼唤她(path 和name写一个就行)
query:{
id:m.id,
title:m.title
},
}">
{{m.title}}
</router-link>
</li>
</ul>
<hr>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Message",
data() {
return {
messageList: [
{ id: "001", title: "消息001000" },
{ id: "002", title: "消息002" },
{ id: "003", title: "消息003" },
],
};
},
};
</script>
<style>
</style>
App.vue
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<Banner/>
</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="{path:'/about'}">About</router-link>
<router-link class="list-group-item " active-class="active" :to="{name:'guanyu'}">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
</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>
About.vue
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name:"About",
beforeDestroy(){
console.log("about组件即将被销毁了");
},
mounted(){
console.log("about组件挂载完毕",this);
//验证一下router是一样的,但是route不是
window.aboutRoute=this.$route
window.aboutRouter=this.$router
},
}
</script>
<style>
</style>
路由跳转传参 params
需要修改的地方
- 路由声明的地方(使用占位符)
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
- Message传递参数的时候
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:m.id,
title:m.title
}
}"
>跳转</router-link>
路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
- Detail接收参数:
$route.params.id
$route.params.title
Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<!--跳转路由携带query参数,to的字符串写法 模板字符串混着js的东西 -->
<!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link> -->
<!--跳转路由携带query参数,to的对象写法 模 -->
<router-link :to="{
name:'xiangqing',//使用params,一定用的是name,不能用path
params:{
id:m.id,
title:m.title
},
}">
{{m.title}}
</router-link>
</li>
</ul>
<hr>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Message",
data() {
return {
messageList: [
{ id: "001", title: "消息001000" },
{ id: "002", title: "消息002" },
{ id: "003", title: "消息003" },
],
};
},
};
</script>
<style>
</style>
Detail.vue
自行更改,只需要把上一个query全部替换为params
index.js
自己更改吧,只需要加上第一步的占位符
路由的props配置
Message.vue
可以直接使用上面的 ,需要注意的是,query参数和params参数的更替。
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: [
//一级路由
{
name: "guanyu",
path: '/about',
component: About
},
{
path: '/home',
component: Home,
//二级路由(home的子路由)
children: [
{
path: "news",//二级路由不要加/了
component: News,
},
{
path: "message",
component: Message,
children: [
{
name: "xiangqing",
path: 'detail/:id/:title',//占位符
component: Detail,
/*props的第一种写法,值为对象,
该对象中的所有key-value都会以props的形式传给Detail组件
props: { a: 999, b: "liuyue" }
*/
/*2.值为布尔值,
若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
path: 'detail',
props:true,
*/
/*3.值为函数*/
props($route){
return{
id:$route.params.id,
title:$route.params.title
/*如果Message中使用的query传值,那么这个地方可以更正为
id:$route.query.id,
title:$route.query.title
*/
}
},
/*
解构赋值
props({query}){
return{
id:query.id,
title:query.title
}
}
解构赋值的连续写法
props({query:{id,title}}){
return{
id:id,
title:title
}
}
*/
}
]
},
]
},
]
})
Detail.vue
<template>
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
<li>a:{{a}}</li>
<li>b:{{b}}</li>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</template>
<script>
export default {
name:"Detail",
props:['a','b',"id","title"],
mounted(){
//得到组件相关的路由信息
console.log(this.$route);
}
}
</script>
声明式路由导航router-link
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:push和replace,其中push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push方式
- 开启replace模式:
<router-link replace ...>News</router-link>
4.加上了replace属性之后,点击某一个,他不会在历史记录里留下她的上一个,也就是说直接把上一个链接替换掉,没办法返回到上一个页面
编程式路由导航this.$router.push
不借助router-link的导航
注意,我将所有的params都换成了query
goSearch() {
//获取input搜索框内的内容
// this.$router.push({ name: "search", query: { keyword: this.keyword } });
let location = { name: "search", params: { keyword: this.keyword } };
if (this.$route.query) {
location.query = this.$route.query;
}
// console.log(location);
this.$router.push(location);
},
sendGoodId(id){
this.$router.push({name:"detail",query:{skuId:id}})
}
Banner.vue
<template>
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="test">测试一下go</button>
</div>
</template>
<script>
export default {
name: "Banner",
methods:{
back(){
// console.log(this.$router);//还是在原型里面
this.$router.back()
},
forward(){
this.$router.forward()
},
test(){
//往前走几步
this.$router.go(3)
}
}
};
</script>
<style>
</style>
Detail.vue
<template>
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
<li>a:{{a}}</li>
<li>b:{{b}}</li>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</template>
<script>
export default {
name:"Detail",
props:['a','b',"id","title"],
mounted(){
//得到组件相关的路由信息
console.log(this.$route);
}
}
</script>
<style>
</style>
Message.vue
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link
:to="{
name: 'xiangqing', //使用params,一定用的是name,不能用path
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: "消息001000" },
{ id: "002", title: "消息002" },
{ id: "003", title: "消息003" },
],
};
},
methods: {
pushShow(m) {
// console.log(this.$router);//输出之后,在原形中找到push/replace
this.$router.push({
name: "xiangqing", //使用params,一定用的是name,不能用path
query: {
id: m.id,
title: m.title,
},
});
},
replaceShow(m){
this.$router.replace({
name: "xiangqing", //使用params,一定用的是name,不能用path
query: {
id: m.id,
title: m.title,
},
});
}
},
};
</script>
<style>
</style>
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: [
//一级路由
{
name: "guanyu",
path: '/about',
component: About
},
{
path: '/home',
component: Home,
//二级路由(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
}
},
}
]
},
]
},
]
})
缓存路由组件
让不展示的路由组件保持挂载,不被销毁
如果不写的话,就是都缓存
//缓存一个路由组件
<keep-alive include="News"> //include中写想要缓存的组件名,不写表示全部缓存
<router-view></router-view>
</keep-alive>
//缓存多个路由组件
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
记住,对news进行缓存,是要写在home组件中,想实现在input中输入内容,切换回来之后,内容依旧存在。
News.vue
<template>
<div>
<ul>
<li>news001</li><input type="text">
<li>news002</li><input type="text">
<li>news003</li><input type="text">
</ul>
</div>
</template>
<script>
export default {
name: "News",
beforeDestroy(){
console.log("News组件即将被销毁了");
//如果被销毁了,是会显示这句话的
}
};
</script>
<style>
</style>
两个新的生命周期钩子
Home.vue和上面的一样,加上一个keep-alive即可
News.vue
<template>
<div>
<ul>
<li :style="{ opacity }">欢迎学习Vue</li>
<li>news001</li>
<input type="text" />
<li>news002</li>
<input type="text" />
<li>news003</li>
<input type="text" />
</ul>
</div>
</template>
<script>
export default {
name: "News",
data() {
return {
opacity: 1,
}
},
/* mounted(){
this.timer=setInterval(()=>{
this.opacity-=0.01
if(this.opacity<=0)
this.opacity=1
},30)
},
*/
// beforeDestroy(){
// console.log("News组件即将被销毁了");
// //如果被销毁了,是会显示这句话的
// },
activated() {
//激活
console.log("News组件被激活了");
},
deactivated() {
console.log("News组件被失活了");
},
};
</script>
<style>
</style>
activated和deactivated是路由组件所独有的两个钩子,用于捕获路由组件的激活状态
activated路由组件被激活时触发
deactivated路由组件失活时触发
全局前置
首先设置,可以将代码中的School更改一下,查看
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
},
{
name: "zhuye",
path: '/home',
component: Home,
//二级路由(home的子路由)
children: [
{
name: "xinwen",
path: "news",//二级路由不要加/了
component: News,
},
{
name: "xiaoxi",
path: "message",
component: Message,
children: [
{
name: "xiangqing",
path: 'detail',
component: Detail,
props($route) {
return {
id: $route.query.id,
title: $route.query.title
}
},
}
]
},
]
},
]
})
//全局前置路由守卫,初始化的时候+每一次路由切换之前,调用函数
router.beforeEach((to, from, next) => {
// console.log("#");
console.log(to);
//先判断去的是哪里
// if (to.name === 'xinwen' || to.name === 'xiaoxi') {
if (to.path == '/home/news'||to.path == '/home/message') {
if (localStorage.getItem('school') === 'atguigu') {
next()//正确的时候,才会放行
}
else {
alert("学校名不对,无权限查看")
}
} else{//不是上面那两个,就放行
next()
}
})
export default router
后置路由守卫
有一个小bug,index.html这个地方的名字没有改,所以切换到 时候可能会出错
还可以把这个地方名字改了
//该文件用于创建整个应用的路由器
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: {
isAuth:true,
title: "关羽",
}
},
{
name: "zhuye",
path: '/home',
component: Home,
//二级路由(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("#");
console.log("前置路由守卫", to);
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
独享路由守卫
给哪个组件添加就把这个加在组件里,可以和后置路由搭配使用,可以随意搭配
//独享路由(需求,只对新闻这个路由做出响应)
beforeEnter: (to, from, next) => {
console.log("独享路由守卫", to);
if (to.meta.isAuth) {//判断是否需要鉴权
if (localStorage.getItem('school') === 'atguigu') {
next()//正确的时候,才会放行
}
else {
alert("学校名不对,无权限查看")
}
} else {//不是上面那两个,就放行
next()
}
}
组件内路由守卫
index.js用上面的
About.vue
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name: "About",
beforeDestroy() {
console.log("about组件即将被销毁了");
},
//通过路由规则(如果一开始通过新增一个组件,就不是通过规则进入的),进入该组件时被调用
beforeRouteEnter(to, from, next) {
console.log("beforeRouteEnter,进入了About", to);
if (to.meta.isAuth) {
//判断是否需要鉴权
if (localStorage.getItem("school") === "atguigu") {
next(); //正确的时候,才会放行
} else {
alert("学校名不对,无权限查看");
}
} else {
//不是上面那两个,就放行
next();
}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to, from, next) {
console.log("beforeRouteLeave,离开了About", to);
next()
},
};
</script>
<style>
</style>
history和hash 路由器的工作模式
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
hash模式:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
history模式:
- 地址干净,美观
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员去解决刷新页面服务端404的问题
新开一个终端,生成一个dist文件夹,npm run build
,这个文件就是后端需要的东西。
新建一个文件夹,比如叫demo,
在终端输入
npm init
变成一个合法的包,在中断中会出现一个起名字,atguigu_test_server 回车回车 (若干)
输入npm i express左边新建一个server.js,在里面写入代码
再新建一个static文件夹,把脚手架生成的东西加进去,引入到server,然后停到服务器,重新启动,可以在网站上直接访问
至于一些错误,后端解决,播放链接
https://www.bilibili.com/video/BV1Zy4y1K7SH?p=133