【干货】Javascript 实现Hash路由、History 路由

1,362 阅读2分钟

网页url组成部分

image.png

获取方式

image.png

1. H5 Hash路由

1.1 hash的特点:

  1. hash变化会 触发网页跳转,即浏览器的前进、后退;
  2. hash 变化不会刷新页面,SPA必需的特点;
  3. hash 永远不会提交到 server 端(前端自生自灭)

1.2 hash 变化方式

  • JS 修改 url
  • 手动修改 url 的 hash
  • 浏览器前进、后退

1.3 hash 代码实现

        <button id="btn1">修改 hash</button>
        
        ...
        
        window.onhashchange = (event) => {
            console.log('old url', event.oldURL)
            console.log('new url', event.newURL)

            console.log('hash:', location.hash)
        }

        // 页面初次加载,获取 hash
        document.addEventListener('DOMContentLoaded', () => {
            console.log('hash:', location.hash)
        })

        // JS 修改 url
        document.getElementById('btn1').addEventListener('click', () => {
            location.href = '#/user'
        })

2. H5 History 路由

  • url 规范的路由,但是跳转时不刷新页面
  • history.pushState
  • window.onpopstate

2.1 正常的页面浏览

2.2 改造成H5 history 路由

2.3 H5 history 代码实现

  • 设置 history.pushState(state, '', 'page1')

  • 监听 window.onpopstate=()=>{}

  • 页面初次加载,获取 document.addEventListener('DOMContentLoaded',()=>{})

  • history,需要 server 端配合,可参考

     <p>history API test</p>
     <button id="btn1">修改 url</button>

    ... 
    
    <script>
        // 页面初次加载,获取 path
        document.addEventListener('DOMContentLoaded', () => {
            console.log('load', location.pathname)
        })

        // 打开一个新的路由
        // 【注意】用 pushState 方式,浏览器不会刷新页面
        document.getElementById('btn1').addEventListener('click', () => {
            const state = { name: 'page1' }
            console.log('切换路由到', 'page1')
            history.pushState(state, '', 'page1') // 重要!!
        })

        // 监听浏览器前进、后退
        window.onpopstate = (event) => { // 重要!!
            console.log('onpopstate', event.state, location.pathname)
        }
    </script>

2.4 可以拦截 a 标签的点击事路由实现

class JSHistoryRouter {
 constructor(routerview){
     this.routerView = routerview
 }
 init() {
     let that = this
     let linkList = document.querySelectorAll('a[href]')
     linkList.forEach(el => el.addEventListener('click', function (e) {
         e.preventDefault() // 阻止 <a> 默认跳转事件
         history.pushState(null, '', el.getAttribute('href')) // 获取 URL,跳转
         onPopState()
     }))
     // 监听 URL 改变
     window.addEventListener('popstate', onPopState)
 }
 push(path){
     history.pushState(null, '', path)
     onPopState()
 }
 replace(path){
     history.replaceState(null, '', path)
     onPopState()
 }
 // 路由变化时,根据路由渲染对应 UI
function onPopState () {  
     switch (location.pathname) {    
         case '/home':      
         routerView.innerHTML = 'Home'      
         return    
     case '/about':      
         routerView.innerHTML = 'About'      
         return    
      default:      
         return  
     }
   }
}

3. 总结

hashH5 history
监听window.onhashchangewindow.onpopstate
设置location.href = '#/user'history.pushState
需要支持需要后端支持(将所有的路由拦截返回 index.html)

3.1 两者选择

  • TO B 的系统推荐使用hash,简单易用,对url 规范不敏感
  • TO C 的系统,可以考虑H5 history,但需要服务器支持
  • 能选择简单的,就别用复杂的,要考虑成本和收益

[题外话] react-router支持三种

  • BrowserRouter,控制浏览器真实地址的 history 对象
  • HashHistory, 控制浏览器 hash 的 history 对象
  • StaticRouter, 控制内存中地址的

[题外话] history 对象

  • 使用的是history库。 push,go,goBack, goForward操作的支持。
  • BrowserRouter和HashHistory调用的浏览器的history对象
  • StaticRouter会在内存中维护一个地址栈。支持 push,go,goBack, goForward操作[存疑?]

浏览器历史堆栈是如何管理的

文章:www.cnblogs.com/accordion/p…