JS implementation listens for route changes
preface
Recently, my colleague is working on a special project called Mapping data acquisition system. He threw me a question about how to implement route listener interception with native JS and add events to get the current page path. Threaten you to search online, but also said to me a week can not come out!! Too much!)
Found this article: juejin.cn/post/684490… (Smack in the face ~ HHHH ~ [smirk ing])
Function Appeal: Listens to intercept route changes
Before you begin, review the concept of front-end routing: What do you know about front-end routing?
We know that front-end routing can be implemented in two ways:
- Hash pattern
- Listening onhashchange
- Historical pattern
- The history mode relies on native event PopState
- History.pushstate () or history.replacEstate () do not trigger the popState event
Since listening for route changes on all types of pages is not sufficient just to listen for hashchange and popState events, you need to override the pushState and replaceState methods on the history object.
Implementation approach
- Complete a subscription-publish pattern
- rewrite
history.pushState
.history.replaceState
- Add message notifications (create event-bus to implement notifications)
- Trigger event subscription function execution
Subscription-publish Demo
Constructor (name){this.id = new Date(); this.subs = [] // Collection of objects under this event} defined(){// Add subscriber dep.watch.add (this); } this.subs.foreach ((e, I)=>{if(typeof e.update === 'function'){try{e.update.apply(e); if(typeof e.update === 'function'){try{e.update.apply(e); // Trigger subscriber update function}catch(err){console.warr(err); } } }) } } Dep.watch = null; class Watch{ constructor(name,fn){ this.name = name; This.id = new Date(); This.callback = fn; } add(dep){// Put the subscriber into the DEP subscription pool dep.subs.push(this); } update(){var cb = this.callback; // This cb(this.name) is called in order not to change the function; }}Copy the code
Rewrite the history method, and add window. AddHistoryListener event mechanism
Now we just need to rewrite the history method and add event-bus, the code is as follows:
var addHistoryMethod = (function(){ var historyDep = new Dep(); return function(name){ if(name === 'historychange'){ return function(name,fn){ var event = new Watch(name,fn) Dep.watch = event; historyDep.defined(); Dep.watch = null; / / empty for the use of a subscriber}} else if (name = = = 'pushState' | | name = = = 'replaceState') {var method = history [name]. return function(){ method.apply(history,arguments); historyDep.notify(); }}}} ()); window.addHistoryListener = addHistoryMethod('historychange'); history.pushState = addHistoryMethod('pushState'); history.replaceState = addHistoryMethod('replaceState');Copy the code
Test the History event listener
We added an addHistoryListener event listener to the window, similar to the addEventListener method, and then we overwrote the pushState and replaceState history methods.
Window.addeventlistener ('history',function(){console.log(' window history changed ') console.log(' current page link is: ',window.location.href); }) history.pushState({first:'first'},"page2","/first");Copy the code
Observe the above results printed; We noticed that the history of window changed and we successfully added event listener! As mentioned in the article, the current implementation is flawed in that it lacks event removal. Subsequent tests apply this method to multiple pages, but this method is limited and requires further compatibility.
Reference:
The front-end routing
How do I listen for route changes? SPA implementation principle and DEMO
Design pattern: subscription-publisher pattern