6 vue-router

1 vue-router介绍

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

2 vue-router安装

用 Vue.js + Vue Router 创建单页应用,是非常简单的。使用 Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router添加进来,我们需要做的是,将组件 (components) 映射到路由(routes),然后告诉 Vue Router 在哪里渲染它们

  • 安装

    npm i vue-router@3
    
  • main.js

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    Vue.use(VueRouter)
    

推荐使用:vue add router 一键添加插件(记得提前提交)


3 基本使用

router.js

import Vue from 'vue'
//1.导入
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'

//2.模块化机制 使用Router
Vue.use(Router)

//3.创建路由器对象
const router = new Router({
    routes: [
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
})

export default router;

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false
new Vue({
    // 4.挂载根实例
    router,
    render:h => h(App)
}).$mount('#app')

做好以上配置后:

APP.vue
<template>
    <div id=" app">
        <div id="nav">
            <!-- 使用router-link组件来导航-->
            <!-- 通过传入to属性指定连接 -->
            <!-- router-link默认会 被渲染成一个a标签-->
            <router-link to="/" >Home</router-link>|
            <router-link to="/about" >About</router-link>|
        </div>
        <!--路由出口-->
        <!--路由匹配的组件将被渲染到这里-->
        <router-view/>
    </div>
</template>

打开浏览器,切换Home和About超链接,查看效果


4 命令路由

在配置路由的时候,给路由添加名字,访问时就可以动态的根据名字来进行访问

const router = new Router({
    routes: [
        {
            path: '/home',
            name: "home",
            component: Home
        },
        {
            path: '/about',
            name: 'about',
            component: About
        }
    ]
})

要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:

<router-link :to="{name:'home'}">Home</router-link> |
<router-link :to="{name:'about'}">About</router-link> |

5 动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果

User.vue

<template>
    <div>
        <h3>用户页面</h3>
    </div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
</style>

路由配置:

const router = new Router({
    routes: [
        {
            path: '/user/:id',
            name: ' user',
            component: User,
        },
    ]
})
<router-link :to="{name:'user',params:{id:1}}">User</router-link> |

当匹配到路由时,参数值会被设置到this.$route.params,可以在每个组件中使用,于是,我们可以更新 User 的模板,输出当前用户的 ID:

<template>
  <div>
    <h3>用户页面{{$route.params.id}}</h3>
  </div>
</template>

响应路由参数的变化

提醒一下,当使用路由参数时,例如从 /user/1 导航到 /user/2,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。复用组件时,想对路由参数的变化作出响应的话,你可以简单地watch (监测变化) $route 对象:

/*使用watch(监测变化) $route对象
 watch: {
     $route(to, from) {
     console.log(to.params.id);
     }
 }, */

// 或者使用导航守卫
beforeRouteUpdate(to,from,next){
 //查看路由的变化
 //一定要调⽤next,不然就会阻塞路由的变化
 next();
}

6 404路由

const router = new Router({
    routes: [
        //....
        // 匹配不到理由时,404⻚⾯显示
        {
            path: '*',
            component: () => import('@/views/404')
        }
    ]
})

当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404错误

当使用一个通配符时, $route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:

{
    path: '/user-*',
    component: () => import('@/views/User-admin.vue') 
}

this.$route.params.pathMatch // 'admin'

7 匹配优先级

类似像地址上出现的这种:http://localhos:8080/page?id=1&title=foo

const router = new Router({
    routes:[
        //....
        {
            name:'/page',
            name:'page',
            component:()=>import('@/views/Page.vue')
        }
    ]
})
<router-link :to="{name:'page',query:{id:1,title:'foo'}}">User</router-link> |

访问 http://localhos:8080/page?id=1&title=foo 查看Page

Page.vue
<template>
    <div>
        <h3>Page页面</h3>
        <h3>{{ $route.query.id }}</h3>
    </div>
</template>
<script>
export default {
    created() {
        //查看路由信息对象
        console.log(this.$route);
    },
}
</script>
<style lang="scss" scoped>
</style>

8 路由重定向和别名

例子是从 / 重定向到 /home :

const router = new Router({
    mode: 'history',
    routes: [
        // 重定向
        {
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            name: 'home',
            component: Home
        },
    ]
})

重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
    routes: [
        { path: '/', redirect: { name: 'name' }}
    ]
})

