IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 一口气拿下vue-router所有知识点,薪资暴涨3000 -> 正文阅读

[JavaScript知识库]一口气拿下vue-router所有知识点,薪资暴涨3000

一 vue路由基础使用【固定路由的配置和使用】

首先我们展示一下router使用后的效果。

首先启动服务后,输入路由http://localhost:8080/#/A,到达页面A,展示页面A内容,点击页面上按钮或者将url切换为http://localhost:8080/#/B,到达页面B,展示页面B内容。

在这里插入图片描述

在这里插入图片描述

怎样配置

  1. 创建两个文件src/views/a.vue和src/views/b.vue,用来展示路由切换效果。
// src/views/a.vue
<template>
  <div>
    <h1>我是页面a</h1>
  </div>
</template>
// src/views/b.vue
<template>
  <div>
    <h1>我是页面b</h1>
  </div>
</template>
  1. 创建src/router/index.js文件,在src/router/index.js中引入router必要文件,导出router对象。

src/router/index.js文件代码示例如下。

import Vue from "vue";
import VueRouter from "vue-router";
import a from "../views/a.vue";
import b from "../views/b.vue";

// 注册路由插件
Vue.use(VueRouter);

// 定义路由规则
const routes = [
  {
    path: "/B",
    name: "A",
    component: a,
  },
  {
    path: "/B",
    name: "B",
    component: b,
  },
];

// 创建router对象
const router = new VueRouter({
  routes,
});

export default router;
  1. 在src/main.js中,在全局vue实例创建的时候,注入router对象。

src/main.js代码示例如下。

...
import router from "./router";
...
new Vue({
  router,
  ...
  render: (h) => h(App)
}).$mount("#app");

这一步做了什么呢,我们可以将vue实例打印出来,看一下router写入前后,vue实例有什么变化。

// src/main.js
const vm1 = new Vue({
  router,
  //   store,
  render: (h) => h(App),
}).$mount("#app");

const vm2 = new Vue({
  // router,
  //   store,
  render: (h) => h(App),
}).$mount("#app");

console.log(vm1, vm2);

我们写了两个不同的Vue实例,两者唯一的区别是有无注入router,将它们打印出来后对比一下,可以看到,注入router的vue实例中增加了几个router相关的属性, r o u t e r 和 router和 routerroute属性被赋值。
在这里插入图片描述

请添加图片描述

点击打开后可以看到, r o u t e r 中 有 一 些 路 由 相 关 的 方 法 , router中有一些路由相关的方法, routerroute中有当前路由的信息。

## 图5 图6
在这里插入图片描述

这些信息已经挂载到了全局vue实例上,在页面中就可以直接使用了。

  1. 在App.vue中对应位置添加<router-view/>标签,该标签为路由组件的占位符,路由切换时,该标签会被替换成对应的路由组件。就达到了我们想要的效果。

在切换路由时,我们可以通过点击<router-link />标签或者手动更改url的方式进行页面切换,下面是一个带有<router-view/>标签和<router-link />标签的代码示例。

这里我们为router-link标签写上了一些样式,使展示更加美观。

<template>
  <div id="app">
    <router-link class="a" to='A'>跳往页面A</router-link>
    <router-link class="b" to='B'>跳往页面B</router-link>
    <router-view />
  </div>
</template>

<style>
.a,.b {
 border: 1px solid #42b983;
 padding: 5px 10px;
 margin: 5px;
 margin-top: 300px;
 color: #42b983;
}</style>

怎样控制路由跳转

刚刚介绍了路由在html中使用标签进行跳转,但有时我们希望在js中跳转,该怎样操作呢?这就用到上面挂载在vue实例上的$router。代码示例如下。

this.$router.push('/b') // 根据路由文件中的path跳转到页面B
this.$route.query // 取得当前路由的参数
this.$router.push({ name: 'B' }) // 根据路由文件中的name跳转到页面b
this.$router.replace('/b') // 在不记录当前的路由的情况下跳转到页面b,无法从b后退到当前路由
this.$router.go(-2) // 后退到历史某次访问的页面
this.$router.go(-1) === this.$router.back()

二 如何在路由中携带参数【动态路由的配置和使用】

刚才我们对于不同页面配置了不同固定路由。

