js web简单的路由管理器

灵感来自此博客此库

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
    <template id="home">
      <h1>home</h1>
    </template>
    <template id="about">
      <h1>about</h1>
    </template>
    <template id="dog">
      <h1>dog</h1>
    </template>
    <template id="notFound">
      <h1>404 not found</h1>
    </template>
    <script src="./aja-router.js"></script>
    <script>
      const router = new AjaRouter();
      router.forRoot([
        {
          path: "",
          redirectTo: "home"
        },
        {
          path: "home",
          render(host) {
            const t = document.querySelector("#home");
            host.append(document.importNode(t.content, true));
          }
        },
        {
          path: "about",
          render(host) {
            const t = document.querySelector("#about");
            host.append(document.importNode(t.content, true));
          }
        },
        {
          path: "dog/:id",
          render(host, route) {
            const t = document.querySelector("#dog");
            const dog = document.importNode(t.content, true);
            dog.querySelector(
              "h1"
            ).innerHTML = `dog, id is ${route.params.id.value}`;
            host.append(dog);
          }
        },
        {
          path: "**",
          render(host) {
            const t = document.querySelector("#notFound");
            host.append(document.importNode(t.content, true));
          }
        }
      ]);

      setTimeout(() => {
        router.push("about");
      }, 1200);
    </script>
  </body>
</html>

aja-router.js

const _textIsDynamicRouteExp = /\/?:[a-zA-Z]+/;
class AjaRouter {
  _host = document.querySelector("#root");
  _routes = [];

  constructor(host) {
    if (host) this._host = host;
    this._setup();
  }

  _setup() {
    window.addEventListener("load", e => {
      this._render();
    });

    window.addEventListener("popstate", e => {
      this._render();
    });

    // window.addEventListener("hashchange", e => {
    //   console.log("hash ");
    // });
  }

  forRoot(routes = []) {
    this._routes = routes.map(route => {
      const { path } = route;
      const pathSplit = path.split("/");
      if (path && path.match(_textIsDynamicRouteExp)) {
        route.isDynamic = true;
        // 动态路由
        const params = {};
        let exp = "";
        for (var i = 0; i < pathSplit.length; i++) {
          const item = pathSplit[i];
          let expItem = "/" + item;
          if (item.startsWith(":")) {
            params[item.replace(/^:/, "")] = { index: i };
            expItem = `/(?<${item.replace(/^:/, "")}>[^/]+)`;
          }
          exp += expItem;
        }
        if (exp && exp.trim() != "") {
          exp = exp.replace(/\//, "");
        }
        route.exp = new RegExp(exp);
        route.params = params;
      }

      return route;
    });
  }

  _findHashRoute(path) {
    const hash = path ?? document.location.hash.replace(/#\/?/, "");
    return this._match(hash);
  }

  /**
   * 使用path在routes中寻找路由
   */
  _match(path) {
    // 1, 先找普通路由
    let route = this._routes.find(i => i.path === path);
    if (route) {
      return route;
    }

    // 2, 找动态路由
    route = this._routes
      .filter(i => i.isDynamic)
      .find(i => {
        const pattern = "/";
        const routeNameSplit = path.split(pattern);
        const dynamicRouteNameSplit = i.path.split(pattern);
        const equalRouteLength =
          routeNameSplit.length == dynamicRouteNameSplit.length;
        const match = path.match(i.exp);
        if (match && match.groups) {
          for (const k in match.groups) {
            const param = i.params[k];
            param.value = match.groups[k];
          }
        }

        return equalRouteLength && match;
      });

    if (route) {
      return route;
    }

    // 3, 都没找到,默认返回404路由
    return this._find404Route();
  }

  _find404Route() {
    return this._routes.find(i => i.path === "**");
  }

  _render(path) {
    const matchRoute = this._findHashRoute(path);
    if (matchRoute) {
      this._host.innerHTML = "";
      if (matchRoute.redirectTo) {
        this._render(matchRoute.redirectTo);
      } else {
        matchRoute.render(this._host, matchRoute);
      }
    }
  }

  push(path) {
    try {
      this._render(path);
      window.history.pushState({}, document.title, `#/${path}`);
    } catch (error) {}
  }
}
posted @ 2020-03-18 14:06  Ajanuw  阅读(367)  评论(0编辑  收藏  举报