别名:

{
    path: '/user/:id',
    name: 'user',
    component: User,
    alias: '/alias'
}

起别名,仅仅起起别名 用户访问 http://loacalhost:8080/alias 的时候,显 示User组件

别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。


9 路由组件传参

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

取代与 $route 的耦合

{
    path: '/user/:id',
    name : 'user',
    component: User,
    props: true
},

User.vue

<template>
    <div>
        <h3>用户页面{{ $route.params.id }}</h3>
        <h3>用户页面{{ id }}</h3>
    </div>
</template>
<script>
export default {
    props: {
        id: {
            type: String,
            default: ''
        },
    },
}
</script>

props也可以是个函数

{
    path:'/user/:id',
    name:' user',
    component: User,
    props: (route)=>({
        id: route.params.id,
        title: route.query.title
    })
}

User.vue

<template>
    <div>
        <h3>用户页面{{ id }}-{{ title }}</h3>
    </div>
</template>
<script>
export default {
    props: {
        id: {
            type: String,
            default: ' '
        },
        title: {
            type: String
        }
    },
};
</script>

10 编程式导航

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

image

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如

// 字符串
this.$router.push('home')
// 对象
this.$router.push({ path: 'home' })
// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
this.$.push({ path: 'register', query: { plan: 'private' }})

前进后退

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

11 嵌套路由

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件

router.js

{
    path: '/user/:id',
    name: 'user',
    component: User,
    props: ({params , query})=>({
        id: params.id,
        title: query. title
    }),
    children: [
    //当/user/ :id/profile 匹配成功,
    // Profile 会被渲染在User的<router-view> 中
            {
                path: "profile",
                component: Profile
            },
    //当/user/ :id/posts 匹配成功,
    // Posts 会被渲染在User的<router-view> 中
            {
                path: "posts",
                component: Posts
        }
    ]
}

在 User 组件的模板添加一个 <router-view>

<template>
    <div>
        <h3>用户页面{{$route.params.id}}</h3>
        <h3>用户页面{{id}}</h3>
        <router-view></router-view>
    </div>
</template>

App.vue

<template>
    <div id='app'>
        <!-- 嵌套理由 -->
        <router-link to="/user/1/profile">User/profile</router-link> |
        <router-link to="/user/1/posts">User/posts</router-link> |
    </div>
</template>

12 命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了

{
    path: '/home',
    name: 'home',
    //注意这个key是components
    components: {
        default: Home, //默认的名字
        main: ()=>import('@/views/Main.vue'),
        sidebar: () => import('@/views/Sidebar.vue')
    }
}

App.vue

<router-view/>
<router-view name='main'/>
<router-view name='sidebar'/>

13 导航守卫

“导航”表示路由正在发生改变。

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter 。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter 。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。

13-1 全局守卫

你可以使用 router.beforeEach 注册一个全局前置守卫

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
 // ...
})

有个需求,用户访问在浏览网站时,会访问很多组件,当用户跳转到 /notes ,发现用户没有登录,此时应该让用户登录才能查看,应该让用户跳转到登录页面,登录完成之后才可以查看我的笔记的内容,这个时候全局守卫起到了关键的作用

有两个路由 /notes 和 /login

router.js
const router = new VueRouter({
    routes: [
        {
            path: '/notes',
            name: 'notes',
            component: () => import('@/views/Notes')
        },
        {
            path: "/login",
            name: "login",
            component: () => import('@/views/Login')
        },
    ]
})


