搜索
首页web前端css教程如何使局部存储在VUE中反应性

Vue.js 的响应式系统是其核心优势之一,但对于不了解其底层机制的人来说,可能会感到神秘。例如,为什么它能与对象和数组一起工作,却不能与其他东西(如 localStorage)一起工作?本文将解答这个问题,并演示如何让 Vue 的响应式系统与 localStorage 协同工作。

How to Make localStorage Reactive in Vue

如果运行以下代码,你会发现计数器显示为静态值,不会像预期的那样因间隔改变 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);

虽然 Vue 实例内的 counter 属性是响应式的,但仅仅因为我们在 localStorage 中更改了它的来源,它并不会改变。

解决这个问题的方法有很多,最好的方法可能是使用 Vuex 并保持存储值与 localStorage 同步。但如果我们只需要像这个例子中一样简单的解决方案呢?我们必须深入了解 Vue 的响应式系统的工作原理。

Vue 中的响应式

当 Vue 初始化组件实例时,它会观察 data 选项。这意味着它会遍历 data 中的所有属性,并使用 Object.defineProperty 将它们转换为 getter/setter。通过为每个属性设置自定义 setter,Vue 就能知道属性何时发生变化,并可以通知需要对变化做出反应的依赖项。它如何知道哪些依赖项依赖于某个属性?通过利用 getter,它可以在计算属性、观察者函数或渲染函数访问数据属性时进行注册。

// 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>那么,为什么 localStorage 不是响应式的呢?<strong>因为它不是具有属性的对象。</strong></p>
<p>但是等等。我们也不能用数组定义 getter 和 setter,但在 Vue 中数组仍然是响应式的。这是因为数组在 Vue 中是一个特例。为了拥有响应式数组,Vue 在幕后重写了数组方法,并将它们与 Vue 的响应式系统整合在一起。</p>
<p>我们可以对 localStorage 做类似的事情吗?</p>
<h3 id="重写-localStorage-函数">重写 localStorage 函数</h3>
<p>首先,我们可以通过重写 localStorage 方法来跟踪哪些组件实例请求了 localStorage 项目,从而修复我们最初的示例。</p>
<p>// localStorage 项目键与依赖它的 Vue 实例列表之间的映射
const storeItemSubscribers = {};</p>
<p>const getItem = window.localStorage.getItem;
localStorage.getItem = (key, target) => {
console.info("Getting", key);</p>
<p>// 收集依赖的 Vue 实例
if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
if (target) storeItemSubscribers[key].push(target);</p>
<p>// 调用原始函数
return getItem.call(localStorage, key);
};</p>
<p>const setItem = window.localStorage.setItem;
localStorage.setItem = (key, value) => {
console.info("Setting", key, value);</p>
<p>// 更新依赖 Vue 实例中的值
if (storeItemSubscribers[key]) {
storeItemSubscribers[key].forEach((dep) => {
if (dep.hasOwnProperty(key)) dep[key] = value;
});
}</p>
<p>// 调用原始函数
setItem.call(localStorage, key, value);
};</p>
<p>// ... (其余代码与原文相同)</p>
<p>在这个例子中,我们重新定义了 <code>getItem</code> 和 <code>setItem</code>,以便收集和通知依赖 localStorage 项目的组件。在新的 <code>getItem</code> 中,我们记录了哪个组件请求了哪个项目,在 <code>setItem</code> 中,我们联系所有请求该项目并重写其数据属性的组件。</p>
<p>为了使上面的代码工作,我们必须将对组件实例的引用传递给 <code>getItem</code>,这会改变它的函数签名。我们也不能再使用箭头函数,否则我们将不会有正确的 <code>this</code> 值。</p>
<p>如果我们想做得更好,我们必须更深入地挖掘。例如,我们如何在不显式地传递它们的情况下跟踪依赖项?</p>
<h3 id="Vue-如何收集依赖项">Vue 如何收集依赖项</h3>
<p>为了获得灵感,我们可以回到 Vue 的响应式系统。我们之前看到,当访问数据属性时,数据属性的 getter 会将调用者订阅到该属性的进一步更改。但它如何知道是谁进行了调用?当我们获取数据属性时,它的 getter 函数没有任何关于调用者是谁的输入。Getter 函数没有输入。它如何知道将谁注册为依赖项?</p>
<p>每个数据属性都维护一个其依赖项列表,这些依赖项需要在一个 <code>Dep</code> 类中做出反应。如果我们更深入地研究这个类,我们可以看到,每当注册依赖项时,依赖项本身就已经在一个静态目标变量中定义了。这个目标是由一个迄今为止神秘的 <code>Watcher</code> 类设置的。事实上,当数据属性发生变化时,这些观察者将被实际通知,它们将启动组件的重新渲染或计算属性的重新计算。</p>
<p>但是,再说一次,它们是谁?</p>
<p>当 Vue 使数据选项可观察时,它还会为每个计算属性函数以及所有观察函数(不应与 <code>Watcher</code> 类混淆)和每个组件实例的渲染函数创建观察者。观察者就像这些函数的伴侣。它们主要做两件事:</p>
<ol>
<li>
<strong>它们在创建时评估函数。</strong>这会触发依赖项的收集。</li>
<li>
<strong>当它们被通知到它们依赖的值已更改时,它们会重新运行其函数。</strong>这最终将重新计算计算属性或重新渲染整个组件。</li>
</ol>
<p>在观察者调用它们负责的函数之前,会发生一个重要的步骤:<strong>它们在 <code>Dep</code> 类中的静态变量中将自身设置为目标。</strong>这确保了当访问响应式数据属性时,它们会被注册为依赖项。</p>
<h3 id="跟踪谁调用了-localStorage">跟踪谁调用了 localStorage</h3>
<p>我们不能完全做到这一点,因为我们无法访问 Vue 的内部机制。但是,我们可以使用 Vue 的思想,让观察者在调用它负责的函数之前,在一个静态属性中设置目标。我们可以在调用 localStorage 之前设置对组件实例的引用吗?</p>
<p>如果我们假设在设置数据选项时调用了 localStorage,那么我们可以挂接到 <code>beforeCreate</code> 和 <code>created</code>。这两个钩子在初始化数据选项之前和之后触发,因此我们可以设置,然后清除一个具有对当前组件实例的引用的目标变量(我们可以在生命周期钩子中访问它)。然后,在我们的自定义 getter 中,我们可以将此目标注册为依赖项。</p>
<p>我们必须做的最后一件事是使这些生命周期钩子成为我们所有组件的一部分。我们可以为整个项目使用全局 mixin 来做到这一点。</p>
<p>// ... (其余代码与原文相同)</p>
<p>现在,当我们运行初始示例时,我们将得到一个每秒增加数字的计数器。</p>
<p>// ... (其余代码与原文相同)</p>
<h3 id="我们的思想实验的结束">我们的思想实验的结束</h3>
<p>虽然我们解决了最初的问题,但请记住,这主要是一个思想实验。它缺少一些功能,例如处理已删除的项目和已卸载的组件实例。它也有一些限制,例如组件实例的属性名称需要与存储在 localStorage 中的项目名称相同。也就是说,主要目标是更好地了解 Vue 响应式系统在幕后是如何工作的,并充分利用它,所以我希望这就是你从这一切中获得的东西。</p>

