Code前端首页关于Code前端联系我们

Vuex 4 指南,Vue3 必看!

terry 2年前 (2023-09-08) 阅读数 139 #Vue

作者:Anthony Gore
译者:前端小智
来源:vuejsdevelopers

如果你有梦想和有用的信息,就微信搜索【向世界大运动】遵循这个凌晨还在洗洗碗机的洗碗机智慧。

文章收录在GitHub github.com/qq449245884...。里面有完整的面试考点、资料以及我针对一线厂商的系列文章。

Vuex是Vue.js生态系统中必不可少的工具。但是,刚接触 Vuex 的开发人员可能会对“状态管理模式”之类的术语感到厌烦,并且对他们在 Vuex 中实际需要的东西感到困惑。

这篇文章是对Vuex的介绍。当然,它还将涵盖 Vuex 的高级概念,并展示如何在您的应用程序中使用 Vuex。

Vuex 解决的问题

要了解Vuex,首先要了解它要解决的问题。

假设我们正在开发一个多用户聊天应用程序。该界面有一个用户列表、一个私人聊天窗口、一个包含聊天历史记录的收件箱以及一个通知栏,通知用户当前未查看的其他用户的未读消息。

每天有数百万用户通过我们的应用程序与数百万其他用户聊天。然而,有些人抱怨一个恼人的问题:通知栏有时会给出错误的通知。用户会收到新的未读消息的通知,但当他们看到该消息时,这只是一条已查看过的消息。

作者描述的是几年前Facebook开发者在他们的聊天系统中遇到的真实情况。为了解决这个问题,开发人员创建了一个名为“Flux”的应用程序架构。 Flux 构成了 Vuex、Redux 和其他类似库的基础。

流量

Facebook 开发者一段时间以来一直在努力解决“僵尸通知”问题。他们最终意识到,它的持久性不仅仅是一个简单的错误,它指出了应用程序架构中的一些潜在缺陷。

这个错误在抽象中最容易理解:当应用程序中有多个共享数据的组件时,它们互连的复杂性会增加到无法预测或理解数据状态的程度。因此,应用程序无法扩展或维护。

Flux 是一个模式,而不是一个库。我们无法去Github下载流程。它是一种类似于MVC的设计模式。 Vuex 和 Redux 等库实现流模式的方式与其他框架实现 MVC 模式的方式相同。

事实上,Vuex 并没有实现所有的流程,而只是实现了其中的一个子集。不过现在不用担心,让我们专注于理解它遵循的关键原则。

原则 1:单一来源

组件可以拥有只有它们需要知道的本地数据。例如,用户列表组件中滚动条的位置可能与其他组件不相关。

但是,组件之间共享的任何数据(即应用程序数据)必须保存在单独的位置,与使用它的组件分开。

这个单一的地方被称为“商店”。组件必须从此位置读取应用程序数据,并且不能保留自己的副本,以避免冲突或分歧。

import { createStore } from "vuex";

// Instantiate our Vuex store
const store = createStore({

  // "State" 组件的应用程序数据
  
  state () {
    return {
      myValue: 0
    };
  }
});

// 组件从其计算的属性访问 state 
const MyComponent = {   
  template: "<div>{{ myValue }}</div>",
  computed: {
    myValue () {
      return store.state.myValue;
    }   
  } 
};
 

原理2:数据只读

组件可以自由地从store读取数据。但store中的数据无法更改,至少不能立即更改。

相反,他们必须告知 store 更改数据的意图,并且 store 负责通过一组定义的函数(称为 mutation)进行更改。

为什么采用这种方法?如果我们把数据变更逻辑集中起来,那么如果状态不一致,我们只需要在同一个地方去检查,而不需要去每个具体的文件。我们最大限度地减少某些随机组件(可能在第三方模块中)以意外方式更改数据的可能性。

const store = createStore({ 
  state() { 
    return {
      myValue: 0
    };
  }, 
  mutations: { 
    increment (state, value) { 
      state.myValue += value;
    }
  } 
});
// 需要更新值吗?
// 错误的,不要直接更改 store 值
store.myValue += 10;
// 正确的,调用正确的 mutations。
store.commit('increment', 10);
 