//全局守卫
router.beforeEach((to, from, next) => {
    //用户访问的是' /notes
    if (to.path === '/notes') {
        //查看一下用户是否保存了登录状态信息
        let user = JSON.parse(localStorage.getItem('user'))
        if (user) {
            //如果有,直接放行
            next();
        } else {
            //如果没有,用户跳转登录页面登录
            next('/login')
        }
    } else {
        next();
    }
})
Login.vue
<template>
    <div>
        <input type="text" v-model="username">
        <input type=" password" v-model=" pwd">
        <button @click="handleLogin">提交</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            username: '',
            pwd: ''
        };
    },
    methods: {
        handleLogin() {
            // 1. 获取用户名和密码
            // 2.与后端发生交互
            setTimeout(() => {
                let data = {
                    username: this.username
                };
                //保存用户登录信息
                localStorage.setItem("user", JSON.stringify(data));
                //跳转我的笔记页面
                this.$router.push({name: "notes"});
            }, 1000);
        },
    }
};
</script>
App.vue
<!-- 全局守卫演示-->
<router-link to="/notes">我的笔记</router-link>|
<router-link to="/login">登录</router-link>|
<button @click="handleLogout">退出</button>
<script>
export default {
    methods: {
        handleLogout() {
            //删除登录状态信息
            localStorage.removeItem("user");
            //跳转到首页
            this.$router.push('/')
        }
    },
}
</script>

13-2 组件内的守卫

你可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
<template>
    <div>
        <h3>⽤户编辑页⾯</h3>
        <textarea name id cols="30" rows="10" v-model="content"></textarea>
        <button @click="saveData">保存</button>
        <div class="wrap" v-for="(item,index) in list" :key="index">
            <p>{{ item.title }}</p>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            content: "",
            list: [],
            confir: true
        };
    },
    methods: {
        saveData() {
            this.list.push({
                title: this.content
            });
            this.content = "";
        }
    },
    beforeRouteLeave(to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
        if (this.content) {
            alert("请确保保存信息之后,再离开");
            next(false);
        } else {
            next();
        }
    }
};
</script>

13-3 路由元信息实现权限控制

给需要添加权限的路由设置meta字段

router.js

{
    path: '/blog',
    name: 'blog',
    component: () => import('@/views/Blog'),
    meta: {
        requiresAuth: true
    }
},
{
    //路由独享的守卫
    path: '/notes',
    name: 'notes',
    component: () => import('@/views/Notes'),
    meta: {
        requiresAuth: true
    }
},

//全局守卫
router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresAuth)) {
        //需要权限
        if (!localStorage.getItem('user')) {
            next({
                path: ' /login',
                query: {
                    redirect: to.fullPath
                }
            })
        } else {
            next();
        }
    } else {
        next();
    }
})

login.vue

//登录操作
handleLogin() {
    // 1.获取用户名和密码
    // 2.与后端发生交互
    setTimeout(() => {
        let data = {
            username: this.username
        };
        localStorage.setItem("user",
            JSON.stringify(data));
        // 跳转到之前的页面
        this.$router.push({path:
            this.$route.query.redirect });
    }, 1000);
}

13-4 数据获取

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

导航完成后获取数据

当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

<template>
    <div class="post">
        <div v-if="loading" class="loading">Loading...
        </div>
        <div v-if="error" class="error">{{ error }}</div>
        <div v-if="post" class="content">
            <h2>{{ post.title }}</h2>
            <p>{{ post.body }}</p>
        </div>
    </div>
</template>
export default {
    name: "Post",
    data() {
        return {
            loading: false,
            post: null,
            error: null
        };
    },
    //组件创建完后获取数据,
    //此时data已经被监视了
    created() {
    //如果路由有变化,会再次执行该方法
        this.fetchData();
    },
    watch: {
        $route: "fetchData"
    },
    methods: {
        fetchData() {
            this.error = this.post = null;
            this.loading = true;
            this.$http.get('/api/post').then((result) => {
                    this.loading = false;
                    this.post = result.data;
                }).catch((err) => {
                this.error = err.toString();
            });
        }
    }
};

自我笔记

vur-router知识


1.跳转
this.$router.push({path: '/'})
this.$router.go(-1)  // 0:刷新 1:前进 -1:后退


2.路由信息对象(index.js中的信息)
// http://127.0.0.1/user/1
this.$route.params  --> params命名路由匹配
<router-link :to="{name:'user', params:{id: 1}}">user</router-link>

