前端路由history简单实现

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta
            http-equiv="X-UA-Compatible"
            content="IE=edge" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>

    <body>
        <button id="btn1">home</button>
        <button id="btn2">mine</button>
        <div id="app"></div>
        <script>
            const home = document.querySelector('#btn1')
            const mine = document.querySelector('#btn2')
            /* 
                pushState 和 replaceState 会改变当前页面显示的url,但都不会刷新页面
                popState 无法监听 pushState 和 replaceState
                因此重写这两个方法 在这两个方法中添加自定义事件并派发
                history模式需要服务器配置访问路径不存在时,指向index.html, 因为pushState和replaceState不刷新页面只改变url,若刷新页面则会导致浏览器向服务器请求url路径的资源
            */
            class VueRouter {
                constructor(router) {
                    this.router = router || []
                    this.curRoute = null
                    history.pushState = this.reWriteFn('pushState')
                    history.replaceState = this.reWriteFn('replaceState')
                    // 绑定this 不然this为window
                    this.getQuery = this.getQuery.bind(this)
                    this.popState = this.popState.bind(this)
                    window.addEventListener('pushState', this.getQuery)
                    // 在同一文档的两个历史记录条目之间导航会触发该事件 如histroy.back() 或点击前进后退按钮
                    window.addEventListener('popstate',this.popState)
                }
                reWriteFn(type) {
                    let fn = window.history[type]
                    // 函数柯里化
                    return function () {
                        /*
                            apply: xxx.apply(this,[xxx,xxxx])
                            bind: xxx.bind(this,xxx,xxxx)
                            call: xxx.call(this,xxx,xxxx)
                            apply 和 call 区别为, apply 传入数组作为参数, call 可以直接传参
                            bind 也可以直接传参, 但是bind是返回一个绑定this过后的函数, 需要自己去调用
                        */
                        // 此处 this 为 history
                        console.log(this,'this')
                        let rw = fn.apply(this, arguments)
                        let e = new Event(type)
                        e.arguments = arguments
                        // 派发事件使得我们能够监听到pushState和replaceState
                        window.dispatchEvent(e)
                        return rw
                    }
                }
                getQuery(e) {
                    const { arguments: args } = e
                    let url = args[2].slice(1)
                    this.showComponent(url)
                }
                showComponent(url){
                    this.curRoute = this.router.find(route=>{
                        if(route.path == url){
                            return route
                        }
                    })
                    if(!this.curRoute) this.curRoute = this.router[0]
                    document.querySelector('#app').innerHTML = this.curRoute.component
                }
                push(state,title,url){
                    if(!url){
                        throw new Error('路由跳转不能缺少参数')
                    }
                    history.pushState(state, title, url)
                }
                popState(e){
                    /* 
                        window.location.host = http://127.0.0.1:5500/mine
                        window.location.port = 5500
                        window.location.hostname = http://127.0.0.1
                        window.location.pathname = /mine
                        window.location.searchURL = ?xxx= xxx(查询参数)
                        window.location.hash = #xxx(hash值)
                    */
                    // 获取URL的路径部分
                    const url = window.location.pathname || ''
                    this.showComponent(url)
                }
            }
            home.addEventListener('click', () => {
                Router.push({ state: 1 }, null, './home')
            })
            mine.addEventListener('click', () => {
                Router.push({ state: 2 }, null, './mine')
            })
            const Router = new VueRouter([
                {
                    path: '/home',
                    name: 'home',
                    component: '<div>主页</div>'
                },
                {
                    path: '/mine',
                    name: 'home',
                    component: '<div>我的</div>'
                }
            ])
        </script>
    </body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值