vue3.0(十六)路由vue-router


路由

  1. 路由(routing)
    路由是指从源页面到目的页面时,决定端到端路径的决策过程。
  2. 前端路由
    前端路由即由前端来维护一个路由规则。实现模式有两种:
  • 利用URL的Hash模式:就是常说的锚点,JavaScript通过 hashChange事件来监听URL的改变。(IE7及以下版本需要使用轮询)。在地址中以’#'分隔页面
  • 利用HTML5的History模式:它使用URL看起来像普通网站一样,在地址中以’/'分隔页面。但是页面并没有跳转。这种模式需要服务器端支持,服务端在接收到所有的请求后,都指向同一个HTML文件,不然会出现页面错误。

vue-router路由

路由就是根据一个请求路径选中一个组件进行渲染的决策过程。VueRouter路由是Vue官方推出的路由管理器。
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。

1. vue-router 功能

  1. 嵌套路由映射
  2. 动态路由选择
  3. 模块化、基于组件的路由配置
  4. 路由参数、查询、通配符
  5. 展示由 Vue.js 的过渡系统提供的过渡效果
  6. 细致的导航控制
  7. 自动激活 CSS 类的链接
  8. HTML5 history 模式或 hash 模式
  9. 可定制的滚动行为
  10. URL 的正确编码
  • route:表示单个路由。
  • routes:表示多个路由的集合。是一个数组,在这个数组中包含了多个route。
  • router:译为路由器,是route、routes的管理者。
    路由的查找过程是: router --> routes --> route(当用户在页面上点击按钮的时候,router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由)

2. vue-router 组成

  • VueRouter类:路由器类,维护的一种路由表,根据路由请求在路由视图中动态渲染选中的组件。
  • router-link:路由的链接组件,类似于<a>标签。声明用以提交路由请求的用户接口。
  • router-view:路由视图组件,路由出口,负责动态渲染路由选中的组件。

3. vue-router 常用的函数

  • createRouter函数:创建路由器
  • createWebHashHistory函数:创建Hash模式的路由
  • createWebHistory函数:创建History模式路由
import { createRouter, createWebHistory } from 'vue-router'

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

vue-router使用

1.安装

   npm install vue-router
   // 安装最新版本
   npm i vue-router -s

2. vue3.0配置

router/index.ts文件

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    // routViewere level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 创建 Vue 应用
const app = createApp(App)
app.use(router).mount('#app')

3. vue2.0配置

import Vue from 'vue'
import Router from 'vue-router'
import login from "@/views/loginB";
Vue.use(Router)

const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  linkActiveClass: 'on',
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return {
        x: 0,
        y: 0
      }
    }
  },
  routes: [
    //login
     {
      path: '/login',
      component: login,
      name: 'login',
    },
    {
      path: '*',
      redirect: '/admin',
    },
  ],
})

main.js文件

import Vue from 'vue'
import App from './App.vue'
//6.引入导出的路由
import router from './router/index'

Vue.config.productionTip = false

new Vue({
  //7.注册路由
  router,
  render: h => h(App),
}).$mount('#app')

4. 基本用法

  1. 声明路由链接和占位符
    在 src/App.vue 组件中,使用 vue-router 提供的和声明路由链接和占位符
    <template>
      <div class="app">
        <h2>App</h2>
        <!-- 设置要跳转的url,将来会被渲染成a标签 -->
        <!-- 点击首页, url会自动拼接上/#/home, Home组件 就会替换下面的router-view -->
        <router-link to="/home">首页</router-link>
        <!-- 点击关于, url会自动拼接上/#/about, About组件 就会替换下面的router-view -->
        <router-link to="/about">关于</router-link>
    
        <!-- 设置组件要展示的位置 -->
        <router-view></router-view>
      </div>
    </template>
    
  2. 路由重定向
    指的是:用户在访问地址A的时候,强制用户跳转到地址 C ,从而展示特定的组件页面
    通过路由规则的redirect属性,指定一个新的路由地址,可以很方便地设置路由的重定向
    routes :[
             {
              path: "/",
              redirect:'/home',
            },
            {
              path: "/home",
              name:'home',
              component: Home,
            },
            {
              path: "/about",
              name:'about',
              component: () => import("../components/About.vue"),
            },
          ]
    
    补充:{ path: ‘*’, component: NotFound } 可在最后加上,表示上面的路由都没有匹配到时会展示此组件
  3. 嵌套路由
    通过路由实现组件的的嵌套展示,叫做嵌套路由,
  • 比如Home中包括Product、Message,它们可以在Home内部来回切换,这个时候我们就需要使用嵌套路由,在Home中也使用 router-view 来占位之后需要渲染的组件
  • 使用嵌套路由, 首先我们需要配置嵌套路由:在我们要配置嵌套路由的映射关系中, 添加children属性
    { 
    	  path: "/home",
    	  component: () => import("../views/Home.vue"),
    	  // children中配置home的二级路由
    	  children: [
    	    // 定义默认展示路由
    	    {
    	      path: "/home",
    	      redirect: "/home/product"
    	    },
    	    {
    	      // 配置HomeProduct组件路由, 二级路径直接写子路径即可
    	      path: "product", // 相当于: /home/product
    	      component: () => import("../views/HomeProduct.vue")
    	    },
    	    {
    	      // 配置HomeMessage组件路由
    	      path: "message",
    	      component: () => import("../views/HomeMessage.vue")
    	    }
    	  ]
    	}
    