// http://127.0.0.1/user-*
<router-link :to="{name:'admin-'}">user-admin</router-link>
this.$route.params.pathMatch  --> 匹配的通配字符

// http://127.0.0.1/page?id=1&title=foo
<router-link :to="{name:'user', query:{id: 1, title: 'foo'}}">user</router-link>
this.$route.query  --> query命名路由匹配

// http://127.0.0.1/user/
<router-link :to="{name:'user'}">user</router-link>
this.$route.matched  // 获取当前的所有路由信息


3."404"路由
	{
		path: "*",
		component: ()=>import('@/views/404')
		// @表示src文件夹
	}

4.通配符路由
	{
		path: '/user-*',
		component: () => import('@/views/User-admin')
	}


5.history模式
	mode: 'history'  // 干净的网页地址


6.重定向和别名
	{
		path: '/',
		redirect: '/home'
		redirect: {name: 'home'}
	}
	{
		path: '/home',
		alias: '/aaa'  // 起别名
	}


7.路由组件传值
{
	path: '/user/:id',
	props: true
}
props: ['id']

{
	path: '/user/:id',
	props: () => ({
		id: route.params.id,
		title: route.query.title,
	})
}
props: ['id', 'title']


8.编程式导航
this.$router.push({path: '/'})

// 声明式
<router-link :to="{name:'user'}">user</router-link>

// 编程式
<div @click="goHome"></div>
methods:{
	this.$router.push({path: '/'});
	this.$router.push({name: 'user', params:{id:1}});
}


9.嵌套路由
{
	path: '/home',
	name: 'home',
	component: ()=>import('@/views/Home')
	children: [
		{
			path: 'profile',
			component: ()=>import('@/views/ProFile')
		}
	]
}


10.命名视图的使用
<touter-view></router-view>
<touter-view name="main" class="main"></router-view>
<touter-view name="sidBar" class="sidbar"></router-view>

{
	path: '/home',
	name: 'home',
	componemts: {
		default: Home,  // 默认名字
		main: ()=>import('@/views/Main'),
		sideBar: ()=>import('@/views/SideBar')
	}
}


11.全局守卫做登录操作
<touter-view :to="{name: 'notes'}">笔记</router-view>
{
	path: '/login',
	name: 'login',
	component: ()=>import('@/views/Login')
},
{
	path: '/notes',
	name: 'notes',
	component: ()=>import('@/views/Notes')
}

main.js// 全局守卫
router.beforeEach((to, from, next)=>{
	// 用户访问了/notes
	if(to.path === '/notes'){
		const user = JSON.parse(localStorage.getItem('user'));
		if(user){
			next();
		}else{
			next('/login');
		}
	}
	next();
})


12.组件内的守卫
beforeRouteEnter(to, from, next){
	// 组件么有加载之前
	// 没有实例this
}
beforeRouteUpdate(to, from, next){
	// 组件重用时,调用
}
beforRouteLeave(to, from, next){
	// 导航离开组件的对应路由时调用
}


13.路由的元信息
{
	path: '/notes',
	name: 'notes',
	component: ()=>import('@/views/Notes'),
	meta: {
		requireAuth: true;  // 相当于加入了黑名单
	}
}

main.js  // 全局守卫
router.beforeEach((to, from, next)=>{
	// 黑名单
	if(to.matched.some(record=>record.meta.requireAuth)){
		// 需要权限
		if(!localStorage.geItem('user')){
			next({path: '/login', query:{redirect: to.fullPath}})
		}
	}else{
		next{}
	}
	// 白名单
	next();
})


14 怎么响应路由参数的变化呢?

方式1:
	watch监听属性:
	watch: {
		// to:表示到哪里去,from:表示从哪里来。
		$route: (to, from) =>{
			console.log(to.params.id);
			// 发送ajax请求后端数据
		}
	}

方式2:
	导航守卫的钩子函数:
	beforeRouteEnter(to, from, next){
		// to:表示到哪里去,from:表示从哪里来。
		console.log(to.params.id)
		next();  // 放行路由
	}

posted @ 2022-09-26 17:48  角角边  Views(58)  Comments(0)    收藏  举报