“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”
Concepts of routing
In fact, in the early years, there was no concept of front-end routing, but with the development of the front-end, there was front-end routing. (Lu Xun said)
In the early days of template engine development, you would see a page URL like this: http://test.xxx.help.cn/bbs/sage.php or http://home.xxx.cn/bbs/sage.html to like this. PHP or are. The path of the HTML, is through the server side rendering, returned to a page directly. While this is good for SEO search, the code is not easy to maintain, and it also puts pressure on the server.
Ajax and SPA
The development of front-end routing is inseparable from the popularity of Ajax. Back in the HTTP0.9 (and earlier) era, you returned an HTML page directly. Each update requires a page refresh, which affects the interactive experience. With the development of the network, there is an urgent need for a scheme to improve the situation. With Ajax, user interaction doesn’t have to refresh the page every time, and the experience is vastly improved. Along came the single page application SPA.
What is SPA?
To put it simply; In SPA application, there is only one hTML page, and the hTML page contains an APP, that is, a placeholder. The switching of the page is within the APP, that is, the switching of view. As shown in figure:
The page can be changed by switching the view. But there is a problem: simply by using JAVASCRIPT and HTML, the page URL does not change, the browser does not remember the user’s actions. The user’s back, forward operations are invalid, very embarrassing. And the URL does not change, a page only a URL, is not conducive to SEO. Egg pain.. In order to solve the above problems, the concept of front-end routing is introduced. As long as you can listen for URL changes without sending requests, you can do a simple front-end routing. There are two ways to implement front-end routing: Hash and History.
Hash pattern
The earliest routing solution for hash, www.baidu/#/xx/xx, changes the URL by changing the hash value after the anchor point # without refreshing the page. Using window.location.hash, you can get the hash value and then listen for the hashChange event. When the hash value is updated when the hasChange event is triggered, the front-end routing function can be implemented. Ok, not much bb. Let’s implement it:
Implement a hash
The idea is to maintain an object that stores path(for matching hash) and View (for rendering when matching hash).
class MyHashRouter {
constructor() {
this.router = {}; // Store the registered route
}
window.addEventListener('hashChange'.this.loadView.bind(this), false);
}
Copy the code
Quite simply, loadView is triggered by listening for changes in the hashChange event (by routing matches the View). Note the Router object:
router {
path: "/XX".// Route path
callBack: () = > {} // An execution function (to display the view)
}
Copy the code
Implement the registered route:
registerRouter(path, callBack = ()=>{}) {
this.router[path] = callBack;
// A route corresponds to a view(a turnip and a pit)
}
Copy the code
One caveat: if path does not exist, we default to /, which is the home page.
registerIndexRouter(callBack = () => {}) {
this.router['index'] = callBack; // index is the home page by default
}
Copy the code
ok! Simple route registration is complete. Now let’s think about route switching view switching. A tag is used to implement the HASH change of the URL. During the hash change, we obtain the hash and match the registered route with the hash. Code to go:!
loadView() {
let hash = window.location.hash.slice(1); // remove the # sign
let cb; // Execute function (view)
hash ? cb = this.router[hash] : cb = this.router.index;
// Hash exists: find corresponding view, otherwise corresponding index
cb.call(this); / / execution
}
Copy the code
Such a simple hash route is complete. Note one problem: If the route is not matched, the page 404 is redirected by default.
hashNotFound() {
if (!this.router.hasOwnProperty(hash) {
cb = this.routers['404'] | |function(){}; }}Copy the code
Code is as follows:
class MyHashRouter {
constructor(){
this.router = {};
window.addEventListener('hashChange'.this.loadView.bind(this), false);
}
registerIndexRouter(callBack = () => {}) {
this.router['index'] = callBack;
}
registerRouter(path, callBack = () => {}) {
this.router[path] = callBack;
}
registerNotFound(callback = () => {}){
this.routers['404'] = callback; / / registered 404
}
loadView() {
let hash = window.location.hash.slice(1);
let cb;
if(! hash) { cb =this.router.index;
}
else if (!this.router.hasOwnProperty(hash) && hash) {
cb = this.router['404'];
}
else {
cb = this.router.hash;
}
cb.call(this); }}Copy the code
We can play with it simply:
let router = new MyHashRouter();
let app = document.getElementById('app');
/ / home page
router.registerIndexRouter(() = > app.innerHTML = 'home');
// Register other views
router.registerRouter('/path1'.() = > app.innerHTML = 'view1');
router.registerRouter('/path2'.() = > app.innerHTML = 'view2');
router.registerRouter('/path3'.() = > app.innerHTML = 'view3');
router.registerRouter('/path4'.() = > app.innerHTML = 'view4');
router.registerNotFound(() = > app.innerHTML = 'Page not found'); / / 404
router.loadView(); // Load the view
Copy the code
The history mode
history api
The DOM Window object provides access to the browser’s session history through the History object (not to be confused with WebExtensions History). It exposes a number of useful methods and properties, allows you to jump forward and backward through a user’s browsing history, and — starting with HTML5 — provides manipulation of the contents of the History stack. –MDN
window.history.back() === Browser rollbackwindow.history.forward() === browser forwardwindow.history.go(-1) === move one page back === back()window.history.go(1) === move a page forward === forward() go()2/-2) === jumps to a point specified in historyCopy the code
New attributes history.pushState() and history.replacEstate () have been added to HTML5 to add and modify history entries, respectively. Note: This method works with window.onpopState.
History. pushState(state, title, url) and history.replaceState(state, title, url) accept three arguments:
- State: A legal Javascript object that can be used in popState events
- Null (…)
- Url: Any valid URL used to update the browser’s address bar
PushState () stores the existing URL on the stack. Calling pushState() is similar to setting window.location = “#foo”; both create and activate a new history on the current page. But pushState has the following advantages:
- The new URL can be any URL of the same origin as the current URL.
- If you don’t want to change the URL, don’t change it. Instead, set window.location = “#foo”; A new history entry can only be created if the current hash is not #foo.
ReplaceState () replaces the current page history in history with the URL. Ok! We now know that replaceState() and pushState() can change the URL without a page refresh. So we can use the history properties to implement front-end routing. Here’s the problem:
In hash mode, we change the URL by listening for the hashChange event, change the view. But in History mode, there is no way to listen for history. How to do ah!!
The way to do it is, we intercept all the methods that cause history changes by listening for them. In this way, you can intercept history in disguise.
In HTML5, history changes can be triggered in the following ways:
- A Label click to jump
- Browser rollback, forward event.
- The pushState() and replaceState() methods are triggered.
Ok, now let’s briefly implement the history schema. The hash mode for registering router objects is basically the same as the previous one, but there are only more BB’s here. Basically intercepting methods that can trigger a history change.
Generic processing paths and views
There’s nothing to say. Path changes view changes.
commonDealPath(path) {
let view;
if(!this.router.hasOwnProperty(path)){
view = this.routers['404'] | |function(){};
}
else{
view = this.routers[path]; // There is a corresponding path
}
view.call(this);
}
Copy the code
Listen for link A globally
1. Get the href value of the a tag. 2. Block the default event of tag A.
listenerAClick() {
window.addEventListener('click'.(e) = > {
if (e && e.target) {
let target = e.target; // Get the click event
if(target.tagName.toUpperCase() === 'A' && target.getAttribute('href')) {let path = target.getAttribute('href')); // Get the href attribute of the a tag.
history.pushState({path}, null, path); // Change the URL with pushState.
this.commonDealPath(path); / / triggers commonDealPath}}}}Copy the code
Listening for Browser events
Listening for browser forward and backward, that is, listening for popState events.
listenPop() {
window.addEventListener('popState'.() = > {
let state = e.state || {};
let path = state.path || "";
this.commonDealPath(path); / / triggers commonDealPath
}, false)}Copy the code
Intercept pushState() and replaceState()
So let’s do these two methods separately. Trigger view changes while listening.
pathpush(path) {
history.pushState({path},null,path);
this.commonDealPath(path);
}
pathReplace(path) {
history.replaceState({path},null,path);
this.commonDealPath(path);
}
Copy the code
ok! Simple Histroy mode is done! In a word: Listen for the method that triggers the history change, then change View.
compatibility
hash:
history:
Hash is more compatible than Hsitory.
The last
Simple analysis of a wave of hash and history. The Vue router and react Router will be shared next time. Thank you!!!!!