preface
As we all know, as the business functions of front-end applications become more and more complex, users have higher and higher requirements for the use of experience, single side (SPA) has become the mainstream form of front-end applications. One of the most significant features of large single-page applications is the use of front-end routing to jump rotor page system, by changing the URL of the page, without re-requesting the page, update the page view.
Update the view but the browser not to render the page, just to render molecular page, loading speed, flexible page response, this is the advantage of the SPA, this also is the front-end routing principle of core, it will give a person a kind of the same feeling as if to operate APP, to achieve this function in the browser environment there are two main ways:
- using
URL
的hash(#)
- using
H5
The new methodHistory interface
usingURL
theHash(#)
Before H5 became popular, spas generally used the HASH (#) of the URL as the anchor point, obtained the value after #, monitored its change, and rendered the corresponding sub-page. Netease cloud music official website is the use of this technology.
For example, if your address is http://localhost:8888/#/abc, the output from the location.hash would be #/ ABC.
So I’m going to start with the location object.
Let’s take a look at the official properties of location
attribute | describe |
---|---|
hash | Sets or returns the URL starting with # (anchor) |
host | Sets or returns the hostname and port number of the current URL |
hostname | Sets or returns the host name of the current URL |
href | Sets or returns the full URL |
pathname | Sets or returns the path portion of the current URL |
port | Sets or returns the port number of the current URL |
protocol | Sets or returns the protocol for the current URL |
search | Set or return from? The URL section at the beginning |
As can be seen from the table above, we can easily obtain the part after #, so how can we monitor the change of this part and change the corresponding sub-page?
The window object has an event that listens for hash changes. That event is onHashChange. First we need to listen for this event:
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script>
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
Copy the code
You can see that we are completely listening for the URL change, and the content on the page has changed accordingly. There are three ways to load different pages:
- Find the node content and change it (which is what we demonstrated above)
import
aJS
Files, inside filesexport
Template string- using
AJAX
Load the correspondingHTML
template
I’ve already shown you the first way, but it’s too limited, so I’ll show you two other ways to load a page.
import
way
Define a JS file called demo1.js and enter the content:
const str = '
I'm importing JS file
'
export default str
Copy the code
Import it from the main file and test it (always open it on the server for Chrome, or open it directly from Firefox) :
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<! Type ="module" -->
<script type="module">
import demo1 from './demo1.js'
document.querySelector('#id').innerHTML = demo1
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
Copy the code
You can see that the import file is already in effect, and most frameworks are treated in a similar way after being compiled.
For example, in the Vue framework, a.vue file is a custom file type that describes a VUE component using htML-like syntax. Each.vue file contains three types of top-level language blocks
,
AJAX
way
This article explains the routing mechanism in detail. AJAX uses JQuery as a direct wheel.
Define an HTML file named demo2.html and write something to it (since the main page already has root tags such as head and body, this file only needs to write the tags that need to be replaced) :
<div>I'm an HTML file that AJAX loaded in</div>
Copy the code
We’ll write to the main file and test it:
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="module">
// import demo1 from './demo1.js'
// document.querySelector('#id').innerHTML = demo1
$.ajax({
url: './demo2.html'.success: (res) = > {
document.querySelector('#id').innerHTML = res
}
})
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
Copy the code
As you can see, the files loaded in using AJAX have also taken effect.
Now that loading the contents of the different pages has taken effect, we just need to wrap our listeners and use observer mode to encapsulate the route changes:
<body>
<h1 id="id">I'm a blank page</h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script type="module">
import demo1 from './demo1.js'
// Create a newRouter class
class newRouter {
// Initializes the routing information
constructor() {
this.routes = {};
this.currentUrl = ' ';
}
// Pass in the URL and the corresponding callback function based on the URL
route(path, callback = (a)= >{{})this.routes[path] = callback;
}
// Cut the hash, render the page
refresh() {
this.currentUrl = location.hash.slice(1) | |'/';
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
/ / initialization
init() {
window.addEventListener('load'.this.refresh.bind(this), false);
window.addEventListener('hashchange'.this.refresh.bind(this), false); }}// new a Router instance
window.Router = new newRouter();
// Route instance initialization
window.Router.init();
// Get key nodes
var content = document.querySelector('#id');
Router.route('/id1', () => {
content.innerHTML = 'id1'
});
Router.route('/id2', () => {
content.innerHTML = demo1
});
Router.route('/id3', () => {
$.ajax({
url: './demo2.html'.success: (res) = > {
content.innerHTML = res
}
})
});
</script>
Copy the code
The effect is as follows:
So far, using hash(#) for front-end routing management has been implemented.
usingH5
The new methodHistory interface
The hash method used above is good, but the problem is that it is ugly. If it is used in wechat or other apps that do not display urls, it is fine, but if it is used in regular browsers, it will be a problem.
Therefore, the H5 History mode solves this problem.
Prior to H5, History only had the following apis:
API | instructions |
---|---|
back() |
Go back to the last visitURL (Same as when the browser hits the back button) |
forward() |
Advancing to the point of retractingURL (Same as when the browser clicks the forward button) |
go(n) |
n Receives an integer and moves to the page specified by the integer, for examplego(1) The equivalent offorward() .go(-1) The equivalent ofback() .go(0) Equivalent to refresh the current page |
If you move beyond the bounds of the access history, the above three methods do not fail, but silently fail.
However, in the era of the H5, the new H5 brought more new features:
From the cache
By default, the browser caches the current session page so that when the next page hits the back button, or the previous page hits the forward button, the browser extracts and loads the page from the cache, a feature known as “round-trip caching.”
PS: This cache preserves page data, DOM, and JS state, essentially leaving the entire page intact.
Add record to history stack: pushState(state, title, URL)
Browser support: IE10+
- State: a
JS
Object (no larger than 640kB), used in thepopstate
Is retrieved as a parameter in the event. If you don’t need this object, you can fill it in herenull
- Title: The title of the new page. Some browsers (such as Firefox) ignore this parameter, so it is generally
null
- Url: the address of the new history,It can be a page address or an anchor point valueThe new
url
Must be related to the currenturl
In the same field, otherwise an exception will be thrown. If not specified, this parameter will be set to the current documenturl
Chestnut:
// 现在是 localhost/1.html
const stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2'.'2.html');
// The browser address bar will immediately become localhost/2.html
// But!!
// Does not jump to 2.html
// Does not check if 2.html exists
// Will not be retrieved in popState events
// No page refresh is triggered
// This method simply adds the latest record
Copy the code
In addition, there are a few points to note:
- will
url
Does not fire when set to the anchor point valuehashchange
- If set according to the same Origin policydifferentDomain addresses will report errors. The purpose of this is to prevent users from thinking they are the same site, which is easy to do without this limitation
XSS
、CSRF
And other attack modes
Change the current history: replaceState(state, title, URL)
Browser support: IE10+
- Parameter Meaning The same as
pushstate
- Change the current history rather than add new records
- It doesn’t trigger either
popstate
history.state
Browser support: IE10+
- Of the current history
state
.
popstate
Definition: The PopState event is emitted every time the browsing history (that is, the history object) of the same document changes.
Note: This event is not triggered by calling the pushState or replaceState methods alone, but only when the user clicks the browser’s back and forward buttons, or calls the back, forward, or Go methods using JavaScript. In addition, this event is only for the same document and will not be triggered if a switch in browsing history results in a different document being loaded.
Chestnut:
window.onpopstate= (event) = >{    console.log(event.state) // State object for the current history
}
Copy the code
implementation
With that in mind, let’s implement routing in History mode!
We will modify the above HTML slightly, please be patient analysis:
<body>
<h1 id="id">I'm a blank page</h1>
<a class="route" href="/id1">id1</a>
<a class="route" href="/id2">id2</a>
<a class="route" href="/id3">id3</a>
</body>
Copy the code
import demo1 from './demo1.js'
// Create a newRouter class
class newRouter {
// Initializes the routing information
constructor() {
this.routes = {};
this.currentUrl = ' ';
}
route(path, callback) {
this.routes[path] = (type) = > {
if (type === 1) history.pushState( { path }, path, path );
if (type === 2) history.replaceState( { path }, path, path );
callback()
};
}
refresh(path, type) {
this.routes[this.currentUrl] && this.routes[this.currentUrl](type);
}
init() {
window.addEventListener('load', () = > {// Get the current URL path
this.currentUrl = location.href.slice(location.href.indexOf('/'.8))
this.refresh(this.currentUrl, 2)},false);
window.addEventListener('popstate', () = > {this.currentUrl = history.state.path
this.refresh(this.currentUrl, 2)},false);
const links = document.querySelectorAll('.route')
links.forEach((item) = > {
// Override the click event of the a tag to prevent the default jump behavior
item.onclick = (e) = > {
e.preventDefault()
// Get the modified URL
this.currentUrl = e.target.getAttribute('href')
/ / rendering
this.refresh(this.currentUrl, 2)}})}}// new a Router instance
window.Router = new newRouter();
// Instance initialization
window.Router.init();
// Get key nodes
var content = document.querySelector('#id');
Router.route('/id1', () => {
content.innerHTML = 'id1'
});
Router.route('/id2', () => {
content.innerHTML = demo1
});
Router.route('/id3', () => {
$.ajax({
url: './demo2.html'.success: (res) = > {
content.innerHTML = res
}
})
});
Copy the code
The demo diagram is as follows:
conclusion
In general, hash and history will work, unless you’re more concerned with the appearance level, and the # symbol does look a little ugly in the URL. In addition, calling history.pushState() has the following advantages over modifying hash directly, according to Mozilla Develop Network:
pushState()
Set a newURL
Can be with the currentURL
Homologous arbitraryURL
; whilehash
Can only be modified#
The following section, therefore can only be set with the currentURL
With the documentURL
pushState()
Set a newURL
Can be associated with the currentURL
Exactly the same, this will also add records to the stack; whilehash
The new value set must be different from the original to trigger the action to add the record to the stackpushState()
throughstateObject
Parameters can add any type of data to a record; whilehash
Only short strings can be added;pushState()
Additional Settings availabletitle
Property for later use.
History mode is a happy alternative to hash mode, but history is not always a good alternative to hash mode. It works well in a browser, but when it comes to making HTTP requests from the URL back end, the difference is significant. This is especially true when the user manually enters the URL and hits the press enter, or when the browser is refreshed (or restarted).
hash
In mode, onlyhash
The content before the symbol is included in the request, for examplehttp://www.qqq.com
, so for the back end, even if the full coverage of the route is not achieved, there is no return404
Error.history
In mode, front-endURL
Must beAnd the actual backend initiating the requestURL
Consistent, such ashttp://www.qqq.com/book/id
. If the back end lacks a pair/book/id
Route processing will be returned404
Error.Vue-Router
The official website says: “However, this mode to play well, but also need to support the background configuration… So, you add a candidate resource on the server that covers all cases: ifURL
If no static resource is matched, the same should be returnedindex.html
The page, the page is youapp
Dependent pages.”- The back-end (
Apache
或Nginx
) for simple route configuration, and with front-end routes404
Page support.
Finally, I am sorry to promote the component library I wrote based on the Taro framework: MP-Colorui.
I will be very happy if I can star easily. Thank you.
Click here for documentation
Click here to get the GitHUb address