Two implementation principles of front-end routing

History API

I won't go into detail about the usage of each API here, you can read the MDN documentation: https://developer.mozilla.org…

Focus on the two new APIs  history.pushState and history.replaceState

Both APIs receive three parameters, which are

  • state object  — A JavaScript object associated with a new history entry created with the pushState() method. Whenever the user navigates to a newly created state, the popstate event is fired, and the state property of the event object contains a copy of the history entry's state object.

  • title  - This parameter is currently ignored by the FireFox browser, although it may be used in the future. Considering that this method may be modified in the future, it is safer to pass an empty string. Alternatively, you can pass in a short title indicating the state to be entered.

  • Address (URL)  — The address of the new history entry. The browser will not load the address after the pushState() method is called, but it may try to load it later, for example if the user restarts the browser. The new URL does not have to be an absolute path; if it is a relative path, it will be based on the current URL; the incoming URL and the current URL should be of the same origin, otherwise, pushState() will throw an exception. This parameter is optional; if not specified, it is the current URL of the document.

The difference is that both APIs operate on the browser's history without causing a page refresh.

The difference is that pushState adds a new history, while replaceState replaces the current history.

Let's take Baidu's console as an example (specifically, my browser opens the console on the Baidu homepage...)

We type in the console

window.history.pushState(null, null, "https://www.baidu.com/?name=orange");

Well, we observe that the url at this time becomes like this

We will not test them one by one here, we will directly give other usages, and you can try them yourself.

window.history.pushState(null, null, "https://www.baidu.com/name/orange");
//url: https://www.baidu.com/name/orange

window.history.pushState(null, null, "?name=orange");
//url: https://www.baidu.com?name=orange

window.history.pushState(null, null, "name=orange");
//url: https://www.baidu.com/name=orange

window.history.pushState(null, null, "/name/orange");
//url: https://www.baidu.com/name/orange

window.history.pushState(null, null, "name/orange");
//url: https://www.baidu.com/name/orange

 Note: The url here does not support cross-domain, and an error will be reported when we  www.baidu.com replace  it with.baidu.com

Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'https://baidu.com/?name=orange' cannot be created in a document with origin 'https://www.baidu.com' and URL 'https://www.baidu.com/?name=orange'.

Going back to the above example, the page is not refreshed every time the url is changed. Also according to the above, the browser will generate a history record

This is the premise of changing the url without refreshing the page. Let's talk about the first parameter  state object.

If  history.pushState() the method is run, the record corresponding to the history stack will be stored  in the state object , and we can actively call the history entry at any time.

Here is an example from mozilla

<!DOCTYPE HTML>
<!-- this starts off as http://example.com/line?x=5 -->
<title>Line Game - 5</title>
<p>You are at coordinate <span id="coord">5</span> on the line.</p>
<p>
 <a href="?x=6" onclick="go(1); return false;">Advance to 6</a> or
 <a href="?x=4" onclick="go(-1); return false;">retreat to 4</a>?
</p>
<script>
 var currentPage = 5; // prefilled by server!!!!
 function go(d) {
     setupPage(currentPage + d);
     history.pushState(currentPage, document.title, '?x=' + currentPage);
 }
 onpopstate = function(event) {
     setupPage(event.state);
 }
 function setupPage(page) {
     currentPage = page;
     document.title = 'Line Game - ' + currentPage;
     document.getElementById('coord').textContent = currentPage;
     document.links[0].href = '?x=' + (currentPage+1);
     document.links[0].textContent = 'Advance to ' + (currentPage+1);
     document.links[1].href = '?x=' + (currentPage-1);
     document.links[1].textContent = 'retreat to ' + (currentPage-1);
 }
</script>

When we click  Advance to ? on the corresponding url and template, both +1 will be +1, otherwise the click  retreat to ? will be -1, which satisfies the need for simultaneous changes of url and template view

In practice, we don't need to simulate the onpopstate event. The official documentation provides the popstate event, which will be generated when we switch in the history. As for the way to trigger popstate event, each browser implements it differently, we can do compatible processing according to different browsers.

hash

We often see # in urls. There are two cases for this #. One is what we call anchors, such as the typical back-to-top button principle, the jump between titles on Github, etc. The # in the route is not called Anchor, we call it hash, most of the routing systems of large frameworks are implemented by hash.

Similarly, we need an event that is triggered according to the change of the listening hash - the hashchange event

We  window.location do not re-render the page when we process the hash change, but add it to the history as a new page, so that we jump to the page and register ajax in the hashchange event to change the page content.

Here I simulated the principle on codepen:  http://codepen.io/orangexc/pe...Click to preview

In lower versions of IE, hashchange needs to be implemented by polling and listening for url changes. We can simulate the following

(function(window) {

  // 如果浏览器不支持原生实现的事件,则开始模拟,否则退出。
  if ( "onhashchange" in window.document.body ) { return; }

  var location = window.location,
  oldURL = location.href,
  oldHash = location.hash;

  // 每隔100ms检查hash是否发生变化
  setInterval(function() {
    var newURL = location.href,
    newHash = location.hash;

    // hash发生变化且全局注册有onhashchange方法(这个名字是为了和模拟的事件名保持统一);
    if ( newHash != oldHash && typeof window.onhashchange === "function"  ) {
      // 执行方法
      window.onhashchange({
        type: "hashchange",
        oldURL: oldURL,
        newURL: newURL
      });

      oldURL = newURL;
      oldHash = newHash;
    }
  }, 100);
})(window);

Of course, the routing of large frameworks is not so simple. The routing of angular 1.x associates hashes, templates, and processors, as follows

app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
 $routeProvider
 .when('/article', {
   templateUrl: '/article.html',
   controller: 'ArticleController'
 }).otherwise({
   redirectTo: '/index'
 });
 $locationProvider.html5Mode(true);
}])

This routing scheme is a hash method starting with # by default. If you do not consider the lower version of the browser, you can directly call  $locationProvider.html5Mode(true) the scheme using H5 without the hash scheme.

Summarize

I recommend the hash scheme for the two schemes, because it takes care of low-level browsers, but it is not beautiful (one more #). Low version compatibility see above!

The demo of this link contains the judgment method: http://sandbox.runjs.cn/show/...  . At the same time, the Github warehouse address is given:  minrouter , I recommend everyone to read the source code, only 117 lines, incisive!

If there is an extra # in your url during the above link test, it means that your browser should be updated.

The article comes from orange's personal blog  http://orangexc.xyz/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324954136&siteId=291194637