不知你有没有注意到csdn的个人主页,小明的个人主页链接为https://blog.csdn.net/123,而小米的个人主页链接为https://blog.csdn.net/456,相同的页面对应的路由却不一样,似乎路由中携带了一个参数,用来区分不同用户,而路由指向的又是同一个页面,这是怎么回事呢?

## 图7 图8
在这里插入图片描述

怎样携带参数

我们再看上面配置的src/router/index.js文件route对象。

// src/router/index.js
const routes = [
  {
    path: "/a",
    name: "A",
    component: a,
  },
  {
    path: "/b",
    name: "B",
    component: b,
  },
];

这时我们希望将b页面的路由达到上面携带不同参数,有指向同一个页面的效果,我们将这种路由叫做动态路由,动态路由的配置也很简单,对应位置前加冒号就可以,代码示例如下。

// src/router/index.js
const routes = [
  {
    path: "/a",
    name: "A",
    component: a,
  },
  {
    path: "/b/:id", // 这里变化了哦
    name: "B",
    component: b,
  },
];

这时我们可以看到,原来的路由http://localhost:8080/#/b已经访问不到该页面了,将路由变更为http://localhost:8080/#/b/1可以正常访问页面,将b/后面更改为任意值都可以访问到页面。

带有参数的动态路由也可以通过js指定name跳转。

this.$router.push({ name: 'B', params: { id: 1 } })

在这里插入图片描述

现在参数已经写入动态路由中了,怎么获取该参数呢?

怎样获取参数

  1. 使用 r o u t e . p a r a m s . i d 获 取 , 动 态 路 由 中 的 参 数 都 可 以 通 过 v u e 全 局 属 性 route.params.id获取,动态路由中的参数都可以通过vue全局属性 route.params.idvueroute下的params对象获取,代码示例如下。
<template>
  <div>
    <h1>我是页面b</h1>
    通过当前路由获取:{{ $route.params.id }}
  </div>
</template>

<script>
export default {
  name: B
};
</script>

在这里插入图片描述

  1. 使用props获取,使用该方法需要在route路由中配置props为true,代码示例如下。
// src/router/index.js
const routes = [
  {
    path: "/a",
    name: "A",
    component: a,
  },
  {
    path: "/b/:id",
    name: "B",
    component: b,
    props:true,
  },
];
<template>
  <div>
    <h1>我是页面b</h1>
    通过props获取: {{ id }}
  </div>
</template>

<script>
export default {
  name: B,
  props: ["id"],
};
</script>

在这里插入图片描述

三 如何提取页面相同内容【嵌套路由的配置和使用】

刚刚我们在app.vue中写入了标签,该标签会被路由文件中配置的组件替换。

当前页面是这样的。

在这里插入图片描述在这里插入图片描述

现在我们有了新的需求,在b页面中,我们希望可以再开辟一块自由区域,可以随着路由变化。

这时我们的b页面就是一个layout页面(布局页面),它里面包含一部分可以分化的内容,可以分化出不同页面。

在这里插入图片描述

具体到页面上,我们将实现下面的效果。

在这里插入图片描述
在这里插入图片描述

首先我们新建三个文件,将layout页面和内部的两个分化页面layoutA和layoutB准备好。

// src/view/LayoutA.vue
<template>
    <div>AAAAAAAA</div>
</template>
// src/view/LayoutB.vue
<template>
    <div>AAAAAAAA</div>
</template>
// src/component/Layout.vue
<template>
  <div>
    <div class="bar">header</div>
    <div class="content">
    <router-view />
    </div>
    <div class="bar">footer</div>
  </div>
</template>

<style lang="scss" scoped>
.bar {
    width: 100px;
    padding: 10px;
    text-align: center;
    background-color: green;
    padding:20px 0;
    color: #fff;
    width: 100%;
}
.content {
    background-color: khaki;
    height: 300px;
    color: green;
    line-height: 300px;
    width: 100%;
    font-size: 40px;
}
</style>

然后我们在src/router/index.js中修改router配置如下。

const routes = [
  ... ...
  {
    path: "/b",
    name: "B",
    component: layout,
    children: [
      {
        name: "LayoutA",
        path: "/b/a",
        component: LayoutA,
      },
      {
        name: "LayoutB",
        path: "/b/b",
        component: LayoutB,
      },
    ],
  },
];