以上是如何使局部存储在VUE中反应性的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
什么是CSS网格?什么是CSS网格?Apr 30, 2025 pm 03:21 PM

CSS网格是创建复杂,响应式Web布局的强大工具。它简化了设计,提高可访问性并提供了比旧方法更多的控制权。

什么是CSS Flexbox?什么是CSS Flexbox?Apr 30, 2025 pm 03:20 PM

文章讨论了CSS FlexBox,这是一种布局方法,用于有效地对齐和分布响应设计中的空间。它说明了FlexBox用法,将其与CSS网格进行了比较,并详细浏览了浏览器支持。

我们如何使用CSS使网站迅速响应?我们如何使用CSS使网站迅速响应?Apr 30, 2025 pm 03:19 PM

本文讨论了使用CSS创建响应网站的技术,包括视口元标签,灵活的网格,流体媒体,媒体查询和相对单元。它还涵盖了使用CSS网格和Flexbox一起使用,并推荐CSS框架

CSS盒装属性有什么作用?CSS盒装属性有什么作用?Apr 30, 2025 pm 03:18 PM

本文讨论了CSS盒装属性,该属性控制了元素维度的计算方式。它解释了诸如Content-Box,Border-Box和Padding-Box之类的值,以及它们对布局设计和形式对齐的影响。

我们如何使用CSS动画?我们如何使用CSS动画?Apr 30, 2025 pm 03:17 PM

文章讨论使用CSS,关键属性并与JavaScript结合创建动画。主要问题是浏览器兼容性。

我们可以使用CSS向我们的项目添加3D转换吗?我们可以使用CSS向我们的项目添加3D转换吗?Apr 30, 2025 pm 03:16 PM

文章讨论了Web项目的3D转换,关键属性,浏览器兼容性和性能注意事项的讨论。(角色计数:159)

我们如何在CSS中添加梯度?我们如何在CSS中添加梯度?Apr 30, 2025 pm 03:15 PM

文章讨论了使用CSS梯度(线性,径向,重复)来增强网站视觉效果,添加深度,焦点和现代美学。

CSS中的伪元素是什么?CSS中的伪元素是什么?Apr 30, 2025 pm 03:14 PM

文章讨论了CSS中的伪元素,它们在增强HTML样式方面的使用以及与伪级的差异。提供实用的例子。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具