【前端面试小册】第7节-前端路由

211 阅读2分钟

1、前端路由3问?

  • 为什么会出现前端路由
  • 前端路由解决了什么问题
  • 前端路由实现的原理

2、传统页面

整个项目都是DOM直出,返回的HTML就是最终的效果

3、单页面

react Vue 单页面应用框架通用特点,通过【js渲染页面】

单页面中10几个页面是如何互相跳转的,这个时候前端路由就出现了,根据浏览器pathname匹配不同组件,更新DOM

4、hash哈希路由

#后面的发生变化可以被浏览器的hashchange监听到,而且不会重新向浏览器发送请求,浏览器路由会
hashchange可以监听到如下时间

  • a标签改变浏览器
  • 浏览器的前进后退
  • window.location方法,改变浏览器地址
window.addEventListener('DOMContentLoaded', Load)
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
  }
}

5、history历史模式

history 模式会比 hash 模式稍麻烦一些,因为 history模式依赖的是原生事件 popstate

history监听 url 中的路径变化,需要客户端和服务端共同的支持;

history常用方法

  • history.back ()
  • history.forward()
  • history.go(number)
  • history.pushState(obj, title, url)
  • history.replaceState(obj, title, url)

这5中方法都可以修改修改页面URL而不发送请求,但是页面重新刷新会报错,因为重新刷新会想浏览器发送HTTP请求,所以用history还得服务器配合

注意事项,popstate无法监听到

  • history.pushState()
  • history.replaceState不会触发popstate
  • a标签标签(a标签只改变pathname不会改变hash)

解决a标签无法被popstate监听到

遍历页面上的所有 a 标签,阻止 a 标签的默认事件的同时,加上点击事件的回调函数,在回调函数内获取 a 标签的 href 属性值,再通过 pushState 去改变浏览器的 location.pathname 属性值。然后手动执行 popstate 事件的回调函数,去匹配相应的路由

<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)
window.addEventListener('popstate', PopChange)
var routeView = null
function Load() {
  routeView = document.getElementById('route-view')
  // 默认执行一次 popstate 的回调函数,匹配一次页面组件
  PopChange()
  // 获取所有带 href 属性的 a 标签节点
  var aList = document.querySelectorAll('a[href]')
  // 遍历 a 标签节点数组,阻止默认事件,添加点击事件回调函数
  aList.forEach(aNode => aNode.addEventListener('click', function(e) {
    e.preventDefault() //阻止a标签的默认事件
    var href = aNode.getAttribute('href')
    //  手动修改浏览器的地址栏
    history.pushState(null, '', href)
    // 通过 history.pushState 手动修改地址栏,
    // popstate 是监听不到地址栏的变化,所以此处需要手动执行回调函数 PopChange
    PopChange()
  }))
}
function PopChange() {
  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>

pushState replaceState无法被监听到解决办法

dispatchEvent

const listener = function (type) {
  var orig = history[type];
  return function () {
    var rv = orig.apply(this, arguments);
    var e = new Event(type);
    e.arguments = arguments;
    window.dispatchEvent(e);
    return rv;
  };
};
window.history.pushState = listener('pushState');
window.history.replaceState = listener('replaceState');


// 监听自定义事件
window.addEventListener('pushState', this.refresh, false);
window.addEventListener('replaceState', this.refresh, false);