继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

手摸手教你自己动手实现一个前端路由

Noahlam
关注TA
已关注
手记 23
粉丝 1.4万
获赞 1130

前言

用过现代前端框架的同学,对前端路由一定不陌生, vue, react, angular 都有自己的 router, 那么你对 router 的工作原理了解吗?
如果还不了解, 那么请跟我一起来手写一个简单的前端路由, 顺便了解一下.

实现路由的2种方式

  1. hash模式
  2. history模式

缺点:

hash:丑(地址栏要多一个#), 某些特殊场景有坑 ,如微信支付回调

history: 兼容性较差, 直接访问会400 ,除非后端或者服务器有做处理

基本原理

hash是基于 监听 hashchange 事件实现的,history 是基于 pushState 和 popState,

实现

由于history兼容性较差,而且实现方式基本没多大区别,本文就以hash模式来实现,history的实现方式只实现不同的部分。


    Document

以上就是hash模式的最简单实现,查看控制台,可以看到不管是点击跳转按钮,还是点击浏览器的前进/后退按钮,控制台都会输出当前页面对应的 'url',有了 'url',我们就可以对内容做条件渲染了

我们在上面的代码的基础上,稍作修改一下,增加2个div,一个是login,一个是index


    Document<div class="component">
    </div>

<div class="component">
    </div>

这样,我们就能根据不同的hash地址,显示不同的内容,是不是已经有点路由的味道了呢?但是目前还无法传參,传參的方式有很多种,最常见最通俗的,应该是query string 了吧? query string 其实很简单,解析url即可。

    // 监听hash的变动
    window.addEventListener('hashchange', function (e) {
        let url = location.hash.slice(1) || "index"
        // 解析url
        let questionIndex = url.indexOf("?")
        let path, query
        if(questionIndex &gt;= 0){
            path = url.substr(0,questionIndex)
            let queryString = url.substr(questionIndex+1)
            let queryArray = queryString.split("&amp;")
            let queryObject = {}
            queryArray.map(str =&gt; {
                let equalIndex = str.indexOf("=")
                if(equalIndex &gt; 0) {
                    let key = str.substr(0,equalIndex)
                    let value = str.substr(equalIndex+1)
                    queryObject[key] = value
                }
            })
            query = queryObject
        } else {
            path = url
            query = {}
        }

        console.log('接收到的参数', query)
        setVisible(path)
    })

这样,我们就能获取到URL上传递的query string , 还顺便解决了传參会导致路由解析不正确的bug.

传參其实还有个更简单的方法,就是 设置一个全局变量 param,然后在需要传值的时候,直接对param 赋值即可

    // 设置一个全局变量
    var param = {}
    // 页面跳转
    // 这个多了个参数 args
    function hashPush(url, args) {
        location.hash = "#" + url
        param = args
    }

由于是全局变量,所以可以在任意位置使用 param,不过这样直接使用全局变量的方式,也有它的弱点,就是点击返回按钮的时候无法保存变量,而query string 因为是在 url 里面的,所以返回的时候,可以拿到上个页面的 query string,
那我们有没有办法让全局变量的方式也能保存上一个页面的参数呢? 我们来稍微修改一下代码

    // 设置一个全局变量
    var param = {}
    // 页面跳转
    // 这个多了个参数 args
    function hashPush(url, args) {
        location.hash = "#" + url
        // 如果有传args参数,就在 param 对象下建一个名字跟当前url一样的对象,并把args赋值给它
        if(args) {
            param[url] = args
        }
    }

那我们在 hashchange 的时候,可以根据url来定位该页面的参数。

    // 监听hash的变动
    window.addEventListener('hashchange', function (e) {
        // 省略其他代码
        args = param[path] || {}
        // 省略其他代码
    })

这样,我们就初步实现了一个hash路由,那么接下来,我们来看看history路由怎么实现。

history 模式

history 模式主要依靠 调用 history.pushState() 方法 和 监听 popstate 事件。

history.pushState() 方法接收3个参数:

  1. 要传递的数据(参数)
  2. 给页面设置的标题(兼容性差,几乎没用)
  3. url

我们看看调用实例

    history.pushState({id:1}, '我是页面标题', url)

需要注意的是 pushState 的时候,并不会触发 popstate 事件,只有在前进后退的时候,才会触发,所以 pushState 之后,需要手动去设置页面的相关状态。如上面的例子,我们需要这样做

    history.pushState({id:1}, '我是页面标题', url)
    setVisible(url)

然后监听 popstate 事件,捕获 前进/后退

 window.addEventListener('popstate',function (e) {
     // e.state 就是你 pushState 的时候,传的第一个参数
     let state = e.state || {}  
     let url = state.target.location.pathName
     //  根据参数 做一些其他操作
  })

最后

本文最终实现代码已经放在 github上,想要直接看效果的同学,可以上去直接copy,运行。

如果觉得本文对您有用,请给本文的github加个star,万分感谢

另外,github上还有其他一些关于前端的教程和组件,有兴趣的童鞋可以看看,你们的支持就是我最大的动力。

打开App,阅读手记
4人推荐
发表评论
随时随地看视频慕课网APP

热门评论

手摸手,感觉哪里不太对呢  哈哈哈哈哈哈

查看全部评论