Vue Router深扒实现原理

目录
  • 回顾Vue Router的核心代码
  • 代码实现
    • 创建Vue-Router插件
    • 构造函数
  • 完整代码

Vue Router官网

前置知识:插件、slot插槽、mixins混入、render函数、运行时和完整版的Vue

回顾Vue Router的核心代码

// 注册插件
// Vue.use() 内部调用传入对象的 install 方法
Vue.use(VueRouter)
// 创建路由对象
const router = new VueRouter({
  routes: [
    { name: 'home', path: '/', component: homeComponent }
  ]
})
// 创建 Vue 实例,注册 router 对象
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

注意,上述版本为Vue Router 3.x,用法与最新版的有所不同,但理解了其核心设计思路后,再去手写Vue4也是得心应手。

Vue功能的强大就在于它的插件机制,use可以接受两种参数——函数和对象。如果是函数的话,则会直接调用这个函数,如果是对象的话,则会调用对象的install方法。

代码实现

创建Vue-Router插件

let _Vue = null
export default class VueRouter {
  static install(Vue) {
    // 1. 判断插件是否已经安装,如果是的话,直接返回
    if (Vue.install.installed) return
    Vue.install.installed = true
    // 2.将Vue的构造函数记录在全局
    _Vue = Vue
    // 3.把创建Vue的实例传入的router对象注入到Vue实例
    _Vue.mixin({
      beforeCreate() {
        // 判断是否是实例,如果是实例就不用添加
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
        }
      },
    })
  }
}

构造函数

constructor(options) {
  this.options = options
  this.routeMap = {}
  this.data = _Vue.observable({
    current: '/'
  })
}

将options中的路由规则存放到routeMap中

// 注意,下面的代码只考虑了path和component对应,name同理,也可加上路由元信息,或者遍历加上children,但为便捷及个人水平考虑,并未完整实现
createRouteMap() {
  // 遍历所有的路由规则,把路由股则解析成键值对的形式,存储到routeMap中
  this.options.routes.forEach(route => {
    this.routeMap[route.path] = route.component
  })
}

初始化route-link和router-view两个组件

initComponent(Vue) {
  Vue.component('router-link', {
    props: {
      to: String
    },
    // template: `<a :href="to" rel="external nofollow"  rel="external nofollow" ><slot></slot></a>`
    render(h) {
      return h("a", {
        attrs: {
          href: this.to
        },
        on: {
          click: this.clickhandler
        }
      }, [this.$slots.default])
    },
    methods: {
      clickhandler(e) {
        history.pushState({}, "", this.to)
        this.$router.data.current = this.to
        e.preventDefault();
      }
    },
  })
  const self = this
  Vue.component('router-view', {
    render(h) {
      const component = self.routeMap[self.data.current]
      return h(component)
    }
  })
}

浏览器回退、前进时页面同时改变

initEvent(){
    window.addEventListener("popstate",()=>{
        this.data.current = window.location.pathname
    })
}

完整代码

let _Vue = null
export default class VueRouter {
  constructor(options) {
    this.options = options
    this.routeMap = {}
    this.data = _Vue.observable({
      current: '/'
    })
    this.init()
  }
  static install(Vue) {
    // 1. 判断插件是否已经安装,如果是的话,直接返回
    if (Vue.install.installed) return
    Vue.install.installed = true
    // 2.将Vue的构造函数记录在全局
    _Vue = Vue
    // 3.把创建Vue的实例传入的router对象注入到Vue实例
    _Vue.mixin({
      beforeCreate() {
        // 判断是否是实例,如果是实例就不用添加
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
        }
      },
    })
  }
  init() {
    this.createRouteMap()
    this.initComponent(_Vue)
    this.initEvent()
  }
  createRouteMap() {
    // 遍历所有的路由规则,把路由股则解析成键值对的形式,存储到routeMap中
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component
    })
  }
  initComponent(Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      // template: `<a :href="to" rel="external nofollow"  rel="external nofollow" ><slot></slot></a>`
      render(h) {
        return h("a", {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickhandler
          }
        }, [this.$slots.default])
      },
      methods: {
        clickhandler(e) {
          history.pushState({}, "", this.to)
          this.$router.data.current = this.to
          e.preventDefault();
        }
      },
    })
    const self = this
    Vue.component('router-view', {
      render(h) {
        const component = self.routeMap[self.data.current]
        return h(component)
      }
    })
  }
  initEvent() {
    window.addEventListener('popstate', () => {
      this.data.current = window.location.pathname
    })
  }
}

