一、语法:this.$nextTick(回调函数)
二、作用:在下一次DOM更新结束后执行其指定的回调。
三、什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
四、需求:点击【编辑】按钮后,出现编辑输入框,并且输入框已经获焦。
五、思路:由todo.isEdit
变量来控制输入框的展示和隐藏:,点击【编辑】按钮后,执行handleEdit函数,
todo.isEdit
变量变为true,并让输入框获焦,其余情况todo.isEdit
变量为false,隐藏输入框,handleEdit函数代码如下:
handleEdit(todo){
if(todo.hasOwnProperty('isEdit')){// 看todo对象上有没有isEdit属性,有的话置true
todo.isEdit = true
}else{// 没有的话,添加并置true
this.$set(todo,'isEdit',true)
}
this.$refs.inputTitle.focus()
}
这样写代码根本无法实现点击【编辑】按钮后,出现编辑输入框,此时编辑输入框已经获焦这一需求。因为vue并不是像我们想的那样,发现你改数据了,立马帮你重新解析模板,他会等handleEdit中的代码都执行完了,再去重新解析模板。如果每次改了数据都去重新解析模板会拉低效率,所以vue等handleEdit中的代码都执行完了,再一起去更改数据,解析模板,所以上面执行了todo.isEdit = true
后,页面上并没有出现input框,然后就执行this.$refs.inputTitle.focus()
获取焦点,你现在压根拿不到input框,你也无法实现聚焦。
下面使用nextTick来解决这一问题
handleEdit(todo){
// 对象.hasOwnProperty('属性名'):对象身上是否有某个属性
if(todo.hasOwnProperty('isEdit')){// 有isEdit这个属性,改变它的值
todo.isEdit = true
}else{// 没有isEdit这个属性,新增
this.$set(todo,'isEdit',true)
}
//组件.$nextTick(回调函数):回调函数会在DOM节点更新后调用
this.$nextTick(function(){
this.$refs.inputTitle.focus()// 获取焦点
})
},
一、作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
二、写法:
v-enter
:进入的起点v-enter-active
:进入过程中v-enter-to
:进入的终点v-leave
:离开的起点v-leave-active
:离开过程中v-leave-to
:离开的终点
包裹要过度的元素,也就是说,你想让谁发生动画效果,你就把谁用transition包裹起来,并为
标签配置name属性:<transition name="hello">
<h1 v-show="isShow">你好啊!h1>
transition>
如果不指定name="hello"
属性,那么vue会在合适的时机给transition中的元素添加类名:.v-enter-active
;如果指定了,那么vue会在合适的时机给transition中的元素添加类名:.hello-leave-active
等。如果有多个元素有动画效果,那么一定要指定name属性,不然两个元素的动画就一样了
,且每个元素都要指定key值。三、将transition标签的appear设为true:
表明出现时就激活动画
<transition appear="true">transition>:transition标签中有个appear属性,其值为`"true"`(字符串类型)
<transition appear>transition>:让transition标签有appear属性
四、transition中的元素最终不会形成真正的元素,vue编译时会把transition标签去掉
五、transition其实就只是在特定时候给标签添加类名,该类名具体做什么过度/动画,都是程序员决定的
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏button>
<transition name="hello" appear>
<h1 v-show="isShow">你好啊!h1>
transition>
div>
template>
<script>
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
script>
<style scoped>
h1{
background-color: orange;
}
.hello-enter-active{// 进入时要激活的样式
animation: atguigu 0.5s linear;
}
.hello-leave-active{// 离开时要激活的样式
animation: atguigu 0.5s linear reverse;
}
//动画要使用@keyframes定义个关键帧
@keyframes atguigu {//@keyframes 动画名
//动画过程
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
style>
标签只能用在单个元素上,如果你有多个元素要实现动画效果,使用
标签,
标签中的每个元素都要有个唯一的key值
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏button>
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!h1>
<h1 v-show="isShow" key="2">尚硅谷!h1>
transition-group>
div>
template>
<script>
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
script>
<style scoped>
h1{
background-color: orange;
}
/*
进入的起点、离开的终点
只在进入的起点(第一帧)加.hello-enter,第二帧.hello-enter就被移除了,这个过程很快,我们很难捕捉到
*/
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
//整个进入过程、整个离开过程
.hello-enter-active,.hello-leave-active{
transition: 0.5s linear;
}
/*
进入的终点、离开的起点
只在离开的起点(第一帧)加.hello-leave,第二帧.hello-leave就被移除了,这个过程很快,我们很难捕捉到
*/
.hello-enter-to,.hello-leave{
transform: translateX(0);
}
style>
在vue中可以使用第三方成型的样式库/动画库来辅助我们快速实现炫酷的动画效果,如animate.css
npm install animate.css
import 'animate.css'//因为引入的是样式,不是JS模块,所以直接在组件中写:import 路径
或
标签添加:name="animate__animated animate__bounce"
或
标签添加:enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏button>
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1">你好啊!h1>
<h1 v-show="isShow" key="2">尚硅谷!h1>
transition-group>
div>
template>
<script>
import 'animate.css'//引入,因为引入的是样式,不是JS模块,所以直接写:import 路径
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
script>
<style scoped>
h1{
background-color: orange;
}
style>
一、方法一:在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
二、方法二:编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
说明:
vue.config.js中代码如下:
//本文件用于修改脚手架工作模式
//vue最终会把vue.config.js输送给webpack,webpack是基于Node的,所以vue.config.js中使用commonJS。
//配置方式参考:https://cli.vuejs.org/zh/config/
module.exports = {
pages: {
index: {
entry: 'src/main.js',//入口
},
},
lintOnSave:false, //关闭语法检查
/*
参考:https://cli.vuejs.org/zh/config/#devserver-proxy
开启代理服务器(方式一)
缺点:
1.不能配置多个代理;
2.不能灵活的控制代理是否转发请求,
你请求的资源是:http://localhost:8080/students,public是本服务器的根文件夹,即http://localhost:8080,
如果public/students存在,那么代理服务器会直接返回该资源,而不会发起请求。
但是如果我们希望代理服务器转发请求,这种配置方式无法做到。当你请求的资源不存在时,
无法控制他是否转发请求,他一定会转发请求。
*/
/* devServer: {
proxy: 'http://localhost:5000'//一会儿把请求转发给谁
}, */
//开启代理服务器(方式二)
devServer: {
proxy: {
/*
'/atguigu'和'/demo'代表请求前缀,请求前缀不匹配(没有/atguigu或/demo)则不转发请求
前缀紧跟在端口号后面
*/
'/atguigu': {// 请求前缀为/atguigu,即http://localhost:8080/atguigu/students,则配置如下:
target: 'http://localhost:5000',//一会儿把请求转发给谁
/*
代理服务器和浏览器都在http://localhost:8080,
假设现在App.vue中发起的请求为http://localhost:8080/atguigu/students,
这个请求是发给代理服务器的,代理服务器收到后,发现前缀是/atguigu,
会把这个请求转发给http://localhost:5000,
即代理服务器发起的请求是:http://localhost:5000/atguigu/students,会带上前缀,
如果我们不希望他带上前缀,则需要配置pathRewrite
*/
pathRewrite:{'^/atguigu':''},//把所有以atguigu开头的路径变成''
// ws: true, //用于支持websocket,默认为true,在react中默认为false
// changeOrigin: true //用于控制请求头中的host值(请求来自于哪里),默认为true,在react中默认为false。
// 为false则暴露自己(代理服务器)真实的url(http://localhost:8080),
// 为true则隐藏自己(代理服务器)真实的url,显示服务器的url(http://localhost:5000)
},
'/demo': {
target: 'http://localhost:5001',
pathRewrite:{'^/demo':''},
// ws: true, //用于支持websocket
// changeOrigin: true //用于控制请求头中的host值
}
}
}
}
使用axios发送网络请求
npm i axios
import axios from 'axios'
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 => 子组件。
分类:默认插槽、具名插槽、作用域插槽
子组件中:
<template>
<div>
<slot>插槽默认内容...slot>
div>
template>
父组件(使用者)中:
<Category>
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
Category>
子组件中:
<template>
<div>
<slot name="center">插槽默认内容...slot>
<slot name="footer">插槽默认内容...slot>
div>
template>
父组件(使用者)中:
<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>
标签中指定插入哪个插槽可以用v-slot:footer
代替slot='footer'
<Category>
<template slot="center">
<div>html结构1div>
template>
<template v-slot:footer>
<div>html结构2div>
template>
Category>
一、理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
二、作用域插槽的应用:数据不在你这儿,在Category组件中,你要用数据你又拿不到数据。那么可以:
<template>
<div>
<slot :games="games" msg="hello">我是默认的一些内容slot>
div>
template>
<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
}
},
}
script>
scope="atguigu"或scope="{games}"
,也可以为template标签指定slot-scope属性接收插槽传过来的数据:slot-scope="{games}"
。如果Category组件中使用具名插槽的话,需要在template标签中使用slot指定插槽名<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>
<style scoped>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
style>
一、概念:在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
二、何时使用:多个组件需要共享数据时
三、原理:vuex中数据保存在state中,state是个对象,如果某个组件要操作数据,那么组件调用dispatch('要进行的动作类型(比如加)',你要加几)
,然后你要加的动作和你要加的值都被传递到了actions中,actions也是个对象,里面存放键值对,里面肯定有一个键是你要操作的动作类型,值是个函数,传递到actions后,只要你要进行的动作类型和actions中的某个键一致,就会触发该键的值(函数)的调用,并把你要加的数据作为参数传递给函数,函数中要调用commit('要进行的动作类型(比如加)',你要加几)
,然后你要加的动作和你要加的值都被传递到了mutations中,mutations也是个对象,里面存放键值对,里面肯定有一个键是你要操作的动作类型,值是个函数,传递到mutations后,只要你要进行的动作类型和mutations中的某个键一致,就会触发该键的值(函数)的调用,并把state和你要加的数据作为参数传递给函数,在函数中进行数据操作更改state中的数据,然后vue会重新解析组件并渲染,于是页面上的数据也发生了相应的改变。
这么一看,actions好像是多余的,其实不然,有这么一个场景:当你要进行一个动作,但是进行这个动作的值需要发送Ajax请求才能获取,你就需要再actions中发送请求。但是如果,你知道进行动作的值,也就是不需要发请求,那么在组件中可以直接调用
commit('要进行的动作类型(比如加)',你要加几)
操作state中的数据。
⚠️其实你在actions中可以直接操作state中的数据,但是不建议这么做,因为开发者工具监视的是mutations,你在actions中更改state中的数据,这个操作是不会被开发者捕获到的。vue开发者工具和mutations对话,是因为mutations才是真正帮你修改state中数据的。
四、state、actions、mutations都需要store的管理,因为dispatch、commit都是store提供的
⚠️注意:在vue2中要使用vuex的3版本,在vue3中要使用vuex的4版本
一、在本项目终端安装:npm i vuex@3
二、引入:import Vuex from 'vuex'
三、使用:Vue.use(Vuex)
完成引入和使用,在创建vue实例时可以传入store配置项,配置好了后,vm及所有的vc身上都会有
$store
属性。
四、创建文件:src/store/index.js,index.js用于创建store。$store
属性他没有值啊,所以我们在index.js中创建$store
属性的值store。
五、index.js中定义actions、mutations、state三个对象,store管理这三个对象。
六、在index.js中使用new Vuex.Store({actions、mutations、state})
创建并暴露store
//引入Vue核心库
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
}
七、在main.js中引入暴露的这个store,并在创建vue实例时传入store配置项并将上述store配置给$store
属性
......
//引入store
import store from './store'
......
//创建vm
new Vue({
el:'#app',
render: h => h(App),
store
})
引入一个文件,会先把引入的这个文件中的代码运行完了再执行后面的代码。在脚手架中引入文件,他会扫描整个文件的import语句,并将他们按照你编写代码的顺序汇总到最上方,所以就算你把所有import语句写在了代码最下方,其实也是先执行import语句,不管你在两个import语句之间写了什么代码,都是先执行两个import语句,再执行两个import语句中间的代码
一、初始化数据、配置actions、配置mutations,操作文件store.js
import Vue from 'vue'//引入Vue核心库
import Vuex from 'vuex'//引入Vuex
Vue.use(Vuex)//应用Vuex插件
//准备actions对象——响应组件中用户的动作
const actions = {
// jia:function (){}可以简写为一个函数
jia(context,value){
//接收两个参数:第一个是context,它里面有commit、dispatch、state等,第二个是要操作的值
//在context中给你commit是为了让你继续调用mutations,给你state是为了方便你根据目前的数据做判断,
//给你dispatch是为了让你可以在actions中调用actions,如:context.dispatch('demo',value)
console.log('actions中的jia被调用了')
context.commit('JIA',value)
}
}
//准备mutations对象——修改state中的数据,mutations中的方法名用大写,用来区分actions中的方法
const mutations = {
JIA(state,value){
//接收两个参数,第一个是state,第二个是要操作的值
console.log('mutations中的JIA被调用了')
state.sum += value
}
}
//准备state对象——保存具体的数据
const state = {
sum:0 //当前的和
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
二、组件中读取vuex中的数据:$store.state.sum
三、组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
一、概念:state中的数据需要加工,并且加工数据的逻辑复杂并且逻辑需要复用,可以使用getters。
二、在store.js中追加getters配置
......
//准备getters——用于将state中的数据进行加工,类似于组件的computed
const getters = {
bigSum(state){
return state.sum*10// 一定要写返回值
}
}
//创建并暴露store
export default new Vuex.Store({
......
getters
})
三、组件中读取数据:$store.getters.bigSum
mapState方法:用于帮助我们映射state中的数据为计算属性
我们以前的代码是:
<h1>当前求和为:{{$store.state.sum}}h1>
<h3>当前求和放大10倍为:{{$store.state.bigSum}}h3>
<h3>我在{{$store.state.school}},学习{{$store.state.subject}}h3>
每次我们需要用vuex中的数据时,都要写$store.state
前缀,太麻烦了,我们可以用计算属性处理一下
computed:{
//靠程序员自己亲自去写计算属性
sum(){
return this.$store.state.sum
},
school(){
return this.$store.state.school
},
subject(){
return this.$store.state.subject
},
bigSum(){
return this.$store.getters.bigSum
}
}
但是在计算属性中也写了很多的this.$store.state
,我们可以引入vuex中的mapState来实现,我们只用给mapState传入计算属性的名字,以及要从state中读取的数据,他就会帮我们生成上述代码:
computed:{
//借助mapState生成计算属性,从state中读取数据。(对象写法)
//mapState接收一个对象,对象中是键值对,键是计算属性名,值是要从state中读取的数据,键值都是字符串,
//下面这行代码虽然表面上键是变量,但是vuex会将其进行处理,让他变成字符串形式。
//如果值不加'',那么会被理解为变量
// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),
//借助mapState生成计算属性,从state中读取数据。(数组写法)
//生成的计算属性名和从state中读取的数据名一致,可以使用数组的简写方法,表示计算属性名为sum,读取state中的sum变量
...mapState(['sum','school','subject'])
}
mapGetters方法:用于帮助我们映射getters中的数据为计算属性
computed: {
/* bigSum(){
return this.$store.getters.bigSum
}, */
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},
mapMutations方法:用于助我们生成与mutations对话的方法,即:包含$store.commit(xxx)
的函数
每次调用mutations都需要写this.$store.commit
,太冗余。可以使用mapMutations让vuex帮我们生成以下代码:
increment(){
this.$store.commit('JIA',this.n)
},
mapMutations的用法和mapState一致,mapMutations({increment:'JIA'})
代表生成的方法名为increment,要调用的是JIA这个mutations
⚠️在使用increment时要把值作为参数传进来。
methods:{
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
}
mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)
的函数
可以使用mapActions让vuex帮我们生成:
incrementOdd(){
this.$store.dispatch('jiaOdd',this.n)
}
mapActions的用法和mapMutations一致
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
mapActions与mapMutations使用时,若需要传递参数,需要在模板中绑定事件时传递好参数,否则参数是事件对象。
一、目的:让代码更好维护,让多种数据分类更加明确。
二、修改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']),
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
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'}),
这一部分详见代码
一、理解: 一个路由(route)就是一组映射关系(key - value),key指的是路径,路由的value指的是相应的组件或函数。
二、value是函数这种情况通常出现在后端路由中,用于处理客户端提交的请求,即你是什么路径,我就给你调用指定的函数相应你本次请求。
三、前端路由:key是路径,value是组件。
四、多个路由route需要路由器(router)进行管理。
五、路由route是为了实现单页面应用中页面的切换
六、在单页面应用中,路由器router会时刻监视URL的变化,获取路径(即端口号后面的东西),程序员在router中配置了路由规则(一组key-value的对应关系),如果路径变成了xxx,就展示相应的组件,如果在路由规则中没有配置某路径,那么访问该路径时不会展示任何东西
⚠️vue-router4只能在vue3中使用,vue-router3只能在vue2中使用
一、在项目终端安装vue-router:npm i vue-router@3
二、在main.js中引入:import VueRouter from 'vue-router'
三、在main.js中使用:Vue.use(VueRouter)
完成以上三步就可以在new Vue
时配置router项,下面创建router项的值
四、新建router/index.js,在该文件中使用new VueRouter({配置项对象})
创建路由器router并暴露
import VueRouter from 'vue-router'//引入VueRouter
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建并暴露一个路由器,去管理一组一组的路由规则。路由器router管理的一堆路由route,所以配置对象里是routes,期望routes是一个数组
export default new VueRouter({
routes:[//在里面写一组一组的路由,每组路由都是一个配置对象
//这是个配置对象,配置对象最大的特点就是里面的每一项都是按照设计好的来的,你不能在这里面添加稀奇古怪的属性
{// 如果路径为about,展示About组件
path:'/about',
component:About
},
{// 如果路径为home,展示Home组件
path:'/home',
component:Home
}
]
})
五、在main.js中引入router
import router from './router'//引入路由器
new Vue({//创建vm
el:'#app',
render: h => h(App),
router:router
})
原始html中我们使用a标签实现页面的跳转
<a class="list-group-item active" href="./about.html">Abouta>
<a class="list-group-item" href="./home.html">Homea>
Vue中借助router-link标签实现路由的切换,to属性指定点击后路径变为啥,router-link标签会被转成a标签
active-class指定该元素被激活时的样子,相当于告诉路由,一会儿有人点你的时候,你把active-class中的样式加到自己身上
<router-link class="list-group-item" active-class="active" to="/about">Aboutrouter-link>
<router-link class="list-group-item" active-class="active" to="/home">Homerouter-link>
指定展示位置:
⚠️几个注意点
$route
属性,里面存储着自己的路由信息。$router
属性获取到。一、配置路由规则,使用children配置项:
routes:[
{
path:'/about',
component:About
},
{
path:'/home',// 一级路由要加/
component:Home,
children:[// 配置Home路由下的子路由,也是个数组,因为Home路由下可能有很多个子路由
{
path:'news',// 某个一个路由的子路由不加/,因为人家底层在遍历规则时,已经给你加上/了,
component:News,
},
{
path:'message',//此处一定不要写:/message
component:Message,
}
]
}
]
跳转时,如果路由链接是二级路由或子级路由,必须带着父亲的路径,也就是写完整路径:
一、传递参数
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}router-link>
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
{{m.title}}
router-link>
二、接收参数:传过来的数据都放在了$route.query中
<li>消息编号:{{$route.query.id}}li>
<li>消息标题:{{$route.query.title}}li>
一、作用:可以简化路由的跳转
二、如何使用:
routes:[
{
name:'guanyu',//使用name属性给路由起名字,你给谁起名字,就给谁的配置项写name属性
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
name:'xiangqing',
path:'detail',
component:Detail,
}
]
}
]
}
]
跳转
跳转
<router-link :to="{
// path:'/home/message/detail',
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
}">
{{m.title}}
router-link>
一、配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
//配置路由时,告诉他detail后面的是参数
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}
二、传递参数
跳转
?
,直接把参数拼进去,那这样路由可能就会把参数当成路径,所以我们在配置路由时,要在路径后面使用:
指定占位符<router-link
:to="{
name:'xiangqing',// 如果携带的是params参数,这里只能用name指定路由,不能用path
params:{
id:666,
title:'你好'
}
}"
>跳转router-link>
三、接收参数
<li>消息编号:{{$route.params.id}}li>
<li>消息标题:{{$route.params.title}}li>
一、作用:让路由组件更方便的收到参数
二、 之前我们的代码:
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
每次都要写$route.params
,太冗余,有没有一种办法让我们在这里直接写id或title?此时你可能想到用计算属性,但是计算属性里依旧没避免每个计算属性里都有this.$route.query
,太多余,我们可以在配置路由时,给接收数据的路由配置props属性,详见router/index.js:
routes:[
{
path:'/home',
component:Home,
children:[
{
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){//函数会收到一个参数$route
return {// 返回值必须是对象,数据放在对象
//所有键值对都会以props的形式传给Detail组件
id:$route.query.id,//必须是键值对
title:$route.query.title,
a:1,
b:'hello'
}
}
}
]
}
]
}
]
然后在接收数据的组件中配置props来接收数据:
<template>
<ul>
<li>消息编号:{{id}}li>
<li>消息标题:{{title}}li>
ul>
template>
<script>
export default {
name:'Detail',
//props:['a','b'],//router/index.js中配置的props是一个对象
props:['id','title'],//router/index.js中配置的props是一个布尔值
//props:['id','title','a','b'],//router/index.js中配置的props是一个函数
}
script>
跳转路由时:
<router-link :to="{
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
}">
{{m.title}}
router-link>
一、作用:控制路由跳转时操作浏览器历史记录的模式
二、路由对浏览器历史记录的影响:浏览器中有两个常用的按钮:后退、前进,这两个按钮都是依赖于浏览器的历史记录来工作。使用
标签跳转链接,每次点击都会形成历史记录,且默认的操作模式是push,不破坏任何一条记录,不断往里面压入记录。对历史记录的操作除了push还有replace,replace最大的作用就是替换掉栈顶那一条。也就是说,浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
三、如何开启replace模式,给
标签添加replace属性,即可开启replace模式:
一、有时候我们不能使用
标签实现跳转:
标签了,因为
标签最终转成了a标签,而我们要的是button二、编程式路由导航:不借助
实现路由跳转的路由导航。让路由跳转更加灵活
三、具体编码:
//$router的两个API
//不要写route,route只是个规则,别人去运用这个规则,router才有指挥权
this.$router.push({// 接收一个配置对象作为参数
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
//this.$router.go(n):n>0标签前进n步,n<0表示后退|n|步
一、作用:让不展示的路由组件保持挂载,不被销毁。
二、具体编码:
<keep-alive include="News">
<router-view>router-view>
keep-alive>
<keep-alive>
<router-view>router-view>
keep-alive>
三、路由组件有两个独有的钩子,用于捕获路由组件的激活状态:
activated路由组件被激活时触发。
deactivated路由组件失活时触发。
一、作用:对路由进行权限控制
二、分类:全局守卫、独享守卫、组件内守卫
一、路由暴露前为其添加全局路由守卫
二、全局前置路由守卫:初始化的时候被调用、每次路由切换之前被调用
router.beforeEach(函数)
指定一个函数,在每次初始化时、每次路由切换之前都会调用这个函数。router.beforeEach(函数)
中不进行下一步。router.beforeEach((to,from,next)=>{
console.log('前置路由守卫',to,from)
// if (to.path === '/home/news' || to.path === '/home/message'){//也可以使用这种方法判断是否需要鉴权
// if (to.name === 'xinwen' || to.name === 'xiaoxi'){//也可以使用这种方法判断是否需要鉴权
routes:[
{//这是个配置对象,配置对象最大的特点就是里面的每一项都是按照设计好的来的,你不能在这里面添加稀奇古怪的属性
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:true,title:'关于'}
}
]
to.meta.isAuth
是否为true,为true则鉴权if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
三、全局后置路由守卫:初始化的时候被调用、每次路由切换之后被调用
router.afterEach(函数)
指定一个函数,在每次初始化时、每次路由切换之后都会调用这个函数router.afterEach(函数)
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})
一、独享路由守卫:某一个路由所单独享用的路由守卫,只有前置路由守卫。
二、
routes:[
{
name:'zhuye',
path:'/home',
component:Home,
meta:{title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'},
/*
进入News组件前会调用beforeEnter指定的函数,该函数同样会收到to、from、next三个参数,
用法同全局路由守卫
*/
beforeEnter: (to, from, next) => {
console.log('独享路由守卫',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
}
}
]
}
]
组件内路由守卫:在组件里写路由守卫。当你想给某组件单独写一些逻辑,可以在组件内路由守卫中实现
通过路由规则,进入组件的过程:点击后路径变成/about,前端路由器检测到路径的变化,匹配规则后进入组件展示
不通过路由规则进入组件:比如我一打开页面,xx组件就展示了(通过在页面中写展示组件),xx组件就不是通过路由规则进入组件的
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()
}
一、对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
二、hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
三、hash模式:(默认)
四、history模式:
npm i connect-history-api-fallback
const history = require(connect-history-api-fallback)
const app = express() app.use(history)
app.use(express.static(__dirname+'/static'))//使用静态资源
五、路由模式默认为hash模式,可以在创建路由器时,通过指定mode配置项将路由模式改为history模式:new VueRouter({mode:'history'})