Vue Router 4 的使用,一篇文章给你讲透彻

Vue 3.X 使用Vue Router 4.x 进行路由配置,本文我们就来研究下如何使用Vue Router 4.x,本文中所有的使用方式都是使用 Composition API的方式。

本文通过一步步介绍Vue Router 4.x的使用,来搭建一个简单的博客系统,让你对新版的Vue Router 4.x有一个完整的认识,然后能够非常轻松滴将Vue Router 4.x应用在自己的项目中。

项目初始化

项目搭建

项目使用vite进行创建。

npm init vite@latest vue-router-blog
npm install
npm run dev

目前安装的是Vue 3.2.25

配置vite.config.js

我们配置@别名,这样就比较方便书写引入文件的路径

// 引入文件
const path = require("path");

export default defineConfig({
  // 添加 @
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  plugins: [vue()],
});

配置jsconfig.json

jsconfig.json可以让VSCode更加智能

{
  "include": [
    "./src/**/*",
  ],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Vue Router 4初体验

安装Vue Router 4
npm i vue-router@4

目前安装的是Vue Router 4.0.12

创建两个页面Home.vueAbout.vue




这两个页面很简单,每个页面仅仅就是显示一行文字

创建router

我们在src目录下新建router目录,在router目录下创建index.js文件, 在里面进行路由的信息配置。

import { createRouter, createWebHistory } from "vue-router";

// 引入
import Home from "@/views/Home.vue";
import About from "@/views/About.vue";

// 路由信息
let routes = [
  {
    path: "/",
    name: 'home',
    component: Home,
  },
  {
    path: "/about",
    name: 'about',
    component: About,
  },
];

// 路由器
const router = createRouter({
  history: createWebHistory(), // HTML5模式
  routes,
});

export default router;

安装router

将路由安装router安装到app上。

import { createApp } from 'vue'
import App from './App.vue'

// 引入插件
import router from "@/store/index";
// 安装router插件
createApp(App).use(router).mount('#app')
使用 router-linkrouter-view

修改App.vue


至此,我们的就实现了页面间的切换功能了。

几个重要的概念

router-link组件和a标签的区别?

router-link组件底层也是渲染的a标签,但是router-link的页面切换只是更新了页面的部分内容,不会进行整个页面的刷新,而a标签跳转(例如:调到Home标签
)是对整个页面进行刷新。

底层原理是router-link劫持了元素的点击事件,添加了处理页面更新的逻辑。

Hash模式和HTML5模式的区别?

Hash模式的URL中有一个#号,eg:http://localhost:3000/#/about, #号后面的就是Hash地址,这个模式以前是SPA的常用模式,但是链接有一个#号比较丑。

HTML5模式和正常的链接地址一样的,eg:http://localhost:3000/about, 这个地址很优雅,但是有一个问题,需要服务器支持。 原因是浏览器中输入http://localhost:3000/about支持,服务器以为要访问根路劲下的about目录的HTML文件,而不是访问根路劲下的HTML文件。

webpackvite启动的服务器是支持HTML5模式的,所以开发环境使用HTML5模式没有问题。

router-link组件和router-view组件为什么能直接使用?

安装router插件的时候注册了这两个全局组件,所以能直接使用。

install(app: App) {
    app.component('RouterLink', RouterLink)
    app.component('RouterView', RouterView)
}

路由懒加载

上面写法有一个严重的问题,router中所有的组件都会被一次加载。我们的例子中就是 HomeAbout组件,即使有时候不会用到About组件, 也要加载,这对首页的显示会有很大的影响。

改造如下:




let routes = [
  {
    path: "/",
    name: 'home',
    
    component: () => import("@/views/Home.vue"),
  },
  {
    path: "/about",
    name: 'about',
    
    component: () => import("@/views/About.vue"),
  },
];

这样在开发环境中只有使用到组件才会加载进来,在生产环境中异步组件会分开文件进行打包。

修改代码(创建博客的框架)

为了方便介绍其他内容,我们修改一下代码内容:

新建模拟博客列表数据
[
  {
    "id": 1,
    "catId": 1,
    "catName": "iOS",
    "subCatId": 1,
    "subcatName": "推荐",
    "name": "RxSwift实现MVVM架构",
    "image": "https://images.xiaozhuanlan.com/photo/2018/2f5dff865155d756dfe04f2909cd1a36.png",
    "description": "在本文中,我将介绍iOS编程中的MVVM设计模式,当然还有RxSwift的介绍。本文分为两部分。在第1部分中简要介绍了RxSwift的设计模式和基础知识,在第2部分中 ,我们有一个使用RxSwift的MVVM的示例项目。"
  },
  
  //省略...
]

命名为data.json将其放置在src文件夹下

创建路由信息
// 路由信息
let routes = [
  {
    path: "/",
    name: 'home',
    component: () => import("@/views/All.vue"),
  },
  {
    path: "/ios",
    name: 'ios',
    component: () => import("@/views/iOS.vue"),
  },
  {
    path: "/android",
    name: 'android',
    component: () => import("@/views/Android.vue"),
  },
  {
    path: "/flutter",
    name: 'flutter',
    component: () => import("@/views/Flutter.vue"),
  },
  {
    path: "/web",
    name: 'web',
    component: () => import("@/views/Web.vue"),
  },
];

设置5个路由:全部iOSAndroidFlutterWeb

顶部导航组件


TheNavigation导航组件中有5个router-link,分别切换到全部iOSAndroidFlutterWeb

5个页面组件



APP.vue



至此,博客框架就完成了,实现了5个博客分类,效果如下图:

设置linkActiveClass

路由器可以设置router-link激活的类:

const router = createRouter({
  history: createWebHistory(),
  routes,
  
  linkActiveClass: "blog-active-link"
});

然后设置样式:

#nav .blog-active-link  {
  color: red;
  border-bottom: 2px solid red;
}