注: 1. 子路由路径以 / 开头代表全路径配置,需要包含父路由路径,如 path:‘/home/product’
2. 子路由可省略 / 开头,自动继承父路由路径,如 path:‘child’ 上面也有代码说明也有介绍

在Home组件中定义router-view展示二级路由

<template>
  <div>
    <h2>Home</h2>
    <!-- router-link切换导航 -->
    <router-link to="/home/product">商品</router-link>
    <router-link to="/home/message">信息</router-link>

    <!-- 使用router-view站位 -->
    <router-view></router-view>
  </div>
</template>	
  1. 动态路由匹配
    路由的参数根据需求是动态可变的,就涉及到路由的传值,分为两种
  • query查询参数形式传值(键值对)
  • params路径参数形式传值
  • 通过实例的$route对象获取路由传来的值
    1. query传值
      to属性的值由字符串改为对象,路由由path字段负责,传递的值由query字段负责
    <router-link :to="{path:'/cat',query:{id:111,name:'小狗'}}">商品</router-link>
    //2.0商品组件里取值
    this.$route.query
    //vue3.0组件里取值
    import { useRouter, useRoute } from 'vue-router';
    const route = useRoute();
    const paramValue = route.query;
    // vue3.0实现路由跳转
    const userRouter = useRouter();
    
    1. params传值
      to属性的值由字符串改为对象,路由由name字段负责,传递的值由params字段负责
    	//路由配置
       {
          path: "/sale/:id/:type",
          name:'S',
          component: () => import("../components/Sale.vue"),
    	},
    	//使用
    	<router-link :to="{name:'S',params:{id:111,type:'羽绒服'}}">商品</router-link>
    	//2.0商品组件里取值
    	this.$route.params
     	import { useRouter, useRoute } from 'vue-router';
        const route = useRoute();
        const paramValue = route.query;
        // vue3.0实现路由跳转
        const userRouter = useRouter();
    

5. 编程式导航

路由的页面跳转是通过<router-link>标签完成的,称为声明式导航
但是有时候希望通过代码来完成页面的跳转,比如点击的是一个按钮, 点击一个span等等其他元素也实现页面跳转,这样就叫编程式导航
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是

  • push():跳转到指定 路由,并增加一条历史记录
  • replace():跳转到指定的 路由,并替换掉当前的历史记录,不会增加历史记录
  • go(数值 n):实现导航历史前进、后退
  • back():相当于 router.go(-1)
  • forward():相当于 router.go(1)
    vue2.0
    this.$router.back()
    this.$router.forward()
    this.$router.push("地址")
    this.$router.replace("地址")
    this.$router.go(2)
    
    vue3.0
    // 导入函数useRouter
    import { useRouter } from "vue-router"
    // 通过函数useRouter拿到路由对象
    const router = useRouter()
    function btnClick() {
        router.back()
    	router.forward()
    	router.push("地址")
    	router.replace("地址")
    	router.go(2)
    }
    

6. 路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。

// 替换成
const UserDetails = () => import('./views/UserDetails.vue')

const router = createRouter({
  // ...
  routes: [
    { path: '/users/:id', component: UserDetails }
    // 或在路由定义里直接使用它
    { path: '/users/:id', component: () => import('./views/UserDetails.vue') },
  ],
})

注意
不要在路由中使用异步组件。异步组件仍然可以在路由组件中使用,但路由组件本身就是动态导入的。
把组件按组分块

  1. 使用 webpack
    有时候想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4):
    const UserDetails = () =>
      import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
    const UserDashboard = () =>
      import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
    const UserProfileEdit = () =>
      import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
    
    webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
  2. 使用 vite
    在Vite中,你可以在rollupOptions下定义分块:
    // vite.config.js
    export default defineConfig({
      build: {
        rollupOptions: {
          // https://rollupjs.org/guide/en/#outputmanualchunks
          output: {
            manualChunks: {
              'group-user': [
                './src/UserDetails',
                './src/UserDashboard',
                './src/UserProfileEdit',
              ],
            },
          },
        },
      },
    })
    

导航守卫

