目录

  • nextTick
  • 过度与动画
  • vue脚手架配置代理服务器
  • 插槽
    • 默认插槽
    • 具名插槽
    • 作用域插槽
  • vuex
    • 搭建vuex环境
    • 基本使用:
    • getters的使用
    • 四个map方法的使用
      • mapState方法
      • mapGetters方法
      • mapMutations方法
      • mapActions方法
    • 模块化+命名空间
  • 路由
    • 基本使用
    • 多级路由/嵌套路由
    • 路由的query参数
    • 命名路由
    • 路由的params参数
    • 路由的props配置
    • router-link标签的replace属性
    • 编程式路由导航
    • 缓存路由组件
    • 路由守卫
      • 全局守卫
      • 独享守卫
      • 组件内路由守卫
    • 路由器的两种工作模式

nextTick

一、语法:this.$nextTick(回调函数)
二、作用:在下一次DOM更新结束后执行其指定的回调。
三、什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
四、需求:点击【编辑】按钮后,出现编辑输入框,并且输入框已经获焦。
五、思路:由todo.isEdit变量来控制输入框的展示和隐藏:<input type="text" v-show="todo.isEdit">,点击【编辑】按钮后,执行handleEdit函数,todo.isEdit变量变为true,并让输入框获焦,其余情况todo.isEdit变量为false,隐藏输入框,handleEdit函数代码如下:

handleEdit(todo){if(todo.hasOwnProperty('isEdit')){// 看todo对象上有没有isEdit属性,有的话置truetodo.isEdit = true}else{// 没有的话,添加并置truethis.$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元素时,在合适的时候给元素添加样式类名。
二、写法:

  1. 准备好样式:
    元素进入的样式:
    (1)v-enter:进入的起点
    (2)v-enter-active:进入过程中
    (3)v-enter-to:进入的终点
    元素离开的样式:
    (1)v-leave:离开的起点
    (2)v-leave-active:离开过程中
    (3)v-leave-to:离开的终点
  2. 使用<transition>包裹要过度的元素,也就是说,你想让谁发生动画效果,你就把谁用transition包裹起来,并为<transition>标签配置name属性:
<transition name="hello"><h1 v-show="isShow">你好啊!</h1>
</transition>

如果不指定name="hello"属性,那么vue会在合适的时机给transition中的元素添加类名:.v-enter-active;如果指定了,那么vue会在合适的时机给transition中的元素添加类名:.hello-leave-active等。如果有多个元素有动画效果,那么一定要指定name属性,不然两个元素的动画就一样了

  1. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

三、将transition标签的appear设为true:<transition :appear="true"></transition>表明出现时就激活动画

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

<transition>标签只能用在单个元素上,如果你有多个元素要实现动画效果,使用<transition-group>标签,<transition-group>标签中的每个元素都要有个唯一的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

  1. 在项目终端安装:npm install animate.css
  2. 引入:import 'animate.css'//因为引入的是样式,不是JS模块,所以直接在组件中写:import 路径
  3. <transition><transition-group>标签添加:name="animate__animated animate__bounce"
  4. <transition><transition-group>标签添加:
    (1)进入的动画:enter-active-class="animate__swing"
    (2)离开的动画:leave-active-class="animate__backOutUp"
    其中,animate__swing、animate__backOutUp都是在animate.css中找的
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-group appearname="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脚手架配置代理服务器

一、方法一:在vue.config.js中添加如下配置:

devServer:{proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

二、方法二:编写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': ''}}}}
}

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

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发送网络请求

  1. 在项目终端安装axios:npm i axios
  2. 在使用的组件中引入:import axios from 'axios'

插槽

作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 => 子组件。
分类:默认插槽、具名插槽、作用域插槽

默认插槽

子组件中:

<template><div><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot>插槽默认内容...</slot></div>
</template>

父组件(使用者)中:

<Category><!-- 这个img标签是在本组件完成解析后再塞到Category组件中,所以你如果想给img标签添加样式,应该在本组件添加 --><img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>

具名插槽

子组件中:

<template><div><!--定义一个插槽(挖个坑,等着组件的使用者进行填充)如果组件中有多个插槽,要给每个插槽设置name属性--><slot name="center">插槽默认内容...</slot><slot name="footer">插槽默认内容...</slot></div>
</template>

父组件(使用者)中:

  1. 方法一:通过slot属性指定放入哪个插槽
<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>
  1. 方法二:在<template>标签中指定插入哪个插槽可以用v-slot:footer代替slot='footer'
<Category><template slot="center"><div>html结构1</div></template><template v-slot:footer><div>html结构2</div></template>
</Category>

作用域插槽

一、理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
二、作用域插槽的应用:数据不在你这儿,在Category组件中,你要用数据你又拿不到数据。那么可以:

  1. 在Category组件中定义插槽,在插槽中把数据传过去
    子组件中:
<template><div><!--数据在组件中,根据数据所生成的结构由组件的使用者决定下面一行代码会把games和msg传给插槽的使用者--><slot :games="games" msg="hello">我是默认的一些内容</slot></div>
</template><script>export default {name:'Category',props:['title'],//数据在子组件自身data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},}
</script>
  1. 使用插槽的组件将要放在插槽中的内容用template标签包裹,template标签指定scope属性接收插槽传过来的数据,有以下两种方法指定scope的值:scope="atguigu"或scope="{games}",也可以为template标签指定slot-scope属性接收插槽传过来的数据:slot-scope="{games}"。如果Category组件中使用具名插槽的话,需要在template标签中使用slot指定插槽名
    父组件中:
<template><div class="container"><Category title="游戏"><!--ul标签及其中的内容是你要放在插槽中的内容,他们的外面必须用template标签包裹,template标签指定scope属性接收插槽传过来的数据。template标签的atguigu变量会收到插槽传过来的数据,atguigu收到的是个对象,里面存放着键值对,键是插槽传过来的变量名,值是数据。即:atguigu={'games':['红色警戒','穿越火线','劲舞团','超级玛丽'], 'msg':"hello"}--><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>

vuex

一、概念:在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提供的

搭建vuex环境

⚠️注意:在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

getters的使用

一、概念: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

四个map方法的使用

mapState方法

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

mapGetters方法:用于帮助我们映射getters中的数据为计算属性

computed: {/* bigSum(){return this.$store.getters.bigSum}, *///借助mapGetters生成计算属性:bigSum(对象写法)...mapGetters({bigSum:'bigSum'}),//借助mapGetters生成计算属性:bigSum(数组写法)...mapGetters(['bigSum'])
},

mapMutations方法

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

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({//创建vmel:'#app',render: h => h(App),router:router
})

原始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标签实现路由的切换,to属性指定点击后路径变为啥,router-link标签会被转成a标签
active-class指定该元素被激活时的样子,相当于告诉路由,一会儿有人点你的时候,你把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>

指定展示位置:<router-view></router-view>
⚠️几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

多级路由/嵌套路由

一、配置路由规则,使用children配置项:

routes:[{path:'/about',component:About},{path:'/home',// 一级路由要加/component:Home,children:[// 配置Home路由下的子路由,也是个数组,因为Home路由下可能有很多个子路由{path:'news',// 某个一个路由的子路由不加/,因为人家底层在遍历规则时,已经给你加上/了,component:News,},{path:'message',//此处一定不要写:/messagecomponent:Message,}]}
]

跳转时,如果路由链接是二级路由或子级路由,必须带着父亲的路径,也就是写完整路径:<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>

路由的query参数

一、传递参数

  1. 跳转路由并携带query参数,to的字符串写法:
<!-- :to="`/home/message/detail?id=${m.id}&title=${m.title}`":使用v-bind,""里的东西被当作表达式执行,里面是一个模板字符串,还混着表达式,最终解析为:to="/home/message/detail?id=变量的值&title=变量的值" -->
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp;
  1. 跳转路由并携带query参数,to的对象写法
<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>

命名路由

一、作用:可以简化路由的跳转
二、如何使用:

  1. 给路由命名:
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,}]}]}
]
  1. 简化跳转
    简化前,需要写完整的路径:<router-link to="/demo/test/welcome">跳转</router-link>
    简化后,直接通过名字跳转:<router-link :to="{name:'hello'}">跳转</router-link>
    简化写法配合传递参数:如果是二级路由或三级路由,带的path就特别长,但是如果我们给路由配置了名字,就可以通过name指定,但是要配置name属性就必须把to写成个对象