Vue Router 4 的使用,一篇文章给你讲透彻_第1张图片

命名路由

我们在顶部导航组件使用的跳转都是路径跳转例如:to="/", 我们可以给路由设置一个名称name,这样可以通过路由的名称name进行跳转。


路由的query

前面提到的5个博客分类是固定的,我们点击博客列表的每条数据进入博客详情,此时由于不同的博客内容是不同的,所以不能固定写死。实现方法一是通过路由传参实现。

添加博客详情的路由
let routes = [
  //...
  {
    path: '/blogdetail',
    name: "blogdetail",
    component: () => import("@/views/BlogDetail.vue")
  }
];
query传参


设置query: { id: blog.id } }给路由传参

接收query传参



通过route.query.id就能获取到传递的博客id, 然后就能显示对应的博客信息了。

动态路由

博客详情的页面逻辑,也可以用动态路由去实现。

修改博客详情的路由

let routes = [
  //...
  {
    
    path: '/blogdetail/:id',
    name: "blogdetail",
    component: () => import("@/views/BlogDetail.vue")
  }
];

:id 表示 路由的路径是动态的,路径最后表示博客id.

传参


设置params: { id: blog.id } }给动态路由传参

接收参数
let blogId = route.params.id;

通过route.params.id就能获取到传递的博客id, 然后就能显示对应的博客信息了。

重命名路由

知道了动态路由的逻辑后,我们当然可以把iOS, Android, Flutter, Web四个页面合并为一个页面。

合并router

let routes = [
  {
    path: "/",
    name: 'home',
    component: () => import("@/views/All.vue"),
  },
  
  {
    path: "/category/:catId",
    name: 'category',
    component: () => import("@/views/All.vue"),
  },
  {
    path: '/blogdetail/:id',
    name: "blogdetail",
    component: () => import("@/views/BlogDetail.vue")
  }
];
修改导航


列表


这样我就可以把iOS.vue,Android.vue,Flutter.vue,Web.vue四个组件文件删除了。

你应该有个疑问,home路由的内容其实和category路由的内容也是一样的,是否可以合并呢?

重命名"/"

可以将"/“重命名为’/category/0’,这样所有的5个路由都将访问”/category/:catId"这个路由了。


let routes = [
  {
    path: "/",
    
    redirect: '/category/0'
  },
  {
    path: "/category/:catId",
    name: 'category',
    component: () => import("@/views/All.vue"),
  },
  {
    path: '/blogdetail/:id',
    name: "blogdetail",
    component: () => import("@/views/BlogDetail.vue")
  }
];
import sourceData from "@/data.json";
let arrs = catId != '0' ? sourceData.filter((blog) => blog.catId == catId) : sourceData;

判断下,如果catId != '0'为分类筛选,否则就是显示全部

监听路由变化

此时的代码出现了问题,点击顶部的导航切换不同的分类,底下的列表将不会变化。这是因为组件复用了。此时需要监听组件的路由的变化,切换数据。

可以通过watch函数监听route.params, 当路由变化后,就可以重新获取数据。



