前几天写了 Vue状态管理模式:Vuex入门教程 ,今天再整理一下 Vue Router 的入门笔记。
什么是 Vue Router ?
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含如下功能:
-
嵌套的路由/视图表
-
模块化的、基于组件的路由配置
-
路由参数、查询、通配符
-
基于 Vue.js 过渡系统的视图过渡效果
-
细粒度的导航控制
-
带有自动激活的 CSS class 的链接
-
HTML5 历史模式或 hash 模式,在 IE9 中自动降级
-
自定义的滚动条行为
安装
npm install vue-router
在 main.js 中通过 Vue.use() 明确地安装路由功能:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
基础使用
router.js or router/index.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({
router, // 4.挂载根实例
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 超链接,查看效果。
命名路由
在配置路由的时候,给路由添加 name 属性,可以动态的根据 name 来进行访问。
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>
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,映射到同个组件。例如,我们有一个 User 组件,对 ID 各不相同的用户,都使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果。
User.vue
<template>
<div>
<h3>用户页面</h3>
</div>
</template>
<script>
export default {
};
</script>
<style 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>
访问链接:
http://localhost:8080/user/1
http://localhost:8080/user/2
查看效果。
当匹配到路由时,参数值会被设置到 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();
}
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'
匹配优先级:
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
查询参数
像这种地址:http://localhos:8080/page?id=1&title=w3h5
const router = new Router({
routes:[
//....
{
name:'/page',
name:'page',
component:()=>import('@/views/Page.vue')
}
]
})
<router-link :to="{name:'page',query:{id:1,title:'w3h5'}}">User</router-link>
访问 http://localhos:8080/page?id=1&title=w3h5 查看 Page
Page.vue
<template>
<div>
<h3>Page页面</h3>
<h3>{{$route.query.userId}}</h3>
</div>
</template>
<script>
export default {
created () {
//查看路由信息对象
console.log(this.$route);
},
}
</script>
<style scoped>
</style>
路由重定向和别名
例如:从 / 重定向到 /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' }}
]
})
甚至是一个方法:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
别名:
{
path: '/home',
name: 'home',
component: Home,
alias: '/alias'
}
起别名,仅仅起起别名。用户访问 http://loacalhost:8080/alias 的时候,显示 Home 组件。
注意:别名的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
路由组件传参
在组件中使用 $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>
编程式导航
除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
注意:在 Vue 实例内部,你可以通过 router 访问路由实例。因此你可以调用 this.router 访问路由实例。因此你可以调用 this.router.push。
声明式:<router-link :to="...">
编程式:router.push(...)
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
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)
嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。
/user/1/profile /user/1/posts +------------------+ +-----------------+ | User | | User | | +--------------+ | | +-------------+ | | | Profile | | +------------> | | Posts | | | | | | | | | | | +--------------+ | | +-------------+ | +------------------+ +-----------------+
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>
命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 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'/>
导航守卫
“导航”表示路由正在发生改变。
完整的导航解析流程
-
导航被触发。
-
在失活的组件里调用离开守卫。
-
调用全局的
beforeEach守卫。 -
在重用的组件里调用
beforeRouteUpdate守卫 (2.2+)。 -
在路由配置里调用
beforeEnter。 -
解析异步路由组件。
-
在被激活的组件里调用
beforeRouteEnter。 -
调用全局的
beforeResolve守卫 (2.5+)。 -
导航被确认。
-
调用全局的
afterEach钩子。 -
触发 DOM 更新。
-
用创建好的实例调用
beforeRouteEnter守卫中传给next的回调函数。
全局守卫:
你可以使用 router.beforeEach 注册一个全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
有个需求,用户访问在浏览网站时,会访问很多组件,当用户跳转到 /notes,发现用户没有登录,此时应该让用户登录才能查看,应该让用户跳转到登录页面,登录完成之后才可以查看我的笔记的内容,这个时候全局守卫起到了关键的作用。
有两个路由 /notes 和 /login
router.vue
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>
export default {
methods: {
handleLogout() {
//删除登录状态信息
localStorage.removeItem("user");
//跳转到首页
this.$router.push('/')
}
},
}
组件内的守卫:
你可以在路由组件内直接定义以下路由导航守卫:
-
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>
路由元信息实现权限控制:
给需要添加权限的路由设置 meta 字段
{
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);
}
数据获取:
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
-
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
-
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
导航完成后获取数据:
当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 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();
});
}
}
};
未经允许不得转载:w3h5 » Vue官方路由管理器Vue-router入门教程
原创文章,作者:6024010,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/228443.html