<router-link :to="{// path:'/home/message/detail',name:'xiangqing',query:{id:m.id,title:m.title}}">{{m.title}}
</router-link>

路由的params参数

一、配置路由,声明接收params参数

{path:'/home',component:Home,children:[{path:'news',component:News},{component:Message,children:[{name:'xiangqing',//配置路由时,告诉他detail后面的是参数path:'detail/:id/:title', //使用占位符声明接收params参数component:Detail}]}    ]
}

二、传递参数

  1. 跳转并携带params参数,to的字符串写法:<router-link :to="/home/message/detail/666/你好">跳转</router-link>
    不用写?,直接把参数拼进去,那这样路由可能就会把参数当成路径,所以我们在配置路由时,要在路径后面使用:指定占位符
  2. 跳转并携带params参数,to的对象写法,此时只能用name指定路由,不能用path:
<router-link :to="{name:'xiangqing',// 如果携带的是params参数,这里只能用name指定路由,不能用pathparams:{id:666,title:'你好'}}"
>跳转</router-link>

三、接收参数

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

路由的props配置

一、作用:让路由组件更方便的收到参数
二、 之前我们的代码:

<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){//函数会收到一个参数$routereturn {// 返回值必须是对象,数据放在对象//所有键值对都会以props的形式传给Detail组件id:$route.query.id,//必须是键值对title:$route.query.title,a:1,b:'hello'}}}]}]}
]

然后在接收数据的组件中配置props来接收数据:

<template><ul><!-- router/index.js中配置的props是一个对象:<li>a:{{a}}</li><li>b:{{b}}</li>--><!-- router/index.js中配置的props是一个布尔值: --><li>消息编号:{{id}}</li><li>消息标题:{{title}}</li><!--router/index.js中配置的props是一个函数<li>a:{{a}}</li><li>b:{{b}}</li><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>

跳转路由时:

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

router-link标签的replace属性

一、作用:控制路由跳转时操作浏览器历史记录的模式
二、路由对浏览器历史记录的影响:浏览器中有两个常用的按钮:后退、前进,这两个按钮都是依赖于浏览器的历史记录来工作。使用<router-link>标签跳转链接,每次点击都会形成历史记录,且默认的操作模式是push,不破坏任何一条记录,不断往里面压入记录。对历史记录的操作除了push还有replace,replace最大的作用就是替换掉栈顶那一条。也就是说,浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
三、如何开启replace模式,给<router-link>标签添加replace属性,即可开启replace模式:<router-link replace .......>News</router-link>

编程式路由导航

一、有时候我们不能使用<router-link>标签实现跳转:

  1. 如果导航项得使用button写,点击按钮实现跳转,那就不能用<router-link>标签了,因为<router-link>标签最终转成了a标签,而我们要的是button
  2. 有时我们需要等一会再跳转,比如一进入页面,等一会儿跳转到xx页面,之前我们需要引导用户点击再跳转,现在我们没条件让用户点击,我们就想实现跳转

二、编程式路由导航:不借助<router-link>实现路由跳转的路由导航。让路由跳转更加灵活
三、具体编码:

//$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|步

缓存路由组件

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

  1. 缓存多个路由组件:<keep-alive :include="['News','Message']">
  2. 缓存一个路由组件,使用include指定哪些组件需要缓存,include的值是组件名:
<keep-alive include="News"><router-view></router-view>
</keep-alive>
  1. 像下面这样写,以后所有出现在中展示的内容,都会被缓存
<keep-alive><router-view></router-view>
</keep-alive>

三、路由组件有两个独有的钩子,用于捕获路由组件的激活状态:

activated路由组件被激活时触发。
deactivated路由组件失活时触发。

路由守卫

一、作用:对路由进行权限控制
二、分类:全局守卫、独享守卫、组件内守卫

全局守卫

一、路由暴露前为其添加全局路由守卫
二、全局前置路由守卫:初始化的时候被调用、每次路由切换之前被调用

  1. 使用router.beforeEach(函数)指定一个函数,在每次初始化时、每次路由切换之前都会调用这个函数。
  2. to会接收到目标路由的信息,from会接收到源路由的信息,next函数用于控制是否进行下一步,调用才会进行,不调用的话,路由切换会卡在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'){//也可以使用这种方法判断是否需要鉴权
  1. 如果我要对几十个路由进行判断,上面的写法太麻烦了,给每个路由配置都加上meta属性,用于标识本路由是否需要校验权限,meta被称为路由元信息,可以在里面写程序员自己配置的东西,那么我们可以在需要鉴权的配置对象上中的meta中添加个属性,这里添加的是isAuth
routes:[{//这是个配置对象,配置对象最大的特点就是里面的每一项都是按照设计好的来的,你不能在这里面添加稀奇古怪的属性name:'guanyu',path:'/about',component:About,meta:{isAuth:true,title:'关于'}}
]
  1. 那么在这里只用判断to.meta.isAuth是否为true,为true则鉴权
if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='atguigu'){next()}else{alert('学校名不对,无权限查看!')}
}else{next()
}

三、全局后置路由守卫:初始化的时候被调用、每次路由切换之后被调用

  1. 使用router.afterEach(函数)指定一个函数,在每次初始化时、每次路由切换之后都会调用这个函数
  2. to会接收到目标路由的信息,from会接收到源路由的信息,
  3. 有这么一个场景:你一进入一个网页,页签展示尚硅谷,然后你点击了HOME,页签展示主页,你点击About,页签展示关于。在这种情况下,就需要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()}}}]}
]