禁止路由复用

解决上节问题,还有一个更简单的方法,就是禁止路由的复用。



通过这个方法,动态组件将不会复用,直接卸载旧组件,挂载新组件。所以性能上有丢丢的损耗。

给组件传递props

我们前面在组件中需要使用useRoute获取到路由,然后获取对应的route.params, 我们可以通过另外一种方式获取route.params

路由添加props属性

{
    path: "/category/:catId",
    name: 'category',
    component: () => import("@/views/All.vue"),
    
    props: true,
}
组件中获取props属性

组件中可以直接获取到catId参数,个人认为这种写法更优美。

路由props属性支持函数

{
    path: "/category/:catId",
    name: 'category',
    component: () => import("@/views/All.vue"),
    props: route => ({ catId: parseInt(route.params.catId) }) ,
}

函数中,可以对参数进行处理,我们的例子中是将catId从字符串变成了数字

// 定义props
const props = defineProps({
  catId: {
    type: Number,
    required: true,
  }
})

let arrs = props.catId !== 0 ? sourceData.filter((blog) => blog.catId === props.catId) : sourceData;

props catId的定义和使用也要进行相应的修改

编程式导航

除了使用 来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

例如:可以在详情页加一个按钮,点击返回上一个页面


转场动画

Vue Router4 的转场动画的实现 和 以前的版本有些不一致。需要将transition 包含在router-view, 如下所示:

  
    
      
    
  

加上对应的css样式

/* fade 模式 name="fade" mode="out-in" */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

这样切换就有淡入淡出的效果了。效果自定义,很方便。

路由未匹配上

有时候用户可能输入一个根本不存在的路劲(例如:http://localhost:3000/categorys),此时最好是给显示个默认的404页面,这样用户体验更好。

404页面
定义路由

{
    path: '/:pathMatch(.*)*',
    name: "NotFound",
    component: () => import("@/views/404.vue"),
}

注意,这个路由一定要放在最后,否则就有问题了。

404页面

这个页面内容随意

路由守卫

路由独享的守卫

想象下用户浏览器地址栏输入http://localhost:3000/category/6, 其实也会出现一些问题,因为不存在这个分类。这时候需要进行处理, 当分类不存在的时候跳转到404页面。


  {
    path: "/category/:catId",
    name: 'category',
    component: () => import("@/views/All.vue"),
    props: route => ({ catId: parseInt(route.params.catId) }),
    
    beforeEnter: (to, from) => {
      // 如果不是正确的分类,跳转到NotFound的页面
      console.log(to.params.catId);
      if (!["0", "1", "2", "3", "4"].includes(to.params.catId)) { 
        return {
          name: "NotFound",
          // 这个是在地址栏保留输入的信息,否则地址栏会非常的丑
          params: { pathMatch: to.path.split("/").slice(1) },
          query: to.query,
          hash: to.hash,
        };
      }
    }
  },

判断如果不是正确的分类,跳转到NotFound的页面

路由全局守卫

在某些路由中需要一些特定的操作,譬如访问前必须是登录用户。这时候可以通过使用meta属性和全局守卫来实现。

譬如有一个课程专栏我们设置为需要用户登录才能访问。我们可以如下设置


  {
    path: '/course',
    name: "course",
    component: () => import("@/views/Course.vue"),
    
    meta: {needLogin: true}
  },
  {
    path: '/login',
    name: "login",
    component: () => import("@/views/Login.vue"),
  },
  

添加一个全局守卫, 需要登录但是没有登录的情况下就跳转到登录页面


// 全局守卫
router.beforeEach((to, from) => {
  if (to.meta.needLogin && !userLogin) {
    // need to login
    return { name: "login" };
  }
});
组件内的路由守卫

前面的切换分类的章节的问题其实还有第三者解决方案,就是用组件内的路由守卫。


对于一个带有动态参数的路径 /category/:catId,在 /category/1/category/2 之间跳转的时候, 会触发onBeforeRouteUpdate的路由钩子函数,直勾勾自函数中可以进行数据的更新。

扩展 RouterLink

router-link可以实现路由的跳转,此外为了更加丰富功能,可以对其进行扩展。譬如我们可以扩展实现能够跳转到外部链接。





使用:


总结

Vue Router 4.x 的使用基本上介绍完了,最重要的特性是能和Composition API的搭配使用,此外使用上也还是有一些不小的变化。

你可能感兴趣的:(Vue,vue.js,前端,vscode)