<div id="counter"></div>
<div id="get-toggle"></div>
<div id="set-toggle"></div>
@import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap");

html {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-family: "Montserrat", sans-serif;
}

.component {
  border: 1px solid lightgray;
  box-shadow: 5px 5px #ddd;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 70px;
  margin: 20px;
  padding: 15px 20px;
  position: relative;
  width: 200px;
}

.component::before {
  content: "Independent Vue Component";
  color: gray;
  font-size: 0.5em;
  position: absolute;
  top: 10px;
}

.component::after {
  content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='17' height='20' stroke='gray' stroke-width='1' fill='white'><rect x='5' y='0' width='12' height='20' /><rect x='0' y='4' width='8' height='4'/><rect x='0' y='12' width='8' height='4'/></svg>");
  position: absolute;
  right: 10px;
  top: 10px;
}
localStorage.setItem("toggle", true);
localStorage.setItem("counter", 0);

// A map between localStorage item keys and a list of Vue instances that depend on it
const storeItemSubscribers = {};

// The Vue instance that is currently being initialised
let target = undefined;

const getItem = window.localStorage.getItem;
localStorage.getItem = (key) => {
  console.info("Getting", key);

  // Collect dependent Vue instance
  if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
  if (target) storeItemSubscribers[key].push(target);

  // Call the original function
  return getItem.call(localStorage, key);
};

const setItem = window.localStorage.setItem;
localStorage.setItem = (key, value) => {
  console.info("Setting", key, value);

  // Update the value in the dependent Vue instances
  if (storeItemSubscribers[key]) {
    storeItemSubscribers[key].forEach((dep) => {
      if (dep.hasOwnProperty(key)) dep[key] = value;
    });
  }

  // Call the original function
  setItem.call(localStorage, key, value);
};

Vue.mixin({
  beforeCreate() {
    console.log("beforeCreate", this._uid);
    target = this;
  },
  created() {
    console.log("created", this._uid);
    target = undefined;
  }
});

new Vue({
  el: "#counter",
  data: () => ({
    counter: localStorage.getItem("counter")
  }),
  computed: {
    even() {
      return this.counter % 2 == 0;
    }
  },
  template: `<div class="component">
    <div>Counter: {{ counter }}</div>
    <div>Counter is {{ even ? 'even' : 'odd' }}</div>
  </div>`
});

new Vue({
  el: "#get-toggle",
  data: () => ({
    toggle: localStorage.getItem("toggle")
  }),
  template: `<div class="component">
    <div>Toggle: {{ toggle }}</div>
  </div>`
});

new Vue({
  el: "#set-toggle",
  data: () => ({
    toggle: localStorage.getItem("toggle")
  }),
  beforeCreate() {
    console.log("Component specific beforeCreate");
  },
  created() {
    console.log("Component specific created");
  },
  methods: {
    click() {
      localStorage.setItem("toggle", !!this.toggle ? false : true);
    }
  },
  template: `<div class="component">
    <div>
      <button @click="click">Toggle</button>
    </div>
  </div>`
});

const intervalID = setInterval(() => {
  const counter = +localStorage.getItem("counter");
  localStorage.setItem("counter", counter + 1);
}, 1000);

window.onbeforeunload = function () {
  window.clearInterval(intervalID);
};

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js