手把手教你构建一个前端路由

涉及知识点:location对象、history对象

基础概念

什么是路由

路由是一组映射关系,本质上来说是location.href和UI的映射关系

如何实现前端路由

修改location.href,而页面不会主动刷新,需要用js控制对应展示UI

涉及问题

  • 如何修改location.href
  • 何时改变UI
  • 如何浏览器前进/后退🔙事件

前端路由实现方式

在这里插入图片描述
通过hash和history的方式切换路由时都不会引起页面的刷新

1. hash方式

hash通过a标签默认的行为来改变location.hash
通过hashchange事件来监听location.hash改变,可以指定相应呈现内容
此外,还可以跳转到对应id的元素处

  <body>  
    <a href="#hash1">hash2</a>
    <a href="#hash2">hash3</a>
    <a href="#green">green</a>

    <div id="content">当前路由的内容为:</div>
    <div style="height: 1000px; background: red;"></div>
    <div id="green" style="height: 1000px; background: green;"></div>

    <script>
      const onHashChange = () => {
        const contentEl = document.getElementById('content')
        contentEl.innerHTML = window.location.hash
      }

      window.addEventListener('hashchange', onHashChange)
    </script>
  </body>

2. history方式

单页面应用,变换loctaion的hash,但是页面不会自动刷新,需要js指定要改变的UI
原理:调用 history对象的replaceState或者pushState方法来改变路由,手动改变对应UI样式

 <body>
    <button>nav1</button>
    <button>nav2</button>
    <button>nav3</button>

    <div id="content">当前路由的内容为:</div>

    <script>
      const onPopState = () => {
        const contentEl = document.getElementById('content')
        contentEl.innerHTML = window.location.pathname
      }

      // button 只会改变location.path,不会出发popstate实践,监听不到
      const buttonEls = document.getElementsByTagName('button')

      for (let btnEl of buttonEls) {
        // 1 改变location中路径
        btnEl.onclick = () => {
          window.history.pushState(
            { nav: btnEl.innerHTML },
            'title',
            '/' + btnEl.innerHTML,
          )

          // 2 手动改变UI
          onPopState()
        }
      }

      // 点击前进返回按钮时,会调用popstate.改变UI
      window.addEventListener('popstate', onPopState)
    </script>
  </body>

3. debug:本地起服务报错

在这里插入图片描述
原因:history路由中的popstate不支持file协议
解决步骤参考:https://blog.csdn.net/am123999/article/details/120582419

扩展:封装路由类Router

如何使用:class Router
注册:register(navName, fn)
执行:emit(navName)
初始化:init() 最初路由对应的UI

hash

  <body>
    <a href="#hash1">hash2</a>
    <a href="#hash2">hash3</a>
    <a href="#green">green</a>

    <div id="content">当前路由的内容为:</div>
    <div style="height: 1000px; background: red;"></div>
    <div id="green" style="height: 1000px; background: green;"></div>

    <script>
      class Router {
        routerList
        constructor() {
          this.routerList = {}

          window.onhashchange = () => {
            this.routerList[this.getHashName()]()
          }
        }

        getHashName() {
          return location.hash
        }

        register(navName, fn) {
          this.routerList[navName] = fn
        }

        emit(navName) {
          typeof this.routerList[navName] === 'function' &&
            this.routerList[navName]()
        }

        init() {
          this.emit('/')
        }
      }

      const router = new Router()

      router.register('#hash1', () => {
        console.log('当前hash为#hash1')
      })

      router.register('#hash2', () => {
        console.log('当前hash为#hash2')
      })

      router.register('#green', () => {
        console.log('当前hash为green')
      })

    </script>
  </body>

history

  <body>
    <a href="/">主页</a>
    <a href="nav1">nav1</a>
    <a href="nav2">nav2</a>
    <a href="nav3">nav3</a>

    <script>
      class Router {
        routerList
        constructor() {
          this.routerList = {}

          window.onpopstate = () => {
            this.routerList[this.getPathName()]()
          }
        }

        register(navName, fn) {
          this.routerList[navName] = fn
        }

        getPathName() {
          return location.pathname
        }

        emit(navName) {
          history.pushState({ nav: navName }, null, navName)
          typeof this.routerList[navName] === 'function' &&
            this.routerList[navName]()
        }

        init() {
          this.emit('/')
        }
      }

      // 使用
      const router = new Router()
      // 注册函数和路由

      router.register('/', () => {
        console.log('切换到主页')
      })

      router.register('nav1', () => {
        console.log('切换到nav1')
      })

      router.register('nav2', () => {
        console.log('切换到nav2')
      })

      router.register('nav3', () => {
        console.log('切换到nav3')
      })

      router.init()

      const aEls = document.getElementsByTagName('a')

      for (const aEl of aEls) {
        aEl.onclick = (e) => {
          e.preventDefault()
          router.emit(e.target.getAttribute('href'))
        }
      }
    </script>
  </body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值