Vue Router原理

221 阅读2分钟

说到路由,我们都知道,路由切换无外乎使用hashChange或者是history.pushState, 让我们重新梳理一下这两种模式

Hash模式

  • Hash模式基于锚点, 以及onhashchange事件
  • hashChange的方式拥有良好的浏览器兼容性, 但是url中多了丑陋的/#/部分

History模式

History模式是基于HTML5中的Hitstory API, history.pushState方法能提供优雅的url,但需要额外的服务端配置解决任意路径刷新问题

  • history.pushState() // IE10以后才支持
  • history.replaceState()
  • history.popState()

两种模式比较

在Vue Router或者React Router中都提供了两种解决方案,供你自己根据业务需求挑选 个人感觉两种都用的比较多,但有一种情况,用history模式会比较好 路由定义如下图:

const route = {
    path: '/customer-detail/:customerId',
    name: '客户详情',
    component: './Customer/CustomerGroupDetail',// 处理公共部分逻辑之类
    routes: [
      {
        path: '/customer-detail/:customerId/base-info',
        name: '基本信息',
        component: './Customer/CustomerBaseInfo',// 基本信息页
      },
      {
        path: '/customer-detail/:customerId/relation-info',
        name: '关联人信息',
        component: './Customer/RelationInfo',// 关联人信息页
      },
    ]
}

History模式在服务端配置

1.nginx配置, 打开nginx.conf文件,修改server下的location属性

server {
  location / {
    try_files $uri $uri/ /index.html;
 }
}

try_files: 试着去找一下这个文件 &uri: 当前请求的路径,若找到,直接返回该文件,若找不到,返回单页应用的首页(index.html)

2.nodejs配置, 如下图

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')
})

Vue Router--History模式设计思路

  • 通过history.pushState()改变地址栏
  • 监听popState()事件
  • 根据当前路由地址找到对应组件重新渲染

贴一部分,用History模式实现的简易版VueRouter

let _Vue = null
class VueRouter {
    static install(Vue){
        //1 判断当前插件是否被安装
        if(VueRouter.install.installed){
            return;
        }
        VueRouter.install.installed = true
        //2 把Vue的构造函数记录在全局
        _Vue = Vue
        //3 把创建Vue的实例传入的router对象注入到Vue实例
        // _Vue.prototype.$router = this.$options.router
        _Vue.mixin({
            beforeCreate(){
                if(this.$options.router){
                    _Vue.prototype.$router = this.$options.router
                    
                }
               
            }
        })
    }
    constructor(options){
        this.options = options
        this.routeMap = {}
        // observable
        this.data = _Vue.observable({
            current:"/"
        })
        this.init()

    }
    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
            },
            render(h){
                return h("a",{
                    attrs:{
                        href:this.to
                    },
                    on:{
                        click:this.clickhander
                    }
                },[this.$slots.default])
            },
            methods:{
                clickhander(e){
                // pushState接收三个参数, state(将来触发popState时传给popState事件的事件对象的一个参数),title,url
                    history.pushState({},"",this.to)
                    this.$router.data.current=this.to
                    e.preventDefault()
                }
            }
            // template:"<a :href='to'><slot></slot><>"
        })
        const self = this
        Vue.component("router-view",{
            render(h){
                // self.data.current
                const cm=self.routeMap[self.data.current]
                return h(cm)
            }
        })
        
    }
    initEvent(){
        //
        window.addEventListener("popstate",()=>{
            this.data.current = window.location.pathname
        })
    }
}

export default VueRouter

Vue Router--Hash模式设计思路

  • URL中 # 后面地址作为路径地址
  • 监听hashChange事件
  • 根据当前路由地址找到对应组件重新渲染