安装
基础
从一个例子开始
<!-- App.vue文件 -->
<div id="app">
<h1>Hello App!</h1>
<p>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<router-link to="/hello/112233">hello</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
// router/index.js 文件
//createRouter 用来创建路由
//createWebHashHistory 指定路由是hash模式
import { createRouter,createWebHashHistory } from 'vue-router'
import Hello from '../components/Hello.vue'
import ErrorPage from '../components/ErrorPage.vue'
// 配置路由规则
const routers = [
// 匹配所有路由(正则匹配权重低),自定义404
{path: '/:all(.*)*', component: ErrorPage},
{path: '/hello/:id',component: Hello}
]
// 导出路由
export default createRouter({
history: createWebHashHistory(), // 这里我们使用简单的hash模式
routes: routers
})
// main.js 文件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import routers from './routers'
createApp(App)
.use(routers) // 挂载路由
.mount('#app')
项目中的路由对象
tips: 别忘记挂载路由哦 如 use(router)
动态路由
匹配规则
从上面可以看出,路由匹配是支持自定义正则的,官方推荐的路由调试工具 https://paths.esm.dev/
Sensitive 与 strict 路由配置
const router = createRouter({
history: createWebHistory(),
routes: [
{
// /users ok
// /Users error
// /user/ error
path: '/users',
sensitive: true, // 大小写敏感
strict: true, // 是否严格按照path匹配
},
],
strict: true, // 或者这里设置全局
})
嵌套路由
const routes = [
{
path: '/home',
component: Home,
children: [ // home的子级
{
path: 'user',
component: User,
},
{
path: 'posts',
component: Posts,
},
],
},
]
编程式导航
// 字符串路径
router.push('/users/admin')
// 带有路径的对象
router.push({ path: '/users/admin' })
// 命名的路由,让路由建立 url, 可以传递params, query,hash, 传递的参数vue-router会自动编码,如果放在path中需要先编码一下
router.push({ name: 'user', params: { username: 'admin' }, query: { plan: '花儿为什么这么红' }, hash: '#h1' } })
push
: 这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URLreplace
: 会替代当前位置,无法回退,其余的同上forward
: 向前移动一条记录back
: 向后移动一条记录go
: go(-1)后退,反之亦然
命名路由
const routes = [
{
path: '/user/:username',
name: 'user',
component: User,
},
]
router.push({ name: 'user', params: { username: 'any' } }
命名视图
<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
components: {
default: Home,
// LeftSidebar: LeftSidebar 的缩写
LeftSidebar,
// 它们与 `<router-view>` 上的 `name` 属性匹配
RightSidebar,
},
},
],
})
// 或使用嵌套命名视图
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
重定向和别名
// redirect可以是一个字符串,一个对象,一个函数。示例如下
const routes = [
{
// /search/screens -> /search?q=screens
path: '/search/:searchText',
redirect: to => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
return { path: '/search', query: { q: to.params.searchText } }
},
},
{
path: '/search',
// ...
},
]
// 别名,就是说当访问这个别名的时候,跳转到此路由,不受路由限制。推荐少用少用少用!!!
const routes = [
{
path: '/home',
alias: ['/',‘’] // 也可以设置两个别名
}
]
给组件传递props
const routes = [
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
// 有以下几种写法
// 1:props: true
// 2:props: { default: true, sidebar: false }
// 3:{ id: '传入的参数' }
// 4: route=> ({ id: route.params.id })
}
]
路由模式
Hash 模式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
HTML5 模式
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
服务器配置
nginx
location / { try_files $uri $uri/ /index.html; }
node
const http = require('http') const fs = require('fs') const httpPort = 80 http .createServer((req, res) => { fs.readFile('index.html', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.html" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', }) res.end(content) }) }) .listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) })
iis
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Handle History Mode and custom 404/500" stopProcessing="true"> <match url="(.*)" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
进阶
导航守卫
全局守卫
// 进入前
router.beforeEach((to, from)=>{
//返回 false 以取消导航,也可以返回一个对象,比如 {name:'home'},相当于重定向
})
// 或者加入第三个参数 next,如果声明了第三个参数,则必须调用一个,否则会一直等待
router.beforeEach((to, from, next)=>{
// next(false) 或者 next({path: '/home'})
})
// 进入后
router.afterEach((to, from)=>{
})
// 解析守卫,发生在beforeEach后
router.beforeResolve((to, from)=>{
})
路由专用守卫
// 进入路由触发(但是还没有进入,在beforeResolve前触发)
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash], // 可以是数组,按顺序调用
},
]
组件内的守卫
// 选项式: beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave
// 组合式:onBeforeRouteUpdate,onBeforeRouteLeave
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫(2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫(2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由元信息
有时候想要将信息附加到路由上,我们看下面这个例子
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
},
{
path: ':id',
component: PostsDetail
// 任何人都可以阅读文章
meta: { requiresAuth: false }
}
]
}
]
// 我们可以通过 $route.meta 拿到meta数据
router.beforeEach((to, from) => {
// 而不是去检查每条路由记录
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: '/login',
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath },
}
}
})
在组合式API的应用
过度动效
滚动行为
路由懒加载
扩展router-link
路由导航检测
动态路由
var removeRoute = router.addRoute({path: '/user', name: 'user', component: xxx })
// router.replace(router.currentRoute.value.fullPath) // 然后手动调用replace覆盖当前路由
// 可以覆盖替换,当名字一样的情况下
removeRoute('填入路由的名字') // 调用它的返回值可以删掉路由
// 添加嵌套路由
router.addRoute('父路由的名字', {path: '/user', name: 'user', component: xxx })
// 查看现有路由
router.hasRoute() // 检查路由是否存在
router.getRoutes() // 获取一个包含所有路由记录的数组