vue-router是vue我的项目的重要组成部分,用于构建单页利用。单页利用是基于路由和组件的,路由用于设定拜访门路,并将门路和组件映射起来。路由的实质就是建设url和页面之间的映射关系。

hash模式

hash模式是vue-router的默认模式。hash指的是url描点,当描点发生变化的时候,浏览器只会批改拜访历史记录,不会拜访服务器从新获取页面。因而能够监听描点值的变动,依据描点值渲染指定dom。

实现原理

  • 扭转描点

能够通过location.hash = "/hashpath"的形式批改浏览器的hash值。

  • 监听描点变动

能够通过监听hashchange事件监听hash值的变动。

window.addEventListener('hashchange', () => {   const hash = window.location.hash.substr(1)   // 依据hash值渲染不同的dom})

history模式

hash模式下,url可能为以下模式:

http://localhost:8080/index.html#/book?bookid=1

下面的url中既有#又有?,会让url看上去很奇怪,因而,能够应用history模式,在此模式下,url会如上面所示:

http://localhost:8080/book/1

实现原理

  • 扭转url

H5的history对象提供了pushState和replaceState两个办法,当调用这两个办法的时候,url会发生变化,浏览器拜访历史也会发生变化,然而浏览器不会向后盾发送申请。

// 第一个参数:data对象,在监听变动的事件中可能获取到// 第二个参数:title题目// 第三个参数:跳转地址history.pushState({}, "", '/a')
  • 监听url变动

能够通过监听popstate事件监听history变动,也就是点击浏览器的后退或者后退性能时触发。

window.addEventListener("popstate", () => {    const path = window.location.pathname    // 依据path不同可渲染不同的dom})

服务端反对

当应用hash模式的时候,如果手动刷新浏览器,页面也可能失常显示。然而在history模式下,刷新浏览器就会呈现问题。

如拜访http://localhost:8080/book/1时,服务端会查找是否有相应的html可能匹配此门路,在单页利用下,服务端只有一个index.html,所以此时匹配不到,会提醒404。针对这个问题,须要服务端进行history模式反对。

node服务

在nodejs服务中,能够引入connect-history-api-fallback插件:

const path = require('path')// 导入解决 history 模式的模块const history = require('connect-history-api-fallback')// 导入 expressconst express = require('express')const app = express()// 注册解决 history 模式的中间件app.use(history())// 解决动态资源的中间件,网站根目录 ../webapp.use(express.static(path.join(__dirname, '../web')))// 开启服务器,端口是 3000app.listen(3000, () => {  console.log('服务器开启,端口:3000')})

nginx服务

在nginx服务中,能够如下形式批改配置文件,增加history模式反对:

location / {    root html;    index index.html index.htm;    #新增加内容    #尝试读取$uri(以后申请的门路),如果读取不到读取$uri/这个文     件夹下的首页    #如果都获取不到返回根目录中的 index.html    try_files $uri $uri/ /index.html;}

实现自定义VueRouter

VueRouter外围是,通过Vue.use注册插件,在插件的install办法中获取用户配置的router对象。当浏览器地址发生变化的时候,依据router对象匹配相应路由,获取组件,并将组件渲染到视图上。

次要有三个重要点:

  • 如何在install办法中获取vue实例上的router属性。

能够利用Vue.mixin混入申明周期函数beforeCreate,在beforeCreate函数中能够获取到Vue实例上的属性并赋值到Vue原型链上。

_Vue.mixin({   beforeCreate () {      if (this.$options.router) {        _Vue.prototype.$router = this.$options.router      }   }})
  • 如何触发更新

hash模式下:

  1. 通过location.hash批改hash值,触发更新。
  2. 通过监听hashchange事件监听浏览器后退或者后退,触发更新。

history模式下:

  1. 通过history.pushState批改浏览器地址,触发更新。
  2. 通过监听popstate事件监听浏览器后退或者后退,触发更新。
  • 如何渲染router-view组件
  1. 通过Vue.observable在router实例上创立一个保留以后路由的监控对象current。
  2. 当浏览器地址变动的时候,批改监控对象current。
  3. 在router-view组件中监听监控对象current的变动,当current变动后,获取用户注册的相应component,并利用h函数将component渲染成vnodes,进而更新页面视图。

完整版

// 存储全局应用的Vue对象let _Vue = nullclass VueRouter {  // vue.use要求plugin具备一个install办法  static install (Vue) {    // 判断插件是否曾经装置过    if (VueRouter.install.installed) {      return    }    VueRouter.install.installed = true    _Vue = Vue    // 将main文件中实例化Vue对象时传入的router对象增加到Vue的原型链上。    _Vue.mixin({      beforeCreate () {        if (this.$options.router) {          _Vue.prototype.$router = this.$options.router        }      }    })  }  constructor (options) {    this.options = options    // 用于疾速查找route    this.routeMap = {}    this.data = _Vue.observable({      current: window.location.hash.substr(1)    })    this.init()  }  init () {    this.createRouteMap()    this.initComponents(_Vue)    this.initEvent()  }  createRouteMap () {    // 遍历所有的路由规定 吧路由规定解析成键值对的模式存储到routeMap中    this.options.routes.forEach(route => {      this.routeMap[route.path] = route.component    })  }  initComponents (Vue) {    // 注册router-link组件    Vue.component('router-link', {      props: {        to: String      },      methods: {        clickHandler (e) {          // 批改hash          location.hash = this.to          // 批改current,触发视图更新          this.$router.data.current = this.to          e.preventDefault()        }      },      render (h) {        return h('a', {          attrs: {            href: this.to          },          on: {            click: this.clickHandler          }        }, [this.$slots.default])      }    })    const that = this    // 注册router-view插件    Vue.component('router-view', {      render (h) {        const component = that.routeMap[that.data.current]        return h(component)      }    })  }  initEvent () {    // 在hash产生更改的时候,批改current属性,触发组件更新    window.addEventListener('hashchange', () => {      this.data.current = window.location.hash.substr(1)    })  }}export default VueRouter