0. Background:

In the mixed development of mobile terminal, 90% of the content in the APP is H5 embedded. Due to various reasons, I could not use single-page route to jump in the client, but had to open a new window to jump to the page, so I was forced to form the situation of “multi-page”. (Even if it’s a coherent page)

1. Requirement scenario

For example, when you are in a list and click an item to jump to the details and change the state of the item, the list also needs to change the state of the item synchronously. (At this point the list and details are two WebViews)

Final effect :(use two tabs to simulate the real machine)

2. Question thinking

The first thing that comes to mind is the onShow() life cycle of the small program. Our APP also provides a similar protocol method, which means that the H5 page can be monitored to return to the page. However, this method relies on native, and data communication cannot be achieved under normal circumstances. Rerequesting new data when the page returns will cause the entire page to refresh.

3. Final solution

LocalStorage is one of the most commonly used data persistent storage schemes in my front-end development. In the case of homology, it can be completely used as a data communication bridge between multiple pages. I decided to adopt publish and subscribe mode and write an eventBus to decouple the communication between multiple pages. The window native event listener acts as the event stack, listening for changes to localStorage and broadcasting the corresponding action. Here’s how I did it.

4. Implementation code

Create storagebus.js file:

let effects = [];

function depend(obj) { // Collect dependencies
  effects.push(obj);
}
function notify(key, params) { // Perform dependencies
  const fnList = effects.filter(x= > x.name === key);
  fnList.forEach(list= > list.fn(JSON.parse(params).data)); // Only data is needed here
}

export default {
  $emit(name, data) {
    {data: any but not function, timeStamp: XXX
    let item = localStorage.getItem(name) || "{}";
    try {
      item = JSON.parse(item);
      item.data = data
    } catch (e) {
      item = {};
    }
    item.timeStamp = new Date().getTime();
    localStorage.setItem(name, JSON.stringify(item)); }, $on(name, fn) { depend({ name, fn }); }};// Event stack:
window.addEventListener(
  "storage".e= > { // e.key is the triggered method, and e.newvalue is the updated parameter
    notify(e.key, e.newValue);
  }, false );
Copy the code

Introduce storageBus in a toolset file utils.js

import storageBus from "./storageBus";
export default {
  install(myVue){ myVue.prototype.$bus = storageBus; }}Copy the code

Import the utility set utils.js from the Vue entry file (usually main) and configure vue.use (utils);

This calls storageBus globally using this.$bus;

5. Use

List. Vue Register events in the List:

mounted() {
    this.$bus.$on('effect'.(data) = > {
      // TODO ... data.id ...})},Copy the code

Detail. Vue list jump details page send information under a condition:

this.$bus.$emit('effect', { id: xxx })
Copy the code

Yes, it works the same way as Vue’s own eventBus, event broadcast, decoupled god.

6. Unbind events

Vue’s eventBus provides the off method for unbinding events. We also need to implement a unbinding method, otherwise the page will repeatedly register events after entering for many times. However, we also consider the situation of double registration events, so deleting a specific registration event requires two criteria: 1. The off method needs to pass in the anonymous function registered by the ON method. To solve this problem gracefully, we can make the ON method spit out the off method after the call, so as to cancel the corresponding event.

Storagebus.js new code:

let effects = [];

function depend(obj) { // Collect dependencies. }function notify(key, params) { // Perform dependencies. }export default {
  $emit(name, data) { ... },
  
  $on(name, fn) {
    depend({ name, fn });
    return () = > {
      this.$off(name, fn);
    };
  },
  
  $off(name, fn) {
    const fnList = effects.filter(x= > x.name === name);
    effects = fnList.filter(x= >x.fn ! == fn);if (effects.findIndex(x= > x.name === name) === -1) { // Clear the data if there is no corresponding function at all
      localStorage.removeItem(name)
    }
  }
};

window.addEventListener( ... ) ;Copy the code

Register events in the list. vue List and unregister them when the page is destroyed:

mounted() {
    this.storageBusOff = this.$bus.$on('effect'.(data) = > {
      // TODO ... data.id ...})},beforeDestroy() {
    this.storageBusOff(); / / destroyed
  }
Copy the code

7. Real compatibility

After testing, the monitoring of storage in Ios does not take effect, which is very regrettable, but it is normal in Android system, but I found in the accident, Ios and Android webView is not the same in the page jump, Ios WebView can normally carry out web page forward and backward, Android returns with the page closed directly, but in the end I used the judgment environment to use different page jumps to communicate with messages:

Utility functions:

/** * Judge ios, the entire ecosystem, including computers, phones and tablets */
export const isIOS = () = > {
  var u = navigator.userAgent;
  varisiOS = !! u.match(/\(i[^;] +; ( U;) ? CPU.+Mac OS X/); / / ios terminal
  return isiOS;
};
Copy the code

Modify the above toolset file utils.js:

myVue.prototype.$bus = utils.isIOS() ? new Vue() : storageBus; Vue event broadcast and cross-page event broadcast
Copy the code
import router from ".. /router/index";
// Defines its own route, which is the same as vue-router
myVue.prototype.$myRouter = (params, title = null) = > {
      if(! utils.isIOS()) {const path = params.path ? params.path : params.name ? ` /${params.name}` : "";
        let query = "?";
        for (const key in params.query) {
          if (Object.hasOwnProperty.call(params.query, key)) {
            query += `${key}=${params.query[key]}& `;
          }
        }
        query = query.substring(0, query.length - 1);
        xxxxx.appClient({ // This is the open page protocol defined by mixed development
          type: "openPage".data: { url: baseUrl + "saas/dangjian/" + ` #${path}` + query }
        });
      } else { // Use a normal routerouter.push(params); }};Copy the code

Vue file:

// There is no difference between the two types of event communication, the only difference is the logout:
beforeDestroy() {
    this.$bus.$off('effect')
    this.storageBusOff instanceof Function && this.storageBusOff();
  }
Copy the code

The judgment function isIOS determines the entire Apple ecosystem, while the Apple system adopts vue-Router routing and not cross-page communication. However, my development computer is MAC, so the whole development process is relatively smooth. Web debugging and development of the basic effect of the original to re-confirm again, only after the release of test.