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!

A fascinating new site called The Markup just launched. Tagline: Big Tech Is Watching You. We’re Watching Big Tech. Great work from Upstatement. The

I posted about parsing an RSS feed in JavaScript the other day. I also posted about my RSS setup talking about how Feedbin is at the heart of it.

Learn how to create a custom CodePen block with a preview for Sanity Studio, inspired by Chris Coyier’s implementation for Wordpress’ Gutenberg editor.

Line, bar, and pie charts are the bread and butter of dashboards and are the basic components of any data visualization toolkit. Sure, you can use SVG

We are always looking to make the web more accessible. Color contrast is just math, so Sass can help cover edge cases that designers might have missed.

Tartan is a patterned cloth that’s typically associated with Scotland, particularly their fashionable kilts. On tartanify.com, we gathered over 5,000 tartan

Not long ago, I posted about PHP templating in just PHP (which is basically HEREDOC syntax). I'm literally using that technique for some super basic

Have you ever clicked on an image on a webpage that opens up a larger version of the image with navigation to view other photos?


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

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SublimeText3 Linux new version
SublimeText3 Linux latest version

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SublimeText3 Chinese version
Chinese version, very easy to use

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.