组件内路由守卫

组件内路由守卫:在组件里写路由守卫。当你想给某组件单独写一些逻辑,可以在组件内路由守卫中实现

  1. 通过路由规则,进入该组件时被调用

通过路由规则,进入组件的过程:点击后路径变成/about,前端路由器检测到路径的变化,匹配规则后进入组件展示
不通过路由规则进入组件:比如我一打开页面,xx组件就展示了(通过在页面中写<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()}
}
  1. 通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {console.log('About--beforeRouteLeave',to,from)next()
}

路由器的两种工作模式

一、对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
二、hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
三、hash模式:(默认)

  1. 地址中永远带着#号,不美观 。
  2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
  3. 兼容性较好。

四、history模式:

  1. 地址干净,美观 。
  2. 兼容性和hash模式相比略差。
  3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。 在node中使用connect-history-api-fallback库解决。
    (1)在服务器终端安装:npm i connect-history-api-fallback
    (2)在后端引入:const history = require(connect-history-api-fallback)
    (3)再在静态资源使用之前使用connect-history-api-fallback:
const app = express() app.use(history)
app.use(express.static(__dirname+'/static'))//使用静态资源

五、路由模式默认为hash模式,可以在创建路由器时,通过指定mode配置项将路由模式改为history模式:new VueRouter({mode:'history'})