当路由/b/a或者/b/b的时候,组件layout会替换app.vue中的标签,组件LayoutA或者LayoutB会替换组件layout中的标签。

就实现了上面的效果。

四 路由的两种模式有什么不同【hash和history的区别】

表现形式有什么区别

hash模式带有#号,#号后面的内容是我们的路由地址,history模式是正常的路由。

hash:http://localhost:8080/#/b

history:http://localhost:8080/b

实现原理有什么区别

hash模式:基于锚点也就是#号,当#后面的路由发生变化后,触发onhashchange事件,在事件中对页面进行操作。

history模式:基于h5中的historyAPI。

history.pushState() 在浏览器记录中添加一个新纪录,不向后端发送请求。

history.replaceState() 修改浏览器历史中当前历史记录,不向后端发送请求。

由于浏览器的url栏中始终显示最新的url记录,所以就好像触发了页面更新一样,其实并没有。

五 history模式的使用和问题

history模式配置和使用

history的配置很简单,在src/router/index.js中,创建路由实例时设置属性mode为history即可。

import Vue from "vue";
import VueRouter from "vue-router";
...

Vue.use(VueRouter);

const routes = [...];

const router = new VueRouter({
  mode: "history", // 在这里设置
  routes,
});

export default router;

现在我们再次访问刚才的页面。

现在使用http://localhost:8080/#/b/b已经访问不到啦,我们将#去掉,url更改为http://localhost:8080/b/b,页面出现了。

history浏览器请求问题

history模式中,在触发页面跳转时,我们使用history.pushState() 在浏览器记录中添加一个新纪录,不向后端发送请求。

但如果我们使用浏览器触发跳转会怎样呢,比如直接向浏览器中输入url后,或者点击页面刷新。

有的同学有讲,我在开发过程中经常直接使用浏览器跳转呀,没遇到问题,这是因为我们使用的vue脚手架已经帮我们解决了这个问题,但是当代码打包,上线后,问题就出现了,浏览器会告诉我们,找不到该页面。

这是因为我们使用代码去进行跳转/b/b,实际上不是真实的跳转,不会向服务端发送请求,而浏览器跳转会向后端发送请求,后端一看,没有/b/b这个请求路径呀,就会报错。

所以使用history模式,需要在服务端配置一下。

如果我们使用的是node做服务端时,可以通过配置中间件/插件(connect-history-api-fallback)来实现对history的支持。把这个插件在node.js服务端引入后注册就好了。

如果我们使用的是nginx服务,在nginx的配置文件中配置当找不到对应页面的时候返回首页就好啦。

因为我们主要是讲vue-router,这里就不展开讲解node服务和nginx服务啦。

六 浅析vue-router实现原理【面试必备】

hash模式实现原理

  1. url中#后面的内容作为路径地址
  2. 监听hashchange事件
  3. 根据当前路由地址找到对应组件重新渲染

history模式实现原理

  1. 通过history.pushState()方法改变地址栏
  2. 监听popstate事件
  3. 根据当前路由地址找到对应组件重新渲染

七 怎样实现自己的vue-router

vue-router是什么

vue-router是什么?实现vue-router从何下手,首先我们来分析它的使用,在src/router/index.js中,我们引入vue-router,使用vue.use()注册了它,又new了它的实例,最后导出。

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const routes = [...];

const router = new VueRouter({
  mode: "history",
  routes,
});

export default router;

Vue.use方法里面的参数可以是一个对象或者一个函数,当它是一个函数的时候它会被执行,当它是一个对象的时候,会执行对象内部的install函数,那么vue-router是什么呢。

下面我们使用new创建了一个vue-router实例,这里可以看出,vue-router是一个类(类也是对象哦)。

我们现在对vue-router有了一定的认知,它是一个类,里面包含install方法,它有一个构造函数,可以对new时传入的参数进行处理。

知道了它是什么,我们就可以向着这个方向开发啦。

实现一个vue插件【install】

实现一个插件需要几步?首先我们写一个含有install方法的类。

export default class VueRouter {
  static install(Vue) { }
}

该类作为插件,被vue调用install方法的时候,会传入一个参数,就是vue构造函数。在install中我们可以保存vue构造函数和对vue构造函数做改动。

