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.
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!

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

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.

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

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.

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

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

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

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


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

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

Hot Article

Hot Tools

WebStorm Mac version
Useful JavaScript development tools

Dreamweaver CS6
Visual web development tools

SublimeText3 Linux new version
SublimeText3 Linux latest version

SublimeText3 Mac version
God-level code editing software (SublimeText3)

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function