原则 3:突变是同步的

如果应用程序在其架构中实现上述原则,文件不一致调试就会容易得多。可以记录提交并查看状态如何变化(如果您使用 Vue Devtools 确实可以做到这一点)。

但是当我们的mutation被异步调用时,这个能力就被削弱了。我们知道承诺的顺序,但不知道组件提交它们的顺序。

同步mutation 确保状态不依赖于不可预测事件的顺序和时间。

太酷了,那么 Vuex 到底是什么?

有了所有这些背景知识,我们终于可以解决这个问题了 - Vuex 是一个帮助我们在 Vue 应用程序中实现 Flux 架构的库。通过实现上述原则,Vuex 使我们的应用程序数据保持透明且可预测的状态,即使数据在不同组件之间共享也是如此。

现在我们对 Vuex 有了深入的了解,让我们看看如何创建基于 Vuex 的应用程序和实际项目。

// src/components/TodoNew.vue

<template>
  <form @submit.prevent="addTodo">
    <input
      type="text"
      placeholder="Enter a new task"
      v-model="task"
    />
  </form>
</template>
 
制作一个 Vuex 待办事项列表

为了演示 Vuex 的使用,我们设置了一个易于使用的应用程序。您可以在此处访问代码的工作示例。

示例地址:codesandbox.io/s/happy-wil…

如果您尝试使用自己的计算机,可以使用以下命令:

vue create vuex-example
 

安装Vuex

cd vuex-example
npm i -S vuex@4
npm run serve
 

创建一个 Vuex 存储

现在,要创建 Vuex Store,请在项目中创建 src/store/index.js

mkdir src/store
touch src/store/index.js
 

打开文件并导入 createStore 方法。该方法用于定义store及其属性。现在我们导出该 store,以便我们可以在 Vue 应用程序中获取它。

// src/store/index.js

import { createStore } from "vuex";

export default createStore({});
 

添加商店以查看实例

要从任何组件访问 Vuex 存储,我们需要在主文件中导入模块 store 并将 store 作为插件安装在主 Vue 实例上

// src/main.js

import { createApp } from "vue";
import App from "@/App";
import store from "@/store";

const app = createApp(App);

app.use(store);

app.mount("#app");
 

创建一个简单的应用程序

如上所述,Vuex 的重点是通常在大型应用程序中创建可扩展的全局状态。但是,我们可以在一个简单的应用程序中演示其功能。

完成后效果如下:

3108827612-608dfb82a0450_fix732.png3108827612-608dfb82a0450_fix732.png

现在删除 HelloWorld 文件:

rm src/components/HelloWorld.vue
 

TodoNew.vue

现在添加一个新组件 TodoNew 负责创建新的待办事项。

touch src/components/TodoNew.vue
 

打开 TodoNew.vue 并输入以下内容:

// src/components/TodoNew.vue

<template>
  <form @submit.prevent="addTodo">
    <input
      type="text"
      placeholder="Enter a new task"
      v-model="task"
    />
  </form>
</template>
 

现在转到组件定义,有两个本地状态属性 - taskid 为新待办事项提供唯一标识符。

定义存储状态

稍后我们将创建一个显示 Todos 的组件。由于它和 TodoNew 组件访问相同的数据,因此这是我们存储在 Vuex 存储中的全局 state 的理想候选者。

现在,返回state 并定义属性状态。这里我们使用 Vux4 提供的函数createStore,它返回一个对象。该对象有一个属性 todos,它是一个空数组。

// src/store/index.js
import { createStore } from "vuex";

export default createStore({
  state () {
    return {
      todos: []
    }
  }
});
 

定义突变

由原理2可知,Vuex状态不能直接改变,必须定义mutator函数。

现在我们为store添加一个属性,以及一个函数属性addTodo。所有 mutator 方法的第一个参数。第二个可选参数是store,第二个是要传递的数据。

// src/store/index.js

import { createStore } from "vuex";

export default createStore({
  state () {
    return {
      todos: []
    }
  },
  mutations: {
    addTodo (state, item) {
      state.todos.unshift(item);
    }
  }
});
 