导航守卫可以控制路由的访问权限,在home页面跳转到about页面的这个过程, 我们称之为导航,在跳转这个过程的中间环节, 我们称之为导航守卫, 我们可以在这个环节对跳转进行拦截, 进行逻辑判断

  1. 全局前置守卫
    每次发生路由的导航跳转前,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制

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

    to:将要进入的目标路由对象
    from:当前导航正要离开的路由
    next():表示放行,允许这次路由导航
    next 函数的 3 种调用方式
    1. 当前用户拥有后台主页的访问权限,直接放行:next()
    2. 当前用户没有后台主页的访问权限,强制其跳转到登录页面:next(‘/login’)
    3. 当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)

    // 全局前置守卫
    router.beforeEach((to, from, next) => {
    	// 要进行导航守卫的路径值  绕过pathArr中设置的路由
        const pathArr = ['/home','/home/users', '/home/rights']
        if (pathArr.indexOf(to.path) !== -1) {
            const token = localStorage.getItem('token')
            if (token) {
                next()
            } else {
                next('/login')
            }
        } else {
            next()
        }
    })
    

    另一种写法就是给每一个路由添加meta配置项,通过meta里的真假值来判断是否需要进行判断

    { 
        path: 'users',
        component: Users,
        meta: { isAuth: true },
      },
    // 全局前置守卫
    router.beforeEach((to, from, next) => {
        if (to.meta.isAuth) { // 判断是否需要进行导航守卫
            const token = localStorage.getItem('token')
            if (token) {
                next()
            } else {
                next('/login')
            }
        } else {
            next()
        }
    })
    
  2. 全局后置钩子

    • 每次发生路由的跳转之后,都会触发全局后置钩子,用的比较少
    • 但是可能会有这样如下这样的需求,就是当点击某路由链接时,改变 document.title
    • 全局后置钩子是没有第三个参数中的next的
    { 
        path: 'users',
        component: Users,
        meta: { isAuth: true, title: '用户管理'  },
      },
    // 全局后置守卫
    router.afterEach(function (to, from) {
      document.title = to.meta.title || '管理系统' // 修改页面的title
    })
    
  3. 路由独享守卫
    路由独享守卫,与全局前置路由守卫没啥区别,只是作用的范围不同
    独享路由守卫是没有后置路由守卫的

    { 
       path: 'users',
        component: Users,
        meta: { isAuth: true, title: '用户管理'  },
        beforeEnter: (to, from, next)=>{
    	   // ...
        }
     },
    
  4. 组件内路由守卫
    组件中的使用

是没有前置后置可分的,因为beforeRouteLeave是离开该组件时才会被调用,并不是跳转之后调用的
使用场景:判断路由权限的时候,需要当前组件数据源作辅助判断时,可以考虑使用

<template>
  <h4 class="text-center">订单管理</h4>
</template>

<script>
export default {
  name: 'MyOrders',
  // 通过路由规则,进入该组件时被调用,不能获取到组件实例this,因为当守卫执行前,组件实例还没被创建
  beforeRouteEnter(to, from, next){
    // ...
  },
// 通过路由规则,路由改变时被调用,能获取到组件实例this,例如带有动态参数的路径 /foo/:id,在/foo/1和/foo/2切换时
  beforeRouteUpdate(to, from, next){
    // ...
  },
  // 通过路由规则,离开该组件时被调用,能获取到组件实例this
  beforeRouteLeave(to, from, next){
    // ...
  }
}
</script>

路由的两种工作模式

路由有两种工作模式,分别是:hash和history

  1. hash模式
    • 默认开启的就是 hash 的工作模式
    • #就是代表 hash ,后面就是 hash 值
    • #后面的值都是不发给服务器的
  2. history模式
  • 是没有#号的
  1. hash 和 history 的区别
  • 对于一个url来说,什么是hash值?——#及其后面的内容就是hash值

  • hash值不会包含在HTTP请求中,即:hash值不会带给服务器

    hash模式:

    1. 地址中永远带有#号,不美观
    2. 若以后将地址通过第三方手机app分享,若app效验严格,则地址会被标记为不合法
    3. 兼容性较好

    history模式:

    1. 地址干净美观
    2. 兼容性和hash模式相比略差
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

在服务器端配置Vue Router的history模式,你需要确保所有路由都会指向你的单页面应用的入口文件(index.html)。以下是使用不同服务器配置history模式的方法。
Nginx
服务器配置中添加一个location块来实现相同的功能。

location / {
  try_files $uri $uri/ /index.html;
}

Node.js (Express)
使用Express作为服务器,可以使用history中间件来处理路由。

nst express = require('express');
const history = require('connect-history-api-fallback');
 
const app = express();
 
app.use(history());
// 其他中间件配置...
 
app.use(express.static(__dirname + '/public'));
 
app.listen(3000);

Apache
在Apache服务器配置中,需要使用.htaccess文件来重写所有路由到index.html。

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值