ziwei3749 / blog

已停止更新..转移至 https://segmentfault.com/u/ziwei3749

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

几天vue-route的学习成果

ziwei3749 opened this issue · comments

vue-router学习成果

经过几天的研究,对以下问题做了一些探究:

  • 1.前端路由是什么?
  • 2.思考实现一个前端路由需要具备哪些功能
  • 3.前端路由2种实现方式的区别和注意事项
  • 4.【代码实现】基础路由实现代码
  • 5.【vue-router源码学习】vue-router源码中学习到的技巧

1.前端路由是什么?

本质上前端路由: 前端监听和解析url的变化,并匹配url对应的页面

2.思考实现一个前端路由需要具备哪些功能

我觉得最起码需要有2个功能

  • 一是打开的动作。url是#/login,那你得用JS控制显示login页面
  • 二是要有历史记录操作单。就是你url变化后,要在历史栈当中有一个路由对象的记录

打开动作

如果是模拟的话,div的显示隐藏就可以了。

实际上vue-router用起来,有router-view组件,这里渲染的内容,可以通过routes配置

routes是一个数组,里面写着path和components的映射关系

历史记录操作单

那hash变化自动就会有一个历史记录
html5新增加的history.pushState也增加历史记录。

所以这2个api都具备实现前端路由的能力

3.前端路由2种实现方式的区别和注意事项

hash 和 history的区别

  • 兼容性上,hash兼容性更好,因为history是html5的api。默认是hash
  • 是否美观上
  • 是否需要后端配置。
history模式的话需要后端做一个配置,如果这个url匹配到任何路由,那不要返回404,而是返回Index.html

但是这样,你如果url不匹配,也永远没有404了,vue为例子的话,routes里需要配置path:* 对应一个404页面

4.【代码实现】基础路由实现代码,以hash

html

<ul>
    <a href="#/"></a>
    <a href="#/about"></a>
    <a href="#/topics"></a>
</ul>

<div id="router-view"></div>

调用方式

var router = new Router()
var routerView = document.querySelector('#router-view')
router.init()


 // 这里是注册路由,指定url对应做什么事情。有点routes里指定path和component的对应关系
router.route('/',() => {           
    routerView.innerHTML = 'home'
})
router.route('/about',() => {
    routerView.innerHTML = 'about'
})
router.route('/topics',() => {
    routerView.innerHTML = 'topics'
})

实现hash路由

class Router{
    construtor() {
        this.routes = {}     // 存放path和 fn的映射关系
    }

    route(path,fn){
        this.routes[path] = fn
    }

    updateView(){
        // 获取hash就知道执行哪个fn,显示哪个页面了
        var url = window.location.hash.substr(1)
        this.routes[url]()
    }

    init(){
        window.addEventListenr('hashchange',() => {
            this.updateView()
        })
        window.addEventListenr('load',() => {
            this.updateView()
        })
    }

}

梳理一下过程

  • 点击a标签,hash变化,自动存一个历史记录再历史记录单里
  • 同时hash的变化,会被onhashchange监听到,在这个回调里,我们知道hash就能得到对应的页面和参数

那为什么不用history实现呢,因为History要多考虑一些地方,pushState并不能触发popstate的变化

既然通过popstate监听不太靠谱,那如何拦截各种情况下路由的变化的

其实路由的变化,就3个情况

  • 浏览器前进后退
  • a标签点击
  • 或者pushState
  • 至于直接修改url的情况,就是所谓需要后端配置,Url错误时不要返回404,而是返回index.html

在点击a标签或者pushState时去updateView一下,在源码中其实有一个render,需要用这个方法去更新页面

基本就是这一个思路,(注意history跳转时跨域问题)

5.【vue-router源码学习】vue-router源码中学习到的技巧

我是看的v2.03版本,后来有看了一眼vue-router@3.01的版本,目录结构和写法也不一样

目录结构

  • components (rotuer-link / router-view)
  • history (hash.js / html5.js / abstract.js / base.js)
  • utils
  • index.js
  • install.js
  • create-matcher.js
  • create-route-map.js

技巧1.mian.js入口文件,我们一般引入vue-router后,需要Vue.use(VueRouter)

这个是Vue提供的插件机制,这个机制就是Vue会调用插件的install方法(如果没有的话就把插件自身作为函数调用)

技巧2.插件打包时肯定不希望把整个Vue打包进去,但是又希望使用Vue对象的方法,所以就可以在install时赋值

去看index.js的话,vue-rotuer这个插件是有install方法,单独写install.js文件里

_Vue = Vue

技巧3: Object.defineProperty给Vue.prototype添加$router和$route,这样可以让所有Vue都有这2个属性

技巧4: 有一些嘈杂哦判断你的 mode和fallback

  • 默认是hash模式
  • 如果你的浏览器不支持history,并且你还设置用history,强制修改为hash模式
  • 用switch case的方法,根据mode来判断哪个构造函数来实例化this.history
  • HTML5History 、 HashHistory 都是继承 History的
let mode = options.mode || 'hash'

this.fallback = mode === 'history' && !supportsHistory
    if (this.fallback) {
      mode = 'hash'
    }

switch (mode) {
    case 'history':
    this.history = new HTML5History(this, options.base)
    break
    case 'hash':
    this.history = new HashHistory(this, options.base, this.fallback)
    break
    case 'abstract':
    this.history = new AbstractHistory(this)
    break
    default:
    assert(false, `invalid mode: ${mode}`)
}

写的不错,学习一下