Skip to content

Latest commit

 

History

History
212 lines (193 loc) · 5.86 KB

前端路由.md

File metadata and controls

212 lines (193 loc) · 5.86 KB

前端路由

传统路由:url改变,页面改变,刷新页面 spa路由:url改变,页面改变,不刷新页面(通过javascript改变内容)

实现方式:

  • hash模式:带#号,兼容性好,锚点功能失效
  • history模式:美观

hash模式

通过监听 window.addEventListener('hashchange', function() {}) 实现

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>hash</title>
</head>
<body>
  <div id="nav">
    <a href="#/page1">page1</a>
    <a href="#/page2">page2</a>
    <a href="#/page3">page3</a>
    <a href="#/page4">page4</a>
    <a href="#/page5">page5</a>
  </div>
  <div id="container"></div>
</body>
<script>
  class HashRouter {
    constructor(){
      this.routers = {}
      window.addEventListener('hashchange', this.load.bind(this))
    }
    //用于注册每个视图
    register(hash,callback = function(){}){
      this.routers[hash] = callback;
    }
    //用于注册首页
    registerIndex(callback = function(){}){
      this.routers['index'] = callback;
    }
    //用于处理视图未找到的情况
    registerNotFound(callback = function(){}){
      this.routers['404'] = callback;
    }
    //用于处理异常情况
    registerError(callback = function(){}){
      this.routers['error'] = callback;
    }
    load(e) {
      let hash = window.location.hash.slice(1)
      let handler

      if(!hash){
        handler = this.routers.index;
      }
      //未找到对应hash值
      else if(!this.routers.hasOwnProperty(hash)){
        handler = this.routers['404'] || function(){};
      }
      else {
        handler = this.routers[hash]
      }

      try{
        handler.apply(this);
      }catch(e){
        console.error(e);
        (this.routers['error'] || function(){}).call(this,e);
      }
    }
  }

  let router = new HashRouter();
  let container = document.getElementById('container');

  //注册首页回调函数
  router.registerIndex(()=> container.innerHTML = '我是首页');

  //注册其他视图回到函数
  router.register('/page1',()=> container.innerHTML = '我是page1');
  router.register('/page2',()=> container.innerHTML = '我是page2');
  router.register('/page3',()=> container.innerHTML = '我是page3');
  router.register('/page4',()=> {throw new Error('抛出一个异常')})
  router.registerError((e)=>container.innerHTML = '页面异常,错误消息:<br>' + e.message);
  router.registerNotFound(()=>container.innerHTML = '页面未找到');

  //加载视图
  router.load();
</script>
</html>

history模式

  • 移除a标签的默认跳转行为
  • 通过 history.pushStatehistory.replaceState 修改 history 记录
  • 通过 window.addEventListener('popstate', function(){}) 来监听前进后退按钮对 history 的修改
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>history</title>
</head>
<body>
<div id="nav">
  <a href="/page1">page1</a>
  <a href="/page2">page2</a>
  <a href="/page3">page3</a>
  <a href="/page4">page4</a>
  <a href="/page5">page5</a>
  <button id="btn">page2</button>
</div>
<div id="container"></div>
</body>
<script>
  class HistoryRouter{
    constructor(){
      this.routers = {}
      this.listenLink()
      this.listen()
    }
    register(path, cb = function () {}) {
      this.routers[path] = cb
    }
    registerIndex(cb = function () {}) {
      this.routers.index = cb
    }
    registerNotFound(cb = function () {}) {
      this.routers['404'] = cb
    }
    registerError(cb = function () {}) {
      this.routers['error'] = cb
    }
    push(path){
      history.pushState({path}, null, path)
      this.load(path)
    }
    replace(path){
      history.replace({path}, null, path)
      this.load(path)
    }
    listenLink() {
      const that = this
      document.addEventListener('click', function (e) {
        const dom = e.target
        const href = dom.getAttribute('href')
        if (dom.tagName.toLowerCase() === 'a' && href) {
          e.preventDefault()
          that.push(href)
        }
      })
    }
    listen(){
      window.addEventListener('popstate',(e) => {
        const state = e.state
        const path = state.path

        this.load(path)
      })
    }
    load(path) {
      let handler

      if (!this.routers.hasOwnProperty(path)) {
        handler = this.routers['404']
      } else {
        handler = this.routers[path]
      }

      try {
        handler.call(this)
      } catch(e) {
        console.error(e);
        (this.routers['error'] || function(){}).call(this,e);
      }
    }
  }

  let router = new HistoryRouter();
  let container = document.getElementById('container');

  //注册首页回调函数
  router.registerIndex(() => container.innerHTML = '我是首页');

  //注册其他视图回到函数
  router.register('/page1', () => container.innerHTML = '我是page1');
  router.register('/page2', () => container.innerHTML = '我是page2');
  router.register('/page3', () => container.innerHTML = '我是page3');
  router.register('/page4', () => {
    throw new Error('抛出一个异常')
  });

  document.getElementById('btn').onclick = () => router.push('/page2')


  //注册未找到对应path值时的回调
  router.registerNotFound(() => container.innerHTML = '页面未找到');
  //注册出现异常时的回调
  router.registerError((e) => container.innerHTML = '页面异常,错误消息:<br>' + e.message);
  //加载页面
  router.load();
</script>
</html>

参考