vue -【nextTick】-【过度与动画】-【插槽】-【配置代理服务器】-【vuex】-【路由】相关推荐

  1. Vue | 使用Vue脚手架 【脚手架的基本使用+ref属性+props属性+mixin混入+插件scoped样式+TodoList+浏览器本地存储+组件的自定义事件+全局事件总线+过度与动画】

    文章目录 脚手架的基本使用 初始化脚手架 分析脚手架结构 render函数 修改默认配置 ref属性 props属性 mixin混入 插件 scoped样式 Todo-list案例 组件化编码流程(通 ...

  2. vue动画效果配置和弹层css sticky footer

    vue动画效果配置 有两种方式: 一种是css方式 需要注意 4 个(CSS)类名在 enter/leave 的过渡中切换: v-enter: 定义进入过渡的开始状态.在元素被插入时生效,在下一个帧移 ...

  3. Vue全家桶(一):Vue基础+Vue-Cli+Vue组件化+过渡动画

    目录 1.Vue概述 1.1 认识Vue 1.2 Vue的两核心 1.3 Vue的初体验 1.4 Vue的生命周期 2. Vue-CLI (Command Line Interface) 3. Vue ...

  4. VUE学习(二十)、插槽

    VUE学习(二十).插槽 一.默认插槽 1.Category.vue <template><div class="category"><h3>{ ...

  5. 7 种最棒的 Vue Loading 加载动画组件测评与推荐 - 穷尽市面上所有加载动画效果(Vue loader)类型

    本文完整版:<7 种最棒的 Vue Loading 加载动画组件测评与推荐> 目录 7 种不同类型的 Vue Loading 加载动画组件 1. Vue Simple Spinner - ...

  6. 浏览器事件循环机制与Vue nextTick的实现

    浏览器事件循环机制 先上一段简单的代码 console.log('aa'); setTimeout(() => { console.log('bb')}, 0); Promise.resolve ...

  7. vue2知识点:vue-cli脚手架配置代理服务器

    文章目录 6.3vue-cli脚手架配置代理服务器 6.3.1解决跨域问题:配置代理_方式1 案例:开2台模拟服务器,模拟客户端端口8080调用2台服务器端口叫5000和5001,实现ajax解决跨域 ...

  8. vue实现双开门动画

    需求: 前一个页面离开时有双开门动画,后一个页面有缩放动画的效果. 不太理解双开门动画的小伙伴,可以参考ppt的门动画 版本迭代 第一次迭代 设计 第一想法就是通过创建一个父元素,并为其添加两个子元素 ...

  9. vue脚手架解决跨域问题-------配置反向代理

    vue脚手架解决跨域问题-------配置反向代理 参考文章: (1)vue脚手架解决跨域问题-------配置反向代理 (2)https://www.cnblogs.com/zbx-boke/p/9 ...

  10. Vue.nextTick()理解

    什么是Vue.nextTick() 官方解释:在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 注意:重点是获取更新后的DOM 就是在开发过程中有个 ...

最新文章

  1. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试
  2. leetcode - two-sum
  3. Python中的测试工具
  4. Facebook:15年来最重要的转型,F8大会掀开打造私密社交网络的新篇章
  5. SAP CRM WebClient UI上文本是否可编辑,取决于哪些条件
  6. 猜1-10的数字python脚本
  7. tinycore php,tinycore中文支持
  8. LazyInitializationException的四种解决方案–第1部分
  9. 12c oracle 激活_Oracle 12C 安装教程
  10. 薇娅夫妇合伙企业正式注销 系决议解散
  11. vsphere6.0实验拓扑-虚拟机版
  12. 贝叶斯(五)贝叶斯决策
  13. 小米蓝牙耳机使用说明书
  14. Excel-事件(Workbook、Worksheet、Range、OnKey/OnTime)
  15. freemarker字符串替换操作
  16. Versal ACAP AI 引擎入门
  17. 远程同步软件rsync(一)
  18. 友盟推送注册成功但是收不到推送
  19. 2014中国飞思卡尔技术论坛即将开幕
  20. linux 安装wkhtmltopdf 所出的问题

热门文章

  1. 古文观止卷七_陈情表_李密
  2. 国产操作系统Deepin的安装
  3. 强大的合成器微信小程序源码支持视频,gif动态证件照等等几十种功能
  4. 使用Uber-go Zap日志库
  5. 倪光南:网络不安全要挨打 不用县县都建数据中心
  6. mysql column specified twice_Column 'box_id' specified twice 错误
  7. linux程序设计学习心得,几点学习Linux编程的建议
  8. 可乐要加冰才好喝啊---装饰模式
  9. 仿生蛇类机器人 特点_仿生蛇机器人
  10. 小马激活工具拒绝访问cannot open file c:\oemsf解决方法