search
HomeWeb Front-endCSS TutorialHow to Make localStorage Reactive in Vue

Vue.js' responsive system is one of its core strengths, but it may feel mysterious to anyone who doesn't understand its underlying mechanisms. For example, why does it work with objects and arrays but not with other things like localStorage? This article will answer this question and demonstrate how to get Vue's responsive system to work with localStorage.

How to Make localStorage Reactive in Vue

If you run the following code, you will find that the counter is displayed as a static value and will not change as expected due to interval changes in localStorage:

 new Vue({
  el: "#counter",
  data: () => ({
    counter: localStorage.getItem("counter")
  }),
  computed: {
    Even() {
      return this.counter % 2 == 0;
    }
  },
  template: `<div>
    <div>Counter: {{ counter }}</div>
    <div>Counter is {{ even ? 'even' : 'odd' }}</div>
  </div>`
});
 // some-other-file.js
setInterval(() => {
  const counter = localStorage.getItem("counter");
  localStorage.setItem("counter", counter 1);
}, 1000);

Although counter property within the Vue instance is responsive, it does not change just because we changed its source in localStorage.

There are many ways to solve this problem, and the best way is probably to use Vuex and keep the stored values ​​synchronized with localStorage. But what if we just need a solution as simple as in this example? We must have an in-depth understanding of how Vue's responsive systems work.

Responsiveness in Vue

When Vue initializes a component instance, it observes data options. This means it will iterate over all properties in data and convert them to getter/setter using Object.defineProperty . By setting a custom setter for each property, Vue can know when the property changes and can notify dependencies that need to react to the change. How does it know which dependencies depend on a certain property? By leveraging getters, it can be registered when computed properties, observer functions, or rendering functions access data properties.

 // core/instance/state.js
function initData () {
  // ...
  observe(data)
}
 // core/observer/index.js
export function observe (value) {
  // ...
  new Observer(value)
  // ...
}

export class Observer {
  // ...
  constructor (value) {
    // ...
    this.walk(value)
  }

