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


    涉及知识点: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">hash2a>
        <a href="#hash2">hash3a>
        <a href="#green">greena>
    
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2. history方式

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

     <body>
        <button>nav1button>
        <button>nav2button>
        <button>nav3button>
    
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    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">hash2a>
        <a href="#hash2">hash3a>
        <a href="#green">greena>
    
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    history

      <body>
        <a href="/">主页a>
        <a href="nav1">nav1a>
        <a href="nav2">nav2a>
        <a href="nav3">nav3a>
    
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
  • 相关阅读:
    基于Stable Diffusion的图像合成数据集
    【精华】ubuntu编译openpose
    代码随想录训练营第五十七天|647. 回文子串、516.最长回文子序列
    深入理解 Srping IOC
    网关概念及java项目中用使用网关场景
    TrOCR – 基于 Transformer 的 OCR 入门指南
    java计算机毕业设计酒店管理系统设计与实现源码+mysql数据库+系统+lw文档+部署
    如何使一个盒子水平垂直居中以及如何实现双飞翼(圣杯)布局?
    如果你会玩这4个自媒体运营工具,副业收入6000+很轻松
    上周热点回顾(5.20-5.26)
  • 原文地址:https://blog.csdn.net/qq_36154157/article/details/128089626