美文网首页前端开发那些事儿vue_router
WEB大前端进阶-Vue-router原理

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

作者: WEB前端含光 | 来源:发表于2020-08-18 14:08 被阅读0次

    时间总是不经意的从指间流逝,转眼便过去了这么久,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大厂面试真题 点赞、评论、转发 即可免费获取,希望能够对你们有所帮助。



    相关文章

      网友评论

        本文标题:WEB大前端进阶-Vue-router原理

        本文链接:https://www.haomeiwen.com/subject/vzahjktx.html