  walk (obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i <p> So, why is localStorage not responsive? <strong>Because it is not an object with attributes.</strong></p><p> But wait. We can't define getters and setters with arrays, but in Vue, arrays are still responsive. This is because arrays are a special case in Vue. To have responsive arrays, Vue rewritten the array methods behind the scenes and integrated them with Vue's responsive system.</p><p> Can we do something similar to localStorage?</p><h3 id="Rewrite-localStorage-function"> Rewrite localStorage function</h3><p> First, we can fix our initial example by overriding the localStorage method to track which component instances requested the localStorage project.</p><p> // Map between localStorage project key and the list of Vue instances that depend on it const storeItemSubscribers = {};</p><p> const getItem = window.localStorage.getItem; localStorage.getItem = (key, target) => { console.info("Getting", key);</p><p> // Collect dependent Vue instances if (!storeItemSubscribers[key]) storeItemSubscribers[key] = []; if (target) storeItemSubscribers[key].push(target);</p><p> // Call the original function return getItem.call(localStorage, key); };</p><p> const setItem = window.localStorage.setItem; localStorage.setItem = (key, value) => { console.info("Setting", key, value);</p><p> // Update the value in the Vue instance that depends on if (storeItemSubscribers[key]) { storeItemSubscribers[key].forEach((dep) => { if (dep.hasOwnProperty(key)) dep[key] = value; }); }</p><p> // Call the original function setItem.call(localStorage, key, value); };</p><p> // ... (The rest of the code is the same as the original text)</p><p> In this example, we redefined <code>getItem</code> and <code>setItem</code> to collect and notify components that depend on localStorage projects. In the new <code>getItem</code> we record which component requests which item, and in <code>setItem</code> we contact all components that request the item and rewrite its data properties.</p><p> In order for the above code to work, we have to pass a reference to the component instance to <code>getItem</code> , which changes its function signature. We can't use the arrow function anymore, otherwise we won't have the correct value of <code>this</code> .</p><p> If we want to do better, we have to dig deeper. For example, how do we keep track of dependencies without explicitly passing them?</p><h3 id="How-to-collect-dependencies-in-Vue"> How to collect dependencies in Vue</h3><p> For inspiration, we can go back to Vue’s responsive system. We have seen before that when accessing a data property, the getter of the data property subscribes the caller to further changes to that property. But how does it know who made the call? When we get the data attribute, its getter function has no input about who the caller is. The Getter function has no input. How does it know who to register as a dependency?</p><p> Each data attribute maintains a list of its dependencies that need to react in a <code>Dep</code> class. If we dig deeper into this class, we can see that whenever a dependency is registered, the dependency itself is already defined in a static target variable. This goal is set by a mysterious <code>Watcher</code> class so far. In fact, when data properties change, these observers will be actually notified that they will initiate re-rendering of components or re-computing of computed properties.</p><p> But, again, who are they?</p><p> When Vue makes the data option observable, it also creates observers for each computed attribute function as well as all observation functions (which should not be confused with <code>Watcher</code> class) and the rendering function for each component instance. The observer is like a companion to these functions. They do two main things:</p><ol>
<li> <strong>They evaluate functions at creation time.</strong> This triggers the collection of dependencies.</li>
<li> <strong>When they are notified that the value they depend on has changed, they rerun their functions.</strong> This will eventually recalculate the computed properties or re-render the entire component.</li>
</ol><p> An important step occurs before the observer calls the functions they are responsible for: <strong>they set themselves as targets in static variables in <code>Dep</code> class.</strong> This ensures that when responsive data attributes are accessed, they are registered as dependencies.</p><h3 id="Track-who-called-localStorage"> Track who called localStorage</h3><p> We can't do this completely because we can't access Vue's internal mechanisms. However, we can use the idea of ​​Vue to let the observer set the target in a static property before calling the function it is responsible for. Can we set a reference to the component instance before calling localStorage?</p><p> If we assume that localStorage is called when setting data options, then we can hook to <code>beforeCreate</code> and <code>created</code> . These two hooks are fired before and after the initialization of the data option, so we can set it and then clear a target variable with a reference to the current component instance (we can access it in the lifecycle hook). Then, in our custom getter, we can register this target as a dependency.</p><p> The last thing we have to do is make these life cycle hooks a part of all our components. We can do this using global mixin for the entire project.</p><p> // ... (The rest of the code is the same as the original text)</p><p> Now, when we run the initial example, we will get a counter that increases the number per second.</p><p> // ... (The rest of the code is the same as the original text)</p><h3 id="The-end-of-our-thought-experiment"> The end of our thought experiment</h3><p> While we solved the initial problem, remember that this is mainly a thought experiment. It lacks some features, such as handling deleted projects and uninstalled component instances. It also has some limitations, such as the property name of the component instance needs to be the same as the project name stored in localStorage. That said, the main goal is to better understand how Vue responsive systems work behind the scenes and make the most of it, so I hope that's what you get from it all.</p>

The above is the detailed content of How to Make localStorage Reactive in Vue. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
What is CSS Grid?What is CSS Grid?Apr 30, 2025 pm 03:21 PM

CSS Grid is a powerful tool for creating complex, responsive web layouts. It simplifies design, improves accessibility, and offers more control than older methods.

What is CSS flexbox?What is CSS flexbox?Apr 30, 2025 pm 03:20 PM

Article discusses CSS Flexbox, a layout method for efficient alignment and distribution of space in responsive designs. It explains Flexbox usage, compares it with CSS Grid, and details browser support.

How can we make our website responsive using CSS?How can we make our website responsive using CSS?Apr 30, 2025 pm 03:19 PM

The article discusses techniques for creating responsive websites using CSS, including viewport meta tags, flexible grids, fluid media, media queries, and relative units. It also covers using CSS Grid and Flexbox together and recommends CSS framework

What does the CSS box-sizing property do?What does the CSS box-sizing property do?Apr 30, 2025 pm 03:18 PM

The article discusses the CSS box-sizing property, which controls how element dimensions are calculated. It explains values like content-box, border-box, and padding-box, and their impact on layout design and form alignment.

How can we animate using CSS?How can we animate using CSS?Apr 30, 2025 pm 03:17 PM

Article discusses creating animations using CSS, key properties, and combining with JavaScript. Main issue is browser compatibility.

Can we add 3D transformations to our project using CSS?Can we add 3D transformations to our project using CSS?Apr 30, 2025 pm 03:16 PM

Article discusses using CSS for 3D transformations, key properties, browser compatibility, and performance considerations for web projects.(Character count: 159)

How can we add gradients in CSS?How can we add gradients in CSS?Apr 30, 2025 pm 03:15 PM

The article discusses using CSS gradients (linear, radial, repeating) to enhance website visuals, adding depth, focus, and modern aesthetics.

What are pseudo-elements in CSS?What are pseudo-elements in CSS?Apr 30, 2025 pm 03:14 PM

Article discusses pseudo-elements in CSS, their use in enhancing HTML styling, and differences from pseudo-classes. Provides practical examples.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function