WEB大前端进阶-Vue-router原理

时间总是不经意的从指间流逝,转眼便过去了这么久,2020已然过去了一半,时间匆匆,只有学习才能贯彻始终的陪伴着大家,老话说得好,学到老活到老,这句话是很有道理的。

多的就不说了,接下来进入到今天的正题。

今天来讲一些什么呢?今天就来讲一下前端进阶-Vue-router 的原理,如果讲得不好,大家见谅一下。

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')
// 导入 express
const express = require('express')

const app = express()
// 注册处理 history 模式的中间件
app.use(history())
// 处理静态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))

// 开启服务器,端口是 3000
app.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 = null
class 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

如果你现在也想学习前端开发技术,在学习前端的过程当中有遇见任何关于学习方法,学习路线,学习效率等方面的问题,你都可以加入到我的Q群中:前114中6649后671,里面有许多前端学习资料以及2020大厂面试真题 点赞、评论、转发 即可免费获取,希望能够对你们有所帮助。



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352