首先我们可以在install中对插件是否已安装做判断,已安装状态下直接返回。我们可以通过设置一个变量来记录。

然后将vue构造函数记录在一个变量中,以备使用。

let _Vue = null;
export default class VueRouter {
  static install(Vue) {
    // 判断当前插件是否被安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true;
    // 将vue构造函数记录到全局变量
    _Vue = Vue;
  }
}

最后对vue构造函数做一定的修改,我们刚才有提到,我们创建vue实例的时候,将返回的router传入了vue实例中。

new Vue({
  router,
  render: (h) => h(App),
}).$mount("#app");

传入之后vue做了什么呢,要知道router对于vue来说只是一个插件而已,vue原本的逻辑是不包括对router的处理的,所以router处理的这一步应该是写在vue-router的install中的。

在创建vue实例前,vue会调用钩子函数beforeCreate,这部分逻辑可以使用混入/mixin写入。

let _Vue = null;
export default class VueRouter {
  static install(Vue) {
    ...
    // 把创建vue实例时候传入的router对象注入到vue实例上
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router;
        }
      },
    });
  }
}

我们使用混入,为vue实例的beforeCreate钩子函数添加了一段逻辑,这里的this.$options指的就是我们在创建vue实例时传入的对象。比如下面的代码中,option就是含有router和render属性的对象。

new Vue({
  router,
  render: (h) => h(App),
}).$mount("#app");

还记得前面我们有将vue实例打印出来看吗,option中添加了router后,vue实例的$router就有值啦。

现在install函数就写好了,我们的vuerouter已经是一个合格的插件了,我们盘点一下一共做了几步。

  1. 判断当前插件是否被安装
  2. 将vue构造函数记录到全局变量
  3. 为vue添加beforeCreate逻辑,增加对router信息的处理。

实现构造函数

接下来我们看router/index的第二步,它传入了一个对象,new了一个实例。

const router = new VueRouter({
  routes,
});

接下来我们就要写VueRouter的构造函数,在构造函数中,对传入的对象做处理。

构造函数的用处主要是初始化一些内容,最终构造的实例是要挂在vue. r o u t e r 上 的 , 所 以 通 过 对 v u e 实 例 的 打 印 , 可 以 看 到 router上的,所以通过对vue实例的打印,可以看到 routervuerouter也就是VueRouter实例到底生成了什么。

我们实现一个简单的vue,就初始化几个主要的属性,首先将传入的options存储起来,然后设置一个双向绑定的变量data,用来记录当下的路由信息。

由于当下的路由信息是可以变化的,当它变化的时候,我们需要在页面可以观察到它的变化,而通过Vue.observable定义的变量,是可观测的,可以直接在computed和watch中使用,所以当下的路由信息变量采用Vue.observable定义。

let _Vue = null;
export default class VueRouter {
  ...
  constructor(options) {
    // 记录初始路由规则
    this.options = options;
    // 记录路由-组件键值对
    this.routeMap = {};
    this.options.routes.forEach(route => {
        this.routeMap[route.path] = route.component
    })
    // 记录当前路由
    this.data = _Vue.observable({
        current: '/a'
    })
  }
}

上面的代码中,我们定义了options存储变量this.options,和当前路由信息变量data以外,还有一个变量this.routeMap,这个变量是整理后的router规则,它的key是路由地址,value是组件。后面我们要根据它进行渲染。

实现router-link

刚刚我们有讲,router-link是router自带的一个组件,可以点击跳转到指定路由,这个组件我们的router也要有,那什么时间去注册这个组件呢?怎么注册呢?

这里我们使用vue的一个函数render函数,render函数可以创建vue组件,render函数创建vue组件后,可以自动注册到vue上。我们在模板中就可以使用注册的组件了。

我们首先使用html标签实现一下router-link组件。

<a :href="to"><slot></slot></a>

render函数是写在Vue.component()中的,Vue.component()接收两个参数,第一个参数为这次要生成/注册的组件名称,第二个参数为组件的配置,组件的配置中可以写props等使用js定义的内容和负责生成html标签的render函数。

