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
  • rewritehistory.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