到此这篇关于Vue Router深扒实现原理的文章就介绍到这了,更多相关Vue Router内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用vue-router切换组件时使组件不销毁问题

    目录 vue-router切换组件时使组件不销毁 1.用keep-alive包裹router-view 2.定义路由meta字段 3.keep-alive的原理 vue-router路由切换 组件重用挖下的坑 问题描述 翻车现场再现 车祸原因分析 前往救援 一次真的救援 vue-router切换组件时使组件不销毁 默认地,使用vue-router切换组件时,离开的组件会被销毁,新进入地组件会创建. 那么,有时候,我们希望离开的组件不要被销毁.这时就需要使用到keep-alive标签来缓存组件.

  • Vue Router嵌套路由(children)的用法小结

    目录 简介 使用场景 官网网址 示例 路由配置 用户页面(父页面) 子路由的path前加“/” 简介 说明 本文介绍Vue Router的嵌套路由的用法. 嵌套路由就是父路由里面嵌套他的子路由,父路由有自己的路由导航和路由容器(router-link.router-view),通过配置children可实现多层嵌套.(router-view必须要有,否则点击了router-link后,路径会变化,但页面不改变). 使用场景 嵌套路由用于实现页中页效果.例如: 用户页面中,有登录页面和注册页面,这

  • Vue Router应用方法详解

    目录 服务端路由 路由的应用场景 路由 监听浏览器 hashchange 事件实现路由 使用Vue Router+Vue2实现路由 服务端路由 服务端路由时指的是服务器根据用户访问的 URL 路径返回不同的响应结果. 在传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML页面,然后重新加载整个页面. 然而,在单页面应用中,客户端的 JavaScript 可以拦截页面的跳转请求,动态获取新的数据,无需重新加载的情况下更新当前页面. 这样通常可以带来更顺滑的用户体验

  • vue-router中关于children的使用方法

    目录 关于children的使用 children的使用场景 router配置中children配置不起作用 关于children的使用 children的使用场景 比如页面左侧显示菜单,右侧显示不同菜单下的内容,类似如下element网站,那么右侧部分的内容就是当前页面的children 存在如下场景,点击导航一跳转至页面1,导航二跳转页面2,且页面1中存在子页面 路由js如下: const routes = [{ path: '/', name: 'Home', component: Hom

  • Vue cli及Vue router实例详解

    目录 前言: 1. 安装 1.1 npm安装 1.2 vue-cli安装 2. 初始化项目 2.1vue init命令讲解 2.2 项目初始化 3. 运行项目 4. 成功页面 5. 项目结构 5.1 总体框架 5.2 配置目录文件详解 5.3src项目核心文件讲解 6. VUE-ROUTER 6.1 快速入门 6.2 页面跳转 6.3子路由-路由嵌套 6.4路由传递参数 6.5命名路由-命名视图-重定向-别名 前言: 官方文档 vue-cli是vue官方出品的快速构建单页应用的脚手架,里面集成了

  • 关于VueRouter导入的全过程

    目录 router nanoid的使用 引入 使用 路由 1-1 安装依赖 1-2 引入 1-3 在main.js中使用 1-4 App.vue 全局过滤器 element-ui 全局组件 Vuc-cli中的视配 1-1 安装依赖 1-2 配置文件 1-3 main.js 1-4 public/index.html 1-5 在pc端视配 slot封装动画 项目初始化 1-1 .router-link-active 1-2 动态显示tabbar 1-3 跳转回前一个页面 1-4轮播 vant ui

  • Vue Router解决多路由复用同一组件页面不刷新问题(场景分析)

    目录 简介 问题复现 代码 测试 解决方案 方案1:导航守卫 方案2:watch监听$route 方案3:父组件router-view指定key 其他网址 简介 说明 本文介绍如何解决Vue的多路由复用同一组件页面不刷新问题. 多路由复用同一组件的场景 多路由使用同一组件 比如:添加博客(path为:/addBlog)和编辑博客(path为:/editBlog)都对应同一个组件(EditBlog.vue) 动态路由 比如:用户详情页采用动态路由,其path为:/user/:id,组件都是User

  • Vue Router深扒实现原理

    目录 回顾Vue Router的核心代码 代码实现 创建Vue-Router插件 构造函数 完整代码 Vue Router官网 前置知识:插件.slot插槽.mixins混入.render函数.运行时和完整版的Vue 回顾Vue Router的核心代码 // 注册插件 // Vue.use() 内部调用传入对象的 install 方法 Vue.use(VueRouter) // 创建路由对象 const router = new VueRouter({ routes: [ { name: 'ho

  • Vue Router history模式的配置方法及其原理

    vue-router分为 hash和 history模式,前者为其默认模式,url的表现形式为 http://yoursite.com#home,比较难看.后者的url表现形式为 http://yoursite.com/home,比较美观. 但如果要使用 history模式,我们需要在后端进行额外配置.本文将讨论如何配置以及为什么要这样配置. history模式的配置方法 我们来看看官方文档是教我们怎么配置的:HTML5 History 模式. 首先要将 mode设置为 history: con

  • vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法

    解决vue-router嵌套路由(子路由)在history模式下刷新无法渲染页面的问题,具体内容如下 一. 异常描述 本来使用的是vue-router的hash模式,但是hash模式下url需要带"#"符号,不仅看起来不舒服,而且有些场景下是会破坏路由中的"#"(微信分享页面就会把"#"后边的内容处理掉),所以就需要使用history模式,然后就让后端改下nginx配置: location / { try_files $uri $uri/ /in

  • Vue实现双向绑定的原理以及响应式数据的方法

    一.vue中的响应式属性 Vue中的数据实现响应式绑定 1.对象实现响应式: 是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集依赖项,当数据发生变化时在set中通过调用dep.notify进行发布通知,实现视图的更新. 2.数组实现响应式: 对于数组则是通过继承重写数组的方法splice.pop.push.shift.unshift.sort.reverse.等可以修改原数组的方式实现响应式的,但是通过length以及直接

  • vue router 源码概览案例分析

    源码这个东西对于实际的工作其实没有立竿见影的效果,不会像那些针对性极强的文章一样看了之后就立马可以运用到实际项目中,产生什么样的效果,源码的作用是一个潜移默化的过程,它的理念.设计模式.代码结构等看了之后可能不会立即知识变现(或者说变现很少),而是在日后的工作过程中悄无声息地发挥出来,你甚至都感觉不到这个过程 另外,优秀的源码案例,例如 vue . react 这种,内容量比较庞大,根本不是三篇五篇十篇八篇文章就能说完的,而且写起来也很难写得清楚,也挺浪费时间的,而如果只是分析其中一个点,例如

  • Vue Router的手写实现方法实现

    为什么需要前端路由 在前后端分离的现在,大部分应用的展示方式都变成了 SPA(单页面应用 Single Page Application)的模式.为什么会选择 SPA 呢?原因在于: 用户的所有操作都在同一个页面下进行,不进行页面的跳转.用户体验好. 对比多页面,单页面不需要多次向服务器请求加载页面(只请求一次.html文件),只需要向服务器请求数据(多亏了 ajax).因此,浏览器不需要渲染整个页面.用户体验好. 归根结底,还是因为 SPA 能够提供更好的用户体验. 为了更好地实现 SPA,前

  • Vue Router 实现动态路由和常见问题及解决方法

    个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表.常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单. 如何利用Vue Router 实现动态路由 Vue项目实现动态路由的方式大体可分为两种: 前端将全部路由规定好,登录时根据用户角色权限来动态展示路由: 路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染: 第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开.第二种方式现

  • Vue Router中应用中间件的方法

    中间件是我们在软件开发中的一个古老而强大的概念,当我们在应用程序中使用路由相关模式时,它非常有用. 如果您不太了解中间件的含义,Nodejs框架Express里的中间件可以帮助您了解它们的工作原理. 但是,中间件仅适用于后端吗? 不,当应用程序中有路由时,中间件在前端或后端中就会非常常见.比如现在流行的单页应用程序. 有一些示例可以说明,何时可以使用中间件: 不允许未登录用户访问您的网页. 仅允许某些类型的用户查看页面(角色:管理员,作者等) 数据采集. 重置设置或清理存储空间. 限制访问用户的

  • 如何手写简易的 Vue Router

    前言 还是那样,懂得如何使用一个常用库,还得了解其原理或者怎么模拟实现,今天实现一下 vue-router . 有一些知识我这篇文章提到了,这里就不详细一步步写,请看我 手写一个简易的 Vuex 基本骨架 Vue 里面使用插件的方式是 Vue.use(plugin) ,这里贴出它的用法: 安装 Vue.js 插件.如果插件是一个对象,必须提供 install 方法.如果插件是一个函数,它会被作为 install 方法.install 方法调用时,会将 Vue 作为参数传入.这个方法的第一个参数是

  • 详解实现vue的数据响应式原理

    这篇文章主要是给不了解或者没接触过 vue 响应式源码的小伙伴们看的,其主要目的在于能对 vue 的响应式原理有个基本的认识和了解,如果在面试中被问到此类问题,能够知道面试官想让你回答的是什么?「PS:文中如有不对的地方,欢迎小伙伴们指正」 响应式的理解 响应式顾名思义就是数据变化,会引起视图的更新.这篇文章主要分析 vue2.0 中对象和数组响应式原理的实现,依赖收集和视图更新我们留在下一篇文章分析. 在 vue 中,我们所说的响应式数据,一般指的是数组类型和对象类型的数据.vue 内部通过

随机推荐