使用engage来呼叫mutation

现在,您可以通过在 TodoNew 组件中定义 addTodo 方法来在 TodoNew 组件中使用它。

要访问store,我们可以使用全局属性this.$store。使用 commit 方法创建一个新的 mutation。必须传递两个参数 - 首先是mutation的名称,其次是我们要传递的对象,这是一个新的待办事项(由值idtask组成)。

// src/components/TodoNew.vue
methods: {
  addTodo: function() {
    const { id, task } = this;
    this.$store.commit("addTodo", { id, task });
    this.id++;
    this.task = "";
  }
}
 

回顾task

到目前为止:

  1. 用户通过输入框输入待办事项,并绑定到task变量。
  2. 提交表单后,调用addTodo方法
  3. 创建一个待办事项对象并将其“发送”到store
// src/components/TodoNew.vue
<template>
  <form @submit.prevent="addTodo">
    <input
      type="text"
      placeholder="Enter a new task"
      v-model="task"
    />
  </form>
</template>
<script>
export default {
  data() {
    return {
      task: "",
      id: 0
    };
  },
  methods: {
    addTodo: function() {
      const { id, task } = this;
      this.$store.commit("addTodo", { id, task });
      this.id++;
      this.task = "";
    }
  }
};
</script>
 

读取存储数据

现在我们已经创建了做事情的功能。下一步是向他们展示。

创建一个新组件 TodoList.vue 文件。

touch src/components/TodoList.vue
 

读作如下:

// src/components/TodoList.vue
<template>
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.description }}
  </li>
</ul>
</template>
 

todos 是一个计算属性,我们在其中返回 Vuex 存储的内容。

// src/components/TodoList.vue

<script>
export default {
  computed: {
    todos() {
      // 
    }
  }
};
</script>
 

定义吸气剂

与直接访问store的内容相比,getter是一个类似于存储的计算属性的函数。这些工具非常适合在将数据返回到应用程序之前过滤或转换数据。

例如,下面是getTodos,它返回未过滤的状态。在许多情况下,此内容可以转换为 filtermap

todoCount 返回 todo 数组的长度。

通过确保组件愿意保留数据的本地副本,getter 有助于原则 1,单一事实来源。

// src/store/index.js

export default createStore({
  ...
  getters: {
    getTodos (state) {
      return state.todos;
    },
    todoCount (state) {
      return state.todos.length;
    }
  }
})
 

返回组件TodoList,我们通过返回this.$store.getters.getTodos来完成函数。

// src/components/TodoList.vue

<script>
export default {
  computed: {
    todos() {
      return this.$store.getters.getTodos;
    }
  }
};
</script>
 

App.vuestore

要完成此应用程序,我们现在需要做的就是在App.vue中导入并声明我们的组件。

// src/App.vue

<template>
  <div>
    <h1>To-Do List</h1>
    <div>
      <TodoNew />
      <TodoList />
    </div>
  </div>
</template>
<script>
import TodoNew from "@/components/TodoNew.vue";
import TodoList from "@/components/TodoList.vue";

export default {
  components: {
    TodoNew,
    TodoList
  }
};
</script>
 

你真的需要Vuex吗?

显然,在大型应用程序中,拥有全局状态管理解决方案有助于使我们的应用程序可预测和可维护。

但是对于比较小的项目,有时候我觉得用Vuex有点大材小用,大家按照实际需求来比较合理。

Vuex 优点:

  • 易于管理全局状态
  • 强大的调试全局状态

Vuex 缺点:

  • 其他项目依赖项
  • 困难的模板

~结束了,我是支万智,我们洗碗吧,下次再见!


原文:vuejsdevelopers.com/2017/05/15/…

代码部署后存在的Bug无法实时披露。事后为了解决这些BUG,花费了大量的时间在日志调试上。顺便推荐一下Fundebug,一款好用的BUG监控工具。

通讯

有梦想,有干货,微信搜索【走向世界的大招】跟随这条清晨还在​​洗刷的水槽智慧。

这篇文章GitHub github.com/qq449245884...已收录,里面有完整的测试场地、资料以及我采访一线厂商的系列文章。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门