代码实现如下。

    Vue.component("router-link", {
      props: {
        to: String,
      },
      // '<a :href="to"><slot></slot></a>'
      render(h) {
        return h(
          "a",
          {
            attrs: {
              href: this.to,
            },
          },
          [this.$slots.default]
        );
      },
    });

render函数有一个参数,该参数是创建节点的函数,该函数接受三个参数,html标签名称、属性对象列表(可选)和子节点数组(可选)。这里我们实现的组件结构为<a :href="to"><slot></slot></a>,它包含一个子节点,所以第三个参数只有一项[this.$slots.default],子节点是默认插槽,可以使用this.$slots.default获取,如果子节点是其他,可以在这里嵌套Vue.component函数去进行子节点的创建。

我们把注册过程写在我们的router类中的方法initComponents中。

因为这一步属于对vue的处理,所以我们放在install上去执行,放在mixin混入这步执行。

代码如下。

let _Vue = null;
export default class VueRouter {
  static install(Vue) {
    ...
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router;
          this.$options.router.init(); // 添加到这儿执行啦
        }
      },
    });
  }
  init() { // 预处理的步骤到这儿集合
    this.initComponents(_Vue);
  }
  initComponents(Vue) { // 添加了这个方法
    Vue.component("router-link", {
      props: {
        to: String,
      },
      render(h) {
        ...
      }
    });
  }
}

现在我们实验一下成果。

在router/index.js文件中,我们把引入的vue-router改成我们自己的router,然后修改一下路由配置。

// import VueRouter from "vue-router";
import VueRouter from "../vuerouter";
const routes = [
  {
    path: "/a",
    name: "A",
    component: a,
  },
  {
    path: "/b",
    name: "B",
    component: b,
  },
];

我们去到app.vue中,代码修改如下。

<template>
  <div id="app">
    <router-link class="a" to='A'>跳往页面A</router-link>
    <router-link class="b" to='B'>跳往页面B</router-link>
    <router-view />
</template>

打开页面,输入路由http://localhost:8080/a,点击标签,成功触发跳转。

在这里插入图片描述
在这里插入图片描述

注意看图中的url已经改变了,但是页面始终没有变化,页面的变化逻辑在router-view组件中,我们接下来实现一下router-view组件。

实现router-view

我们仍然使用render函数去实现,刚刚我们准备了一个key是路由地址,value是组件的变量routeMap,和记载当前路由的变量data.current,我们就用self.routeMap[self.data.current]找到对应的组件名称,而render函数是可以通过传入已经存在的组件名称,注册组件的。

我们直接把这段代码放置在刚刚router-link代码的下面。

let _Vue = null;
export default class VueRouter {
  ...
  initComponents(Vue) {
    ...
    const self = this;
    Vue.component("router-view", {
      render(h) {
        const component = self.routeMap[self.data.current];
        return h(component)
      },
    });
  }
}

加入这段代码后,我们再看页面,这次页面渲染成功了,点击按钮,url改变了,但是我们发现了问题。

  1. url改变后,页面没有跟随变化,始终是初始页面

从下面可以看到,无论路由是/A还是/B,都展示我是页面a。

在这里插入图片描述
在这里插入图片描述

  1. url改变的时候,页面刷新了

刚刚我们是使用a标签实现的router-link,点击a标签触发了默认行为跳转,接下来我们给这个a标签添加点击事件,在这个点击事件中做三件事。

  1. pushState触发跳转,pushState函数在history模式的使用和问题一章有提到哦,这里就不做重复说明了。
  2. 为记载当前路由的变量data.current赋值,由于这个变量是通过Vue.observable定义的响应式变量,被赋值后会触发依赖该变量的Vue.component函数的重渲染。
  3. 阻止a标签的默认行为。
    Vue.component("router-link", {
      props: {
        to: String,
      },
      methods: {
        clickHander(e) {
          history.pushState({}, "", this.to);
          this.$router.data.current = '/' + this.to;
          e.preventDefault();
        },
      },
      render(h) {
        return h(
          "a",
          {
            attrs: {
              href: this.to,
            },
            on: { // 这里添加了一个点击事件哦
              click: this.clickHander,
            },
          },
          [this.$slots.default]
        );
      }
    });

现在我们的router-link已经可以正常使用啦。

在这里插入图片描述
在这里插入图片描述

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:01:20  更:2022-06-25 18:03:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:20:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码