Vue Router相关知识点-基础篇(一)

在这里插入图片描述

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

详细内容请查阅官网 -> 官网链接

​ 从发展趋势来看,后续的前端路由都会往函数式的编程方式发展(如V4版本的useRouter, useRoute)。不管是React Router 还是 Vue Router,也都趋向于Hooks的使用。

路由的目的:将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们。

安装

  • v4
npm install vue-router@4
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件进行导航 -->
    <!-- 通过传递 `to` 来指定链接 -->
    <!-- `<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签 -->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
  • v3
npm install vue-router
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
  • router-link 类似 a 标签,这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码;
  • router-view 将显示与 url 对应的组件;

使用

  • v4
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 1.导入路由组件
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'

// 2. 创建路由实例并传递 `routes` 配置
const router = createRouter({
  history: createWebHistory(), 
  // 3.定义路由
  routes: [
    {
      path: '/',
      component: HomeView
    },
    {
      path: '/about',
      component: AboutView
    }
  ]
})

export default router
// main.js
import { createApp } from "vue"
// 引入App组件
import App from "./App.vue"
// 引入路由实例
import router from "./route"

// 创建并挂载根实例
const app = createApp(app)
// 确保整个应用支持路由,此步骤添加了全局组件 router-view、router-link
app.use(router)

app.mount(app)

app.vue

<template>
  <header>
    <div class="wrapper">
      <nav>
        <RouterLink to="/">Home</RouterLink>
        <RouterLink to="/about">About</RouterLink>
      </nav>
    </div>
  </header>

  <RouterView />
</template>
  • v3
// router/index.js
import VueRouter from 'vue-router'

// 1.导入路由组件
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'

// 2. 创建路由实例并传递 `routes` 配置
const router = new VueRouter({
  // 3.定义路由
  routes: [
    {
      path: '/',
      component: HomeView
    },
    {
      path: '/about',
      component: AboutView
    }
  ]
})

export default router
// main.js
import Vue from "vue"
// 引入App组件
import App from "./App.vue"
// 引入路由实例
import router from "./route"

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
new Vue({
  router,
     render: h => h(App)
}).$mount('#app')

访问路由

  • v4
import { useRouter,useRoute } from "vue-router"

const router = useRouter()
const route = useRoute()

const toAbout = ()=>{
	router.push("/about")    
}
console.log(route.query)
  • v3
this.$router.push("/about")
this.$route.query

router和route的区别

​ router:路由管理对象,管理路由

​ route:当前路由对象

动态路由

  • 路由配置
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 1.导入路由组件
import HomeView from '../views/HomeView.vue'
import UserView from '../views/UserView.vue'

// 2. 创建路由实例并传递 `routes` 配置
const router = createRouter({
  history: createWebHistory(), 
  // 3.定义路由
  routes: [
    {
      path: '/',
      component: HomeView
    },
    {
        // 动态字段以冒号开始
      path: '/user/:id',
      component: UserView
    }
  ]
})

export default router
  • 获取params

路径参数 用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params 的形式暴露出来。因此,我们可以通过更新 User 的模板来呈现当前的用户 ID:

const User = {
  template: '<div>User {{ $route.params.id }}</div>',
}


同一个路由可设置多个路径参数:

匹配模式匹配路径$route.params
/users/:username/users/eduardo{ username: 'eduardo' }
/users/:username/posts/:postId/users/eduardo/posts/123{ username: 'eduardo', postId: '123' }
相应路由参数变化

当用户从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch监听 $route 对象上的任意属性:

  created() {
    this.$watch(
      () => this.$route.params,
      (toParams, previousParams) => {
        // 对路由变化做出响应...
      }
    )
  },

或者使用路由导航守卫 beforeRouteUpdate :(导航守卫后面讲)

  async beforeRouteUpdate(to, from,next) {
    // 对路由变化做出响应...
    this.userData = await fetchUser(to.params.id)
      
    next()
  },
参数中自定义正则

当定义像 :userId 这样的参数时,我们内部使用以下的正则 ([^/]+) (至少一个不是斜杠 / 的字符)来从 URL 中提取参数。

const routes = [
  // /:orderId -> 仅匹配数字
  { path: '/:orderId(\\d+)' },
  // /:productName -> 匹配其他任何内容
  { path: '/:productName' },
]
可重复参数

如果你需要匹配具有多个部分的路由,如 /first/second/third,你应该用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复:

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]

// 这些也可以通过在右括号后添加它们与自定义正则结合使用:
const routes = [
  // 仅匹配数字
  // 匹配 /1, /1/2, 等
  { path: '/:chapters(\\d+)+' },
  // 匹配 /, /1, /1/2, 等
  { path: '/:chapters(\\d+)*' },
]

这将为你提供一个参数数组,而不是一个字符串,并且在使用命名路由时也需要你传递一个数组

// 给定 { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 产生 /
router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
// 产生 /a/b

// 给定 { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 抛出错误,因为 `chapters` 为空
Sensitive 与 strict 路由配置

默认情况下,所有路由是不区分大小写的 , 并且能匹配带有或不带有尾部斜线的路由

  • strict: true 不允许尾部斜线
  • sensitive: true 严格区分大小写
const router = createRouter({
  history: createWebHistory(),
  routes: [
    // 将匹配 /users/posva 而非:
    // - /users/posva/ 当 strict: true
    // - /Users/posva 当 sensitive: true
    { path: '/users/:id', sensitive: true },
    // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/
    { path: '/users/:id?' },
  ],
  strict: true, // applies to all routes
})
可选参数

你也可以通过使用 ? 修饰符(0 个或 1 个)将一个参数标记为可选:

请注意,* 在技术上也标志着一个参数是可选的,但 ? 参数不能重复。

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

嵌套路由

通过 Vue Router 来表达多层嵌套 的组件结构

注意,以 / 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。

所以嵌套子路由不需要使用 / 开头

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
        // 当 /user/:id 匹配成功
       {
        path: '',
        component: UserDefault,
      },
      {
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]
// 父文件
<div id="app">
  <router-view></router-view>
</div>
// 子文件
<div class="user">
    <h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
嵌套路由命名
const routes = [
  {
    path: '/user/:id',
    component: User,
    // 请注意,只有子路由具有名称
    children: [{ path: '', name: 'user', component: UserHome }],
  },
]

在一些场景中,你可能希望导航到命名路由而不导航到嵌套路由。例如,你想导航 /user/:id 而不显示嵌套路由。那样的话,你还可以命名父路由,但请注意重新加载页面将始终显示嵌套的子路由,因为它被视为指向路径/users/:id 的导航,而不是命名路由:

const routes = [
  {
    path: '/user/:id',
    name: 'user-parent',
    component: User,
    children: [{ path: '', name: 'user', component: UserHome }],
  },
]

编程式导航

push
声明式编程式
<router-link :to="...">router.push(...)

在组件内部,你可以使用 $router 属性访问路由,例如 this.$router.push(...)。如果使用组合式 API,你可以通过调用 useRouter() 来访问路由器。

router.push 可以接收 字符串对象

let router = useRouter()

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

如果提供了 pathparams 会被忽略 ,所以需要以下写法:

const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用

由于属性 torouter.push 接受的对象种类相同,所以两者的规则完全相同。

router.push 和所有其他导航方法都会返回一个 Promise,让我们可以等到导航完成后才知道是成功还是失败。

replace

它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。

声明式编程式
<router-link :to="..." replace>router.replace(...)
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
push 和 replace 区别:

push :导航时会向history中添加路由记录

replace :不会向history中添加路由记录

go

横跨历史 router.go()

该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

命名路由

为路由配置 name ,有以下优点:

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)
const routes = [
  {
    path: '/user/:username',
    name: 'user',
    component: User,
  },
]

使用 name 实现路径导航:

// to 
<router-link :to="{ name: 'user', params: { username: 'erina' }}">
  User
</router-link>

// router.push
router.push({ name: 'user', params: { username: 'erina' } })

注意: 所有路由的命名都必须是唯一的。如果为多条路由添加相同的命名,路由器只会保留最后那一条。

命名视图

一个路由同时 (同级) 展示多个视图

你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

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

路由表配置:此时使用 components ,为多个组件命名。 一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件

    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },

重定向 & 别名

重定向

/home 重定向到 /

const routes = [{ path: '/home', redirect: '/' }]

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

const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

甚至是一个方法,动态返回重定向目标:

const routes = [
  {
    // /search/screens -> /search?q=screens
    path: '/search/:searchText',
    redirect: to => {
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  },
  {
    path: '/search',
    // ...
  },
]

请注意,导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在上面的例子中,在 /home 路由中添加 beforeEnter 守卫不会有任何效果。

在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 childrenredirect 属性,它也应该有 component 属性。

相对重定向
const routes = [
  {
    // 将总是把/users/123/posts重定向到/users/123/profile。
    path: '/users/:id/posts',
    redirect: to => {
      // 该函数接收目标路由作为参数
      // 相对位置不以`/`开头
      // 或 { path: 'profile'}
      return 'profile'
    },
  },
]
别名

配置:

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

上方 当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /的组件。

多个别名:

const routes = [
  {
    path: '/users',
    component: UsersLayout,
    children: [
      // 为这 3 个 URL 呈现 UserList
      // - /users
      // - /users/list
      // - /people
      { path: '', component: UserList, alias: ['/people', 'list'] },
    ],
  },
]

动态路由别名:

const routes = [
  {
    path: '/users/:id',
    component: UsersByIdLayout,
    children: [
      // 为这 3 个 URL 呈现 UserDetails
      // - /users/24
      // - /users/24/profile
      // - /24
      { path: 'profile', component: UserDetails, alias: ['/:id', ''] },
    ],
  },
]

路由组件传参

将 props 传递给路由组件

封装组件时使用 $route 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为:

// 将
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const routes = [{ path: '/user/:id', component: User }]
// 改为:

const User = {
  // 请确保添加一个与路由参数完全相同的 prop 名
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const routes = [{ path: '/user/:id', component: User, props: true }]

这允许你在任何地方使用该组件,使得该组件更容易重用和测试。

布尔模式

props 设置为 true 时,route.params 将被设置为组件的 props。

对象模式
const routes = [
  {
    path: '/promotion/from-newsletter',
    component: Promotion,
    props: { newsletterPopup: false }
  }
]
// props 中可接收到 newsletterPopup: false 
函数模式
const routes = [
  {
    path: '/search',
    component: SearchUser,
    props: route => ({ query: route.query.q })
  }
]

URL /search?q=vue 将传递 {query: 'vue'} 作为 props 传给 SearchUser 组件。

请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 vue 才可以对状态变化做出反应。

命名视图

命名视图需要单独为每个视图定义props配置

const routes = [
  {
    path: '/user/:id',
    components: { default: User, sidebar: Sidebar },
    props: { default: true, sidebar: false }
  }
]
通过 RouterView

你还可以通过插槽传递任意参数:

<RouterView v-slot="{ Component }">
  <component
    :is="Component"
    view-prop="value"
   />
</RouterView>
// 在这种情况下,所有视图组件都会接收到 view-prop。通常这并不是一个好主意,因为这意味着所有的视图组件都声明了一个 view-prop prop,但这未必需要。所以请尽可能使用上述的其他选项。

不同的历史模式

Hash 模式

hash 模式是用 createWebHashHistory() 创建的:

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})

它在内部传递的实际 URL 之前使用了一个哈希字符(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。

Memory 模式

Memory 模式不会假定自己处于浏览器环境,因此不会与 URL 交互也不会自动触发初始导航。这使得它非常适合 Node 环境和 SSR。它是用 createMemoryHistory() 创建的,并且需要你在调用 app.use(router) 之后手动 push 到初始导航

它不会有历史记录,这意味着你无法后退前进

import { createRouter, createMemoryHistory } from 'vue-router'
const router = createRouter({
  history: createMemoryHistory(),
  routes: [
    //...
  ],
})
HTML5 模式

createWebHistory() 创建 HTML5 模式, 当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id。 由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就尴尬了。 要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。

服务器配置示例

注意:以下示例假定你正在从根目录提供服务。如果你部署到子目录,你应该使用Vue CLI 的 publicPath 配置和相关的路由器的 base 属性。你还需要调整下面的例子,以使用子目录而不是根目录(例如,将RewriteBase/ 替换为 RewriteBase/name-of-your-subfolder/)。

  • Apache
<IfModule mod_negotiation.c>
  Options -MultiViews
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

也可以使用 FallbackResource 代替 mod_rewrite

  • nginx
location / {
  try_files $uri $uri/ /index.html;
}
  • 原生 Node.js
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)
  })

404

这有一个注意事项。你的服务器将不再报告 404 错误,因为现在所有未找到的路径都会显示你的 index.html 文件。为了解决这个问题,你应该在你的 Vue 应用程序中实现一个万能的路由来显示 404 页面。

const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: '/:pathMatch(.*)', component: NotFoundComponent }],
})

在这里插入图片描述

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值