官网:https://router.vuejs.org/zh/installation.html
安装
npm i vue-router
配置
// router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router'
import Home from '@/buyCars/Home.vue'
import About from '@/buyCars/About.vue'
import Detail from '@/buyCars/Detail.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
// 路由与子路由
{
path: '/about',
name: 'about',
component: About,
children: [
{
// 访问/about时,默认访问的页面
path: '',
name: 'detail',
component: AboutPage
},
{
// 注意子路由前不要加/,否则会被认为是根目录那一级的路由;不加/会自动默认嵌套在父路由下/about/cart
path: 'cart',
name: 'cart',
component: Cart
}
]
},
{
// 配置动态路由
path: '/detail/:id',
// name: 'detail',
component: Detail
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.Base_URL,
routes
})
export default router;
// main.js
import router from './router'
new Vue({
router, // 注意key是小写,写成大写会报错
render: h => h(App),
}).$mount('#app')
// App.vue
// 嵌套的子路由的视图,记录也要添加router-view标签
路由跳转
{{item.goods}}
// 方法2
this.$router.push({name: 'detail', params: {id: item.id}})
跨域代理配置
vue cli官网 - 配置参考: https://cli.vuejs.org/zh/config/
// vue.conifg.js
module.exports = {
// vue2的配置代理
devServer: {
proxy: {
'/api': {
target: "http://localhost:7777",
pathRewrite: {
"^/api": ""
}
}
}
}
};
$router和$route
- $router:本质上也就是我们创建的路由对象:
const router = new VueRouter({
mode: 'history',
base: process.env.Base_URL,
routes
})
- $route是经过处理的,当前页面的一些路由信息对象:
this.$route === {
fullPath: "/"
hash: ""
matched: [{…}]
meta: {}
name: "home"
params: {}
path: "/"
query: {}
}
// 获取动态路由
let id = this.$route.params.id
它们其实就是由插件的形式,绑定在Vue实例上的
import Vue from 'vue';
import VueRouter from 'vue-router'
Vue.use(VueRouter)
路由守卫
使用场景,举个栗子:当购物车改变排序(比如金额从低到高)的参数时,要通过queryString方式记录,以便分享给他人记录此排序
如果只是queryString(?sort=asc或?sort=desc)改变的话,路由组件会复用,而不是销毁重建,所以组件中初始请求并不会再次发送。这时要监听路由参数的变化,去重新请求的话,有两种方式:
1. 通过watch去监听路由变化
watch: {
// to是要跳转的路由信息,from是跳转之前的信息
$route(to, from) {
console.log('$route', to, from);
const sort = to.query.sort;
this.getGoods(sort) // 参数改变后,重新请求后台数据
}
}
2. 通过路由守卫完成
路由守卫的钩子
- beforeRouteEnter: 在地址栏输入url,按下enter时调用
- beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用,比如像这种只改变queryString的路由
- beforeRouteLeave: 离开当前路由时被调用
它们有参数是一样的:
- to: 目标路由的$route对象
- from: 跳转前的$route对象
- next: 路由确认回调函数,类似Promise中的resolve函数,一定要调用next函数,才能保证后续导航行为继续
- next(): 继续进入管道中的下一个钩子
- next(false): 中断当前导航。如果浏览器url改变了(可能用户手动或浏览器后退),那么url地址会重置到from路由对应的地址
- next('/')或next({path: '/'}): 跳转到另一个路由。当前导航被中断,并跳到指定地址
单个vue组件中的使用
create() {},
// 在地址栏输入url,按下enter时,调用,相当于一个拦截器,必须调用next()才能正常访问路由,否则会被拦截
beforeRouteEnter(to, from, next) {
// 这里是在beforeCreate之前,不能调用组件实例this,只能处理跟data无关的操作
console.log("beforeRouterEnter");
// 如果非要操作实例中的数据,只能在next回调函数中处理
next(vm => {
// 处理和vue实例相关的操作
console.log(1111, vm);
})
},
// 在当前路由改变,但是该组件被复用时调用,比如像这种只改变queryString的路由
beforeRouteUpdate(to, from, next) {
console.log("beforeRouterUpdate");
console.log(to.query.sort);
this.getGoods(to.query.sort);
next()
},
// 离开当前路由时被调用
beforeRouteLeave() {
console.log("beforeRouterLeave");
next()
}
路由独享的守卫,在配置路由时设置beforEnter
在访问某个地址时,就会执行beforEnter中的操作,这种应用不多
// router/index.js
const routes = [
{
path: '/',
name: 'home',
beforeEnter: (to, from, next) => {
// ...
}
}
]
全局路由守卫
参考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB
- beforeEach:当一个导航触发时,全局前置守卫按照创建顺序调用
- beforeResolve:在所有组件内守卫和异步路由组件被解析之后被调用
- afterEach: 导航被确认后调用
// router/index.js
const routes = [{...}]
const router = new VueRouter({
mode: 'history',
routes
})
// 设置全局路由守卫
router.beforeEach((to, from, next) => {
// 如果没有登录,且要跳转的路由不是登录页,就跳转到登录页
if(!isLogin && to.name !== 'login') {
next({name: 'login'})
}else {
next()
}
})
props路由传递
有些子页面可以接收父组件的props参数,确无法接收到父组件的路由params参数。为了解决这个问题,可以在路由配置中将props设置true,这样路由参数会和父组件的props做个合并,传给子组件
当有些页面既是路由组件,同时又是一个组件的子组件(比如一些详情面,既是单独的路由详情页,又是某些页面的弹出窗组件),那么这时候就可以设置params为true,这样就能统一在props中接收动态参数
// router/index.js
const routes = [
// 默认将所有route.params参数放在父组件给子组件中的props中
{
path: '/',
name: 'home',
component: Home,
props: true
},
// 也可以有选择性表示放置哪些参数在props中,但是这个值是事先定好的,不能指定参数
{
path: '/aa/:id',
name: 'home',
component: Home,
// 对象方式,只能传指定值的对象
// props: {id: 2},
// 要想使用参数,要使用函数方式
porps(route) {
return {
a: 1,
b: 2,
id: route.params.id
}
}
},
]
// 父组件
// 子组件中,要设置接收的动态参数名称,注意要和路由设置的名称保持一致;父组件中,也是同样传入同名的参数
props: ['id']
跳转页面,获取数据的方式
跳转页面后获取数据
也就是先跳到详情页,然后再在详情页的created()中获取到数据后,再渲染页面
跳转前获取数据
我们知道,当进入一个页面时,会调用beforeRouteEnter钩子,当在其中调用next()后,才会真正跳转到那个路由,可以利用这一点,进行先获取数据,再进入路由跳转;
但这个可能比较少用,因为它不适用单独一个组件的传参,更适合路由组件
async beforeRouteEnter(to, from, next) {
console.log("进入" + to.name + "页面前");
// 可以使用to.params来获取路由参数
let res = await getGoodsDetail(to.params.id)
const { data, msg } = res.data
next(vm => {
vm.showDetail = msg === 'ok' ? true: false
vm.info = data
})
},
methods: {
getDetail() {
// $route.params.
getGoodsDetail(this.id).then(res => {
console.log(res.data.data);
const {data, msg} = res.data
this.showDetail = msg === 'ok' ? true: false
this.info = data
})
}
}
路由动画
进度条 NProgress.js
官网: https://ricostacruz.com/nprogress/
是一个独立js库,用于在页面加载时添加进度条显示
主要包括以下四个方法:
- NProgress.start() : 设置一个开始进度条
- NProgress.set(0.4) :设置40%的进度条
- NProgress.inc() :将进度条进度往前一点点
- NProgress.done() : 进度条完成
- vue中安装:
npm i nprogress
我们可以在全局路由守卫中给页面加载时,添加进度条:
// router/index.js
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
router.beforeEach((to, from, next) => {
NProgress.start()
next()
})
router.afterEach((to, from, next) => {
NProgress.done()
})
离开,进入时的动画
可以参考trasition的使用
.fade-enter-active, .fade-leave-active {
transition: opacity 3s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
路由滚动位置记录
new VueRouter配置中,有一项是scrollBehavior函数,它接收三个参数:
- to:同路由守卫中$route对象
- from:同路由守卫中$route对象
- savedPositon:记录的滚动条位置
当使用浏览器的回退和前进功能时,scrollBehavior中的savedPosition参数会记录上次用户浏览到的滚动位置; 但如果是用户点击跳转a标签等方式,savedPosition则不会记录,而是为Null
scrollBehavior返回是一个对象{x: 0, y:0},表示当前滚动条要滚动的位置(已作废)
const router = new VueRouter({
mode: 'history',
base: process.env.Base_URL,
routes,
scrollBehavior(to, from, savedPosition) {
console.log("当使用:", savedPosition);
console.log("当使用:", savedPosition);
// vue-router 不再支持return 方式
if(savedPosition) {
console.log("回退到原来位置:", savedPosition);
}else {
return {x:0, y: 0}
}
// 路由滚动一直都不生效,后续等解决....
document.body.srcollTop = 50
document.documentElement.scrollTop = 50
window.scrollTo(0, 50)
}
})
路由懒加载
静态方式:
// router/index.js
// 1. 静态方式
import Home from '@/buyCars/Home.vue'
import Detail from '@/buyCars/Detail.vue'
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
// 生成的dist包:会将所有静态路由都打包一个包,chunk-verdors.js,这样体积会比较大,且一次加载时间也会比较长
// 2. 动态加载(懒加载)方式
const routes = [
{
path: '/',
name: 'home',
component: () => import('@/buyCars/Home.vue')
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ '@/buyCars/Detail.vue'),
children: [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "about" */ '@/buyCars/Home.vue')
},
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "about" */ '@/buyCars/Home.vue')
}
]
}
]
// 生成的dist包,会打包成多个(一般一个懒加载对应一个)chunk-verdors.js文件
// 但是,往往一个级联路由,会将这一个模块的路由组件都打成一个包,以便当成一个整体模块,通过注释 /* webpackChunkName: "about" */ 说明包的名称,包名称一致webpack会将其打包成一个包
router.addRoutes动态加载路由
// login.vue
methods: {
login() {
// 如果已经登录,且为管理员
if(this.$store.isLogin && this.$store.username === 'admin') {
// 动态加载后台管理权限
this.$router.addRoutes([
{
path: '/admin',
name: 'admin',
component: () => import(/*webpackChuckName: "admin" */ '@/views/admin.vue')
}
])
this.$router.push({ name: 'admin'})
}
}
}