前端路由

什么是前端路由

传统的多页面应用程序,页面跳转会刷新并请求服务器, 服务器控制路由根据url返回相应的响应或者资源(返回一个完整的html页面包含css,js图片等).浪费带宽且用户体验不好;

面对日益增长的网页需求,网页开始走向模块化、组件化的道路,vue,react,angular等前端框架的出现让单页面应用程序变成了大趋势.

单页面应用程序只有第一次会请求服务器,加载完整的页面,页面跳转不会刷新页面;之后的请求仅仅获取必要的数据,减少了请求体积,加快页面响应速度,降低了对服务器的压力,有更好的用户体验,运行更加流畅

前端路由就是单页面网站,根据浏览器地址路径的不同,来匹配相对应的页面组件

原理

hash模式

hash模式最显著的特点就是浏览器地址栏有#,#后面的值就是hash值
浏览器有一个原生事件叫做hashchange,通过hashchange事件可以监听浏览器#后面的hash值的变化,从而匹配相对应的组件显示到页面上
hash模式简易路由

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
</head>
  <body>
    <div>
      <ul>
        <li><a href="#/page1">page1</a></li>
        <li><a href="#/page2">page2</a></li>
      </ul>
      <!--渲染对应组件的地方-->
      <div id="route-view"></div>
    </div>
  <script type="text/javascript">
  
    window.addEventListener('DOMContentLoaded', Load)   // 第一次加载的时候,不会执行hashchange监听事件,所以可以在dom加载完成时默认执行一次hashchange
    window.addEventListener('hashchange', HashChange)
   
    var routeView = null  // 展示页面组件的节点
    function Load() {
      routeView = document.getElementById('route-view')
      HashChange()
    }
    function HashChange() {
      // 每次触发 hashchange 事件,通过 location.hash 拿到当前浏览器地址的 hash 值,根据不同的路径展示不同的内容
      switch(location.hash) {
      case '#/page1':
        routeView.innerHTML = 'page1'
        return
      case '#/page2':
        routeView.innerHTML = 'page2'
        return
      default:
        routeView.innerHTML = 'page1'
        return
      }
    }
  </script>
  </body>
</html>


history模式

history模式对比hash模式最大的特点是浏览器地址没有#,且history模式依赖的是popstate事件
a标签的点击事件,history.pushsState,history.replaceState不会触发popstate,所以需要做一些特殊处理
tip: 如果使用history模式,需要后端或者nginx配合配置,否则刷新后页面会404(原因是history模式没有# 刷新后浏览器会将整个页面地址当成url去请求服务端,而url中有一部分其实是前端的路由,服务端根据url肯定是找不到资源的,则返回404了)
history模式简易路由

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
</head>
<body>
  <div>
    <ul>
      <li><a href="/page1">page1</a></li>
      <li><a href="/page2">page2</a></li>
    </ul>
    <div id="route-view"></div>
  </div>
  <script type="text/javascript">
    window.addEventListener('DOMContentLoaded', Load) // 在dom加载完成时默认执行一次popstate
    window.addEventListener('popstate', PopChange)
    var routeView = null
    function Load() {
      routeView = document.getElementById('route-view')
      PopChange() // 默认执行一次 popstate 的回调函数,匹配一次页面组件
      
      var aList = document.querySelectorAll('a[href]') // 获取所有带 href 属性的 a 标签节点
   
      aList.forEach(aNode => aNode.addEventListener('click', function(e) {    // 遍历 a 标签节点数组,阻止默认事件,添加点击事件回调函数
        e.preventDefault() //阻止a标签的默认事件
        var href = aNode.getAttribute('href')   
        history.pushState(null, '', href)  //  通过手动修改浏览器的地址栏
       
        PopChange()    // popstate 是监听不到地址栏的变化,所以此处需要手动执行回调函数 PopChange
      }))
    }
    
    function PopChange() {   //popstate的事件监听处理函数 
      console.log('location', location)
      switch(location.pathname) {
      case '/page1':
        routeView.innerHTML = 'page1'
        return
      case '/page2':
        routeView.innerHTML = 'page2'
        return
      default:
        routeView.innerHTML = 'page1'
        return
      }
    }
  </script>
</body>
</html>

原文地址:https://www.cnblogs.com/hanyan99/p/14637223.html