Shenglian / -WORK_TIP

工作上小技巧
4 stars 1 forks source link

[小程式]使用JavaScript 写Web路由 #3

Open Shenglian opened 7 years ago

Shenglian commented 7 years ago
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Building a router</title>

  <script>
    // Put John's template engine code here...
    // Simple JavaScript Templating
    // John Resig - https://johnresig.com/ - MIT Licensed
    (function () {
      var cache = {};

      this.tmpl = function tmpl(str, data) {
        // Figure out if we're getting a template, or if we need to
        // load the template - and be sure to cache the result.
        var fn = !/\W/.test(str) ?
          cache[str] = cache[str] ||
          tmpl(document.getElementById(str).innerHTML) :

          // Generate a reusable function that will serve as a template
          // generator (and which will be cached).
          new Function("obj",
            "var p=[],print=function(){p.push.apply(p,arguments);};" +

            // Introduce the data as local variables using with(){}
            "with(obj){p.push('" +

            // Convert the template into pure JavaScript
            str
            .replace(/[\r\t\n]/g, " ")
            .split("<%").join("\t")
            .replace(/((^|%>)[^\t]*)'/g, "$1\r")
            .replace(/\t=(.*?)%>/g, "',$1,'")
            .split("\t").join("');")
            .split("%>").join("p.push('")
            .split("\r").join("\\'") +
            "');}return p.join('');");

        // Provide some basic currying to the user
        return data ? fn(data) : fn;
      };
    })();

    (function () {
      var routes = {};  // A hash to store our routes:
      var events = [];  // An array of the current route's events:
      var el = null;    // The element where the routes are rendered:
      var ctx = {       // Context functions shared between all controllers:
        on: function (selector, evt, handler) {
          events.push([selector, evt, handler]);
        },
        refresh: function (listeners) {
          listeners.forEach(function (fn) {
            fn();
          });
        }
      };

      // Defines a route:
      function route(path, templateId, controller) {
        if (typeof templateId === 'function') {
          controller = templateId;
          templateId = null;
        }

        var listeners = [];

        Object.defineProperty(controller.prototype, '$on', {
          value: ctx.on
        });

        Object.defineProperty(controller.prototype, '$refresh', {
          value: ctx.refresh.bind(undefined, listeners)
        });

        routes[path] = {
          templateId: templateId,
          controller: controller,
          onRefresh: listeners.push.bind(listeners)
        };
      }

      function forEachEventElement(fnName) {
        for (var i = 0, len = events.length; i < len; i++) {
          var els = el.querySelectorAll(events[i][0]);
          for (var j = 0, elsLen = els.length; j < elsLen; j++) {
            els[j][fnName].apply(els[j], events[i].slice(1));
          }
        }
      }

      function addEventListeners() {
        forEachEventElement('addEventListener');
      }

      function removeEventListeners() {
        forEachEventElement('removeEventListener');
      }

      function router() {        
        el = el || document.getElementById('view'); // Lazy load view element:

        removeEventListeners();                     // Remove current event listeners:
        events = [];                                // Clear events, to prepare for next render:

        var url = location.hash.slice(1) || '/';    // Current route url (getting rid of '#' in hash as well):
        var route = routes[url] || routes['*'];     // Get route by url or fallback if it does not exist:

        if (route && route.controller) {            // Do we have a controller:
          var ctrl = new route.controller();
          if (!el || !route.templateId) {
            // If there's nothing to render, abort:
            return;
          }
          // Listen on route refreshes:
          route.onRefresh(function () {
            removeEventListeners();
            // Render route template with John Resig's template engine:
            el.innerHTML = tmpl(route.templateId, ctrl);
            addEventListeners();
          });
          // Trigger the first refresh:
          ctrl.$refresh();
        }

        if (el && route.controller) { // 使用John Resig的模板引擎,渲染路由模板 
          el.innerHTML = tmpl(route.templateId, new route.controller()); 
        }
      }

      this.addEventListener('hashchange', router);  // Listen on hash change:
      this.addEventListener('load', router);        // Listen on page load:

      this.route = route;                           // Expose the route register function:
    })();
  </script>

  <script type="text/html" id="home">
    <h1>Router FTW!</h1>
  </script>
  <script type="text/html" id="template1">
    <h1>Page 1: <%= greeting %>
    </h1>
    <p><%= moreText %></p>
    <button class="my-button">Click me <%= counter %> </button>
  </script>
  <script type="text/html" id="template2">
    <h1>Page 2: <%= heading %></h1>
    <p>Lorem ipsum...</p>
  </script>
  <script type="text/html" id="error404">
    <h1>404 Not found</h1>
  </script>

</head>

<body>
  <article>
    <a href="https://www.w3cplus.com/javascript/a-javascript-router.html"> 使用JavaScript 写Web路由 </a>
  </article>

  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#/page1">Page 1</a></li>
    <li><a href="#/page2">Page 2</a></li>
  </ul>

  <div id="view"></div>
  <script>
    route('/', 'home', function () {});
    route('/page1', 'template1', function () {
      this.greeting = 'Hello world!';
      this.moreText = 'Bacon ipsum...';
      this.counter = 0;
      this.$on('.my-button', 'click', function () {
        this.counter += 1;
        this.$refresh();
      }.bind(this));
    });
    route('/page2', 'template2', function () {
      this.heading = 'I\'m page two!';
    });
    route('*', 'error404', function () {});
  </script>
</body>

</html>
Shenglian commented 6 years ago

自製 router

Shenglian commented 6 years ago

使用JavaScript 写Web路由