js 实现路由,了解前端项目路由原理

122 阅读1分钟

思路:根据路径变化展示相应的内容

Hash模式

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>原生实现路由Hash模式</title>
  </head>
  <body>
    <head class="header">
      <h1>我是头部</h1>
    </head>
    <!-- 左侧导航栏 -->
    <section class="section_left">
      <ul>
        <li>
          <a href="/">这是默认菜单</a>
        </li>
        <li>
          <a href="#/html">HTML学习</a>
        </li>
        <li>
          <a href="#/css">CSS学习</a>
        </li>
        <li>
          <a href="#/javascript">Javascript学习</a>
        </li>
      </ul>
    </section>
    <!-- 右侧内容显示区域 -->
    <sidebar class="sidebar_right">
      <h1>我是默认内容</h1>
    </sidebar>
    <!-- 底部 -->
    <footer class="footer">
      <h1>我是底部</h1>
    </footer>

    <script>
      (function() {
        var Router = function() {
          this.routes = {}; //保存路由
          this.curUrl = ""; //获取当前的hash值
        };
        Router.prototype.init = function() {
          //hashchange事件,当hash变化时,调用reloadPage方法
          //第一个this指向window,bind里面的this指向调用这个函数的对象,具体使用方法可以百度
          window.addEventListener("hashchange", this.reloadPage.bind(this));
        };

        Router.prototype.reloadPage = function() {
          this.curUrl = location.hash.substring(1) || "/"; //获取当前hash的值(去掉#)
          this.routes[this.curUrl](); //运行当前hsah值对应的函数
        };

        Router.prototype.map = function(key, callback) {
          //保存路由对应的函数:
          this.routes[key] = callback; //key表示hash的值(去掉#),callback表示当前hash对应的函数
        };
        window.oRou = Router;
      })();

      var oRouter2 = new oRou();
      oRouter2.init();
      oRouter2.map("/", function() {
        var oSidebar = document.querySelector("sidebar");
        oSidebar.innerHTML = "我是主页";
      });

      oRouter2.map("/html", function() {
        var oSidebar = document.querySelector("sidebar");
        oSidebar.innerHTML = "我是html页面";
      });

      oRouter2.map("/css", function() {
        var oSidebar = document.querySelector("sidebar");
        oSidebar.innerHTML = "我是css页面";
      });

      oRouter2.map("/javascript", function() {
        var oSidebar = document.querySelector("sidebar");
        oSidebar.innerHTML = "我是javascript页面";
      });
    </script>

    <style>
      * {
        padding: 0;
        margin: 0;
      }
      a {
        text-decoration: none;
        color: #ffffff;
      }
      .header {
        width: 100%;
        height: 100px;
        background-color: rgb(198, 195, 212);
        text-align: center;
        line-height: 100px;
        color: #865a5a;
      }
      .section_left {
        width: 16%;
        height: 500px;
        float: left;
        background-color: rgb(173, 145, 145);
      }
      .section_left ul li {
        list-style: none;
        width: 100%;
        height: 50px;
        border-bottom: 1px solid #ffffff;
        text-align: center;
        line-height: 50px;
      }
      .sidebar_right {
        width: 78%;
        float: right;
        height: 470px;
        background-color: rgb(105, 90, 90);
        margin-right: 3%;
        margin-top: 15px;
        text-align: center;
        line-height: 470px;
        border-radius: 10px;
        color: #e6cdcd;
      }
      .footer {
        width: 100%;
        height: 100px;
        background-color: rgb(190, 195, 216);
        clear: both;
        text-align: center;
        line-height: 100px;
        color: #925959;
      }
    </style>
  </body>
</html>

History模式

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>原生实现路由History模式</title>
  </head>
  <body>
    <header class="header">
      <h1>我是头部</h1>
    </header>
    <!-- 左侧导航栏 -->
    <section class="section_left">
      <ul>
        <li>
          <a href="javascript:;" data-href="/home">首页</a>
        </li>
        <li>
          <a href="javascript:;" data-href="/abc">菜单一</a>
        </li>
        <li>
          <a href="javascript:;" data-href="/def">菜单二</a>
        </li>
        <li>
          <a href="javascript:;" data-href="/ghi">菜单三</a>
        </li>
      </ul>
    </section>
    <!-- 右侧内容显示区域 -->
    <sidebar class="content">
      <h1>我是默认内容</h1>
    </sidebar>
    <!-- 底部 -->
    <footer class="footer">
      <h1>我是底部</h1>
    </footer>
    <script>
      const history = window.history;
      const createSomeElement = (textContent) => {
        const div = document.createElement("div");
        div.textContent = textContent;
        return div;
      };
      // 路径与路由组件的银映射
      const pages = {
        "/home": createSomeElement("首页"),
        "/abc": createSomeElement("内容一"),
        "/def": createSomeElement("内容二"),
        "/ghi": createSomeElement("内容三"),
      };

      const IfRedirectPath = (path) => {
        const pagesId = Object.keys(pages);
        if (path == "/" || !pagesId.includes(path)) {
          path = "/home";
          history.pushState({}, "", "/home");
        }
        return path;
      };

      const renderPage = (path) => {
        // redirect的逻辑
        path = IfRedirectPath(path);
        const cont = document.querySelector(".content");
        const firstChild = cont.firstElementChild;
        if (firstChild) {
          cont.removeChild(firstChild);
        }
        cont.appendChild(pages[path]);
      };

      const myRouter = function() {
        const handlers = [];
        const listen = (listener) => {
          handlers.push(listener);
          // return function unlisten() {
          //   handlers.filter((h) => h !== listener);
          // };
        };
        const call = (location) => {
          handlers.forEach((cb) => cb(location));
        };

        window.addEventListener("popstate", function() {
          call({ url: window.location.pathname });
        });

        return {
          listen,
          call,
        };
      };

      const { listen, call } = myRouter();

      const push = (url, state = {}, title) => {
        try {
          history.pushState(state, title, url);
        } catch (e) {
          window.assign(url);
        }
        call({ state, title, url });
      };

      document
        .querySelector(".section_left")
        .addEventListener("click", function(e) {
          if (e.target.tagName !== "A") return;
          push(e.target.dataset.href);
        });

      var unListen = listen(function(loc) {
        renderPage(loc.url);
      });

      window.addEventListener("load", function() {
        const path = window.location.pathname;
        push(path);
      });
    </script>

    <style>
      * {
        padding: 0;
        margin: 0;
      }
      a {
        text-decoration: none;
        color: #ffffff;
      }
      .header {
        width: 100%;
        height: 100px;
        background-color: rgb(198, 195, 212);
        text-align: center;
        line-height: 100px;
        color: #865a5a;
      }
      .section_left {
        width: 16%;
        height: 500px;
        float: left;
        background-color: rgb(173, 145, 145);
      }
      .section_left ul li {
        list-style: none;
        width: 100%;
        height: 50px;
        border-bottom: 1px solid #ffffff;
        text-align: center;
        line-height: 50px;
      }
      .content {
        width: 78%;
        float: right;
        height: 470px;
        background-color: rgb(105, 90, 90);
        margin-right: 3%;
        margin-top: 15px;
        text-align: center;
        line-height: 470px;
        border-radius: 10px;
        color: #e6cdcd;
      }
      .footer {
        width: 100%;
        height: 100px;
        background-color: rgb(190, 195, 216);
        clear: both;
        text-align: center;
        line-height: 100px;
        color: #925959;
      }
    </style>
  </body>
</html>