vue3 - router学习后的一些思考和总结,防止忘记了。

627 阅读4分钟

vue-router是什么?

vue-router是什么,它是干什么的? 首先需要知道路由这个词,这里指的路由并不是指我们平时所说的生活中的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。

那与传统的页面跳转有什么区别呢?

1.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。

2.传统的页面应用,是用一些超链接来实现页面切换和跳转的。

在vue-router单页面应用中,切换路由实际上是组件的切换。路由模块的本质 就是建立起url和组件之间的映射关系。

至于为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,没有页面的实际切换,只是页面的显示控制。

vue-router实现原理

SPA(single page application):单一页面应用程序,有且只有一个完整的页面;当它在加载页面的时候,不会加载整个页面的内容,而只更新某个指定的容器中内容。

单页面应用(SPA)的核心之一是:

1.更新视图而不重新请求页面;

2.vue-router在实现单页面前端路由时,提供了三种方式:Hash模式、History模式、abstract模式,旧版根据mode参数来决定采用哪一种方式,新版则如下

image.png

路由模式

vue-router 提供了三种运行模式:

● hash: 使用 URL hash 值来作路由。默认模式。

● history: 依赖 HTML5 History API 和服务器配置。

● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端,不需要依赖浏览器的API。

Hash模式

vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。

hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/..),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同组件。

image.png

History模式

HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;

由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

image.png 当使用这种历史模式时,URL 会看起来很 "正常",例如 https://example.com/user/id。漂亮!

不过,问题来了。由于我们的应用是一个单页的客户端应用,如果路由不存在,并且没有适当的服务器配置,用户在浏览器中直接访问就会得到一个 404 错误。这在用户体验上不是太好。

解决办法如下:
服务端

image.png

路由部分(这样在路由匹配不到的时候,可以显示404页面,当然也可以根据需求使用路由守卫来处理,附属上找到的例子

image.png

abstract模式

abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。不依赖于浏览器的api实现路由的跳转管理。

使用SSR服务端渲染的时候用到这种模式

image.png

通过查询,我发现这个模式的另一个用途,一起总结下来,利用到了abstract这种与浏览器分离的路由模式(仅限于旧版本的vue2.x,新的vue3无法以此种方式尝试验证,createMemoryHistory还是用于SSR使用)
抽屉显例
/*route.js*/
const routes = [
  {
    path: "/",
    redirect: "abstract-route",
  },
  {
    path: "/embed-route",
    name: "embedded",
    component: () =>
      import("../views/embed.vue"),
  },
  {
    path: "/abstract-route",
    name: "abstract",
    component: () =>
      import("../views/abstract.vue"),
  },
];
const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes
})

/*abstract.vue*/
<template>
  <div>
    <RouterDrawer
      :visible.sync="visible"
      :path="{ name: 'embedded' }"
      size="50%"
      title="drawer comps"
    ></RouterDrawer>
    <el-button @click="visible = true">open drawer</el-button>
  </div>
</template>


/*embedded.vue*/
<template>
  <div>
    embedded views
  </div>
</template>


router-drawer 封装

当前项目默认是history 的路由模式,因此在进入abstract页面时,浏览器Url为http://localhost:8080/abstract-route,而router-drawer要做的是在此基础上,重新实例化一个abstract模式的路由,然后在组件当中利用<router-view />去挂载要被内嵌的目标页面。即:

<template>
  <el-drawer
    :visible.sync="visible"
    v-bind="$attrs"
    :before-close="handleClose"
  >
    <router-view />
  </el-drawer>
</template>
<script>
import { routes } from "../router/index";
import VueRouter from "vue-router";

export default {
  name: "router-drawer",
  props: {
    path: {
      type: Object,
      required: true,
    },
    visible: {
      type: Boolean,
      required: true,
      default: false,
    },
  },
   // 此处实例化一个新的router来配合当前页面的router-view
  router: new VueRouter({
    mode: "abstract",
    base: "/",
    routes,
  }),
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
    },
  },
  mounted() {
    console.log("drawer router", this.$router);
    this.$router.push(this.path);
  },
};
</script>
代码执行结果

image.png