Maison  >  Article  >  interface Web  >  Prenez Vuex comme exemple pour découvrir le mystère de la gestion de l'État

Prenez Vuex comme exemple pour découvrir le mystère de la gestion de l'État

藏色散人
藏色散人avant
2021-12-16 15:46:081831parcourir

Utiliser Vuex comme guide pour avoir un aperçu de l'ensemble de la gestion de l'état

Comme nous le savons tous, Vuex est le solution officielle de gestion d'état de Vue.

L'utilisation et l'API de Vuex ne sont pas difficiles, et l'introduction sur le site officiel est également concise et claire. Grâce à cela, il est très simple d’intégrer rapidement Vuex dans votre projet. Cependant, en raison de son utilisation flexible, de nombreux étudiants sont quelque peu confus quant à la conception et à l'utilisation de Vuex.

En fait, avant de l'utiliser, autant faire une pause et réfléchir à quelques questions :

  • Qu'est-ce que la gestion de l'État ?
  • Pourquoi devrais-je utiliser Vuex ?
  • Comment distribuer l'état interne des composants et l'état de Vuex ?
  • Quels sont les problèmes potentiels liés à l'utilisation de Vuex ?

Si vous êtes ambigu sur ces questions, alors félicitations, cet article est peut-être ce dont vous avez besoin.

Veuillez me rejoindre ci-dessous pour dévoiler le mystère de la gestion de l'État depuis l'origine, en utilisant Vuex comme exemple.

Aperçu des grandes lignes

Le contenu présenté dans cet article comprend les aspects suivants :

  • La naissance de l'État et de ses composants
  • La gestion de l'État doit-elle être gérée ?
  • Source de données unique
  • Méthode de mise à jour du statut
  • Mise à jour asynchrone ?
  • Modularisation d'état
  • Le slot de modularisation
  • La prochaine étape

La naissance de l'état et des composants

Depuis la naissance des trois frameworks majeurs, les deux capacités qu'ils partagent ont complètement frappé Jquery. Ces deux capacités sont :

  1. Vue basée sur les données
  2. Componentisation

La vue basée sur les données nous permet de dire adieu à l'époque où l'on ne pouvait compter que sur la manipulation du DOM pour mettre à jour les pages. Nous n'avons plus besoin de rechercher le DOM à travers des couches de recherche, puis de modifier ses propriétés et son contenu à chaque fois que nous mettons à jour la page. Nous pouvons faire ces choses en manipulant les données.

Bien sûr, aux yeux de notre front-end, les données peuvent fondamentalement être comprises comme le stockage de différents types de données 变量。在 数据驱动 Après l'émergence de ce concept, certaines variables ont également reçu une signification particulière.

Tout d'abord, il s'agit d'une variable ordinaire, qui n'est pas différente de l'ère JQ et qui n'est utilisée que pour stocker des données. De plus, il existe un type de variables qui ont un effet réactif. Ces variables sont liées à la vue. Lorsque les variables changent, la vue liée à ces variables déclenchera également les mises à jour correspondantes Variables d'état. .

La vue dite basée sur les données, à proprement parler, signifie que les variables d'état pilotent la vue. Avec la popularité de Vue et React, l'attention des développeurs front-end s'est progressivement déplacée du fonctionnement du DOM vers le fonctionnement des données, et les variables d'état sont devenues le noyau.

Variables d'état, maintenant tout le monde semble préférer les appeler

état. On parle souvent d’état et de gestion d’état. En fait, cet état fait référence à des variables d’état. L'état mentionné ci-dessous fait également référence aux variables d'état.

Après l'arrivée du statut, les composants arrivent également.

A l'ère JQ, une page front-end n'est qu'un html, et il n'y a pas de notion de "composant". Il n'est pas trop difficile de réutiliser avec élégance les parties publiques de la page. Heureusement, les trois principaux frameworks ont abouti à des conceptions de composants très matures. Il est facile d'extraire un fragment DOM en tant que composant, et le composant peut conserver son propre état en interne, ce qui le rend plus indépendant.

Une caractéristique importante des composants est que ces états internes sont isolés de l'extérieur. Le composant parent ne peut pas accéder à l'état interne du composant enfant, mais le composant enfant peut accéder à l'état (Props) transmis par le composant parent et répondre automatiquement en fonction des modifications.

Cette fonctionnalité peut être comprise car l'état est modularisé. L’avantage est qu’il n’est pas nécessaire de considérer que l’état actuel des paramètres affectera d’autres composants. Bien sûr, il n'est pas réaliste d'isoler complètement l'état des composants. Il sera inévitablement nécessaire que plusieurs composants partagent l'état. Dans ce cas, la solution consiste à extraire l'état du composant parent le plus proche de ces composants et à le transmettre via Props. .

Le schéma de partage de statut ci-dessus ne pose généralement aucun problème et constitue également une bonne pratique officiellement recommandée.

Mais si votre page est complexe, vous constaterez qu'elle est encore insuffisante. Par exemple :

    La hiérarchie des composants est trop profonde et doit partager l'état. À ce stade, l'état doit être transmis couche par couche.
  • Lorsqu'un composant enfant met à jour un état, il peut y avoir plusieurs composants parents, qui sont partagés par des composants frères, ce qui rend la mise en œuvre difficile.
Dans ce cas, si vous continuez à utiliser la méthode "

Extraire l'état vers le composant parent", vous constaterez que c'est très compliqué. Et à mesure que le nombre de composants augmente et que le niveau d’imbrication s’approfondit, la complexité devient de plus en plus élevée. Étant donné qu'il existe de nombreux états associés et une transmission complexe, il est facile que des problèmes tels qu'un certain composant soit mis à jour de manière inexplicable ou qu'un certain composant ne soit pas mis à jour, et qu'un dépannage anormal soit difficile.

Compte tenu de cela, nous avons besoin d’une solution plus élégante pour faire face à cette situation complexe.

Besoin d'une gestion de statut ?

Nous avons mentionné dans la section précédente qu'avec la complexité de la page, nous rencontrions des problèmes épineux dans la mise en œuvre de l'état partagé entre les composants.

Alors y a-t-il une solution ? Bien sûr, il existe, et grâce aux efforts des dirigeants communautaires, il existe plus d’un plan. Mais ces solutions ont toutes un nom commun, dont nous avons discuté très intensément il y a deux ans : State Management.

La gestion de l'état peut en fait être comprise comme une gestion globale de l'état. L'état ici est différent de l'état à l'intérieur du composant et est ensuite associé au composant qui a besoin de cet état d'une manière ou d'une autre.

La gestion des statuts a son propre plan de mise en œuvre. Vue a Vuex, React a Redux, Mobx et bien sûr il existe d'autres solutions. Mais ils résolvent tous le même problème, à savoir le problème du partage d’état entre composants.

Je me souviens qu'au cours des deux dernières années, en raison de la popularité du concept de « gestion d'état », il semblait être devenu un élément indispensable du développement d'applications. En prenant Vue comme exemple, la création d'un projet introduira inévitablement Vuex pour la gestion de l'état. Mais beaucoup de gens ne savent pas pourquoi, quand et comment utiliser la gestion de l'État, et ils suivent aveuglément la tendance. En conséquence, il y a eu de nombreux exemples d'abus de la gestion de l'État.

Voyant cela, il faut savoir que la gestion étatique n'est pas nécessaire. Pourquoi il apparaît et quels problèmes il résout sont essentiellement expliqués ci-dessus. Si vous ne comprenez pas, faites une pause et relisez-le depuis le début. Ne pensez pas que le contexte dans lequel une solution technique est née est sans importance. Si vous ne comprenez pas quel problème elle est conçue pour résoudre, vous ne pourrez pas véritablement jouer son rôle.

L'auteur de Redux a un dicton célèbre :

Si vous ne savez pas si vous avez besoin de Redux (gestion de l'état), alors vous n'en avez pas besoin.

D'accord, si vous utilisez la gestion d'état ou si vous avez besoin d'utiliser la gestion d'état pour vous aider à résoudre des problèmes, continuons à lire.

Vuex

Vue est largement utilisé en Chine, en particulier pour les petites et moyennes équipes, donc la première solution de gestion d'état avec laquelle la plupart des gens entrent en contact devrait être Vuex.

Alors, comment Vuex résout-il le problème du partage d'état entre composants ? Explorons-le ensemble.

Créer un magasin

Comme nous l'avons mentionné ci-dessus, pour l'état général de partage de composants, la recommandation officielle est de "

Extraire l'état vers le composant parent le plus proche". Vuex va encore plus loin et extrait tous les états vers le composant racine, afin que n'importe quel composant puisse y accéder.

Peut-être vous demanderez-vous : cela n’expose-t-il pas le statut à la situation globale ? Cela ne supprimerait-il pas complètement les avantages de la modularité ?

En fait, ce n’est pas le cas. L'objectif principal de Vuex est de permettre à tous les composants d'accéder à ces états et d'éviter complètement la situation où l'état des sous-composants n'est pas accessible. Vuex place toutes les données d'état sur un seul objet, suivant le principe de la

source de données unique. Mais cela ne signifie pas que l'état est empilé. Vuex implémente sa propre solution modulaire sur cet arbre d'état unique.

Ne vous inquiétez pas, procédons étape par étape et voyons d'abord comment utiliser Vuex.

Vuex existe en tant que plug-in pour Vue. Installez-le d'abord avec npm :

$ npm install --save vuex
Après l'installation, nous créons un nouveau dossier src/store et mettons ici tout le code lié à Vuex.

src/store 文件夹,在这里放所有 Vuex 相关的代码。

新建 index.js 并写入如下代码。这段代码主要的作用就是用 Vue.use 方法加载 Vuex 这个插件,然后将配置好的 Vuex.Store 实例导出。

import Vue from 'vue'
import Vuex from 'vuex'
// 安装插件
Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
})

上面导出的实例我们通常称之为 store。一个 store 中包含了存储的状态(state)和修改状态的函数(mutation)等,所有状态和相关操作都在这里定义。

最后一步,在入口文件将上面导出的 store 实例挂载到 Vue 上:

import store from './store'

new Vue({
  el: '#app',
  store: store
})

注意:挂载这一步不是必须的。挂载这一步的作用只是为了方便在 .vue 组件中通过 this.$store 访问我们导出的 store 实例。如果不挂载,直接导入使用也是一样的。

单一数据源(state)

上一步我们用构造函数 Vuex.Store 创建了 store 实例,大家至少知道该怎么用 Vuex 了。这一步我们来看看 Vuex.Store 构造函数的具体配置。

首先是 state 配置,他的值是一个对象,用来存储状态。Vuex 使用 单一状态树 原则,将所有的状态都放在这个对象上,便于后续的状态定位和调试。

比如说我们有一个初始状态 app_version 表示版本,如下:

new Vuex.Store({
  state: {
    app_version: '0.1.1'
  }
}

现在要在组件中获取,可以这样:

this.$store.state.app_version

但这并不是唯一的获取方式,也可以这样:

import store from '@/store' // @ 表示 src 目录
store.state.app_version

为什么要强调这一点呢?因为很多小伙伴以为 Vuex 只能通过 this.$store 操作。到了非组件内,比如在请求函数中要设置某一个 Vuex 的状态,就不知道该怎么办了。

事实上组件中获取状态还有更优雅的方法,比如 mapStateCréez un nouveau index.js et écrivez le code suivant. La fonction principale de ce code est de charger le plug-in Vuex à l'aide de la méthode Vue.use, puis d'exporter l'instance Vuex.Store configurée.

import { mapState } from 'vuex'

export default {
  computed: {
    ... // 其他计算属性
    ...mapState({
      version: state => state.app_version
    })
  }
}
🎜L'exemple exporté ci-dessus est généralement appelé store. Un magasin contient un état stocké (state) et des fonctions qui modifient l'état (mutation). Tous les états et opérations associées sont définis ici. 🎜🎜La dernière étape consiste à monter l'instance de magasin exportée ci-dessus vers Vue dans le fichier d'entrée : 🎜
new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state, count) {
      // 变更状态
      state.count += count
    }
  }
})
🎜Remarque : 🎜Cette étape de montage n'est pas nécessaire🎜. Le but de cette étape de montage est simplement de faciliter l'accès à notre instance de magasin exportée via this.$store dans le composant .vue. S'il n'est pas monté, c'est la même chose qu'une importation directe. 🎜🎜Source de données unique (état)🎜🎜Dans l'étape précédente, nous avons utilisé le constructeur Vuex.Store pour créer une instance de magasin. Tout le monde sait au moins comment utiliser Vuex. Dans cette étape, examinons la configuration spécifique du constructeur Vuex.Store. 🎜🎜La première est la configuration state. Sa valeur est un objet utilisé pour stocker l'état. Vuex utilise le principe Single State Tree pour placer tous les états sur cet objet afin de faciliter la localisation et le débogage ultérieurs des états. 🎜🎜Par exemple, nous avons un état initial app_version représentant la version, comme suit : 🎜
this.$store.commit('increment', 2)
🎜Maintenant nous voulons l'obtenir dans le composant, vous pouvez faire ceci : 🎜
new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    add(state) {
      state.count++
    },
    reduce(state) {
      state.count--
    }
  },
  actions: {
    increment(context, data) {
      axios.get('**').then(res => {
        if (data.iscan) {
          context.commit('add')
        } else {
          context.commit('reduce')
        }
      })
    }
  }
})
🎜Mais ce n'est pas le cas la seule façon de l'obtenir, vous pouvez aussi l'obtenir comme ceci :🎜
this.$store.dispatch('increment', { iscan: true })
🎜Pourquoi devrais-je insister sur ce point ? Parce que de nombreux amis pensent que Vuex ne peut être exploité que via this.$store. Lorsqu'il s'agit de non-composants, par exemple, si vous souhaitez définir un certain état Vuex dans la fonction de requête, vous ne savez pas quoi faire. 🎜🎜En fait, il existe des moyens plus élégants d'obtenir l'état des composants, comme la fonction mapState, qui facilite l'obtention de plusieurs états. 🎜
import { mapState } from 'vuex'

export default {
  computed: {
    ... // 其他计算属性
    ...mapState({
      version: state => state.app_version
    })
  }
}

状态更新方式(mutation)

Vuex 中的状态与组件中的状态不同,不能直接用 state.app_version='xx' 这种方式修改。Vuex 规定修改状态的唯一方法是提交 mutation

Mutation 是一个函数,第一个参数为 state,它的作用就是更改 state 的状态。

下面定义一个名叫 increment 的 mutation,在函数内更新 count 这个状态:

new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state, count) {
      // 变更状态
      state.count += count
    }
  }
})

然后在 .vue 组件中触发 increment

this.$store.commit('increment', 2)

这样绑定了 count 的视图就会自动更新。

同步更新

虽然 mutation 是更新状态的唯一方式,但实际上它还有一个限制:必须是同步更新

为什么必须是同步更新?因为在开发过程中,我们常常会追踪状态的变化。常用的手段就是在浏览器控制台中调试。而在 mutation 中使用异步更新状态,虽然也会使状态正常更新,但是会导致开发者工具有时无法追踪到状态的变化,调试起来就会很困难。

再有 Vuex 给 mutation 的定位就是更改状态,只是更改状态,别的不要参与。所谓专人干专事儿,这样也帮助我们避免把更改状态和自己的业务逻辑混起来,同时也规范了函数功能。

那如果确实需要异步更新,该怎么办呢?

异步更新

异步更新状态是一个非常常见的场景,比如接口请求回来的数据要存储,那就是异步更新。

Vuex 提供了 action 用于异步更新状态。与 mutation 不同的是,action 不直接更新状态,而是通过触发 mutation 间接更新状态。因此即便使用 action 也不违背 “修改状态的唯一方法是提交 mutation” 的原则。

Action 允许在实际更新状态前做一些副作用的操作,比如上面说的异步,还有数据处理,按条件提交不同的 mutation 等等。看一个例子:

new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    add(state) {
      state.count++
    },
    reduce(state) {
      state.count--
    }
  },
  actions: {
    increment(context, data) {
      axios.get('**').then(res => {
        if (data.iscan) {
          context.commit('add')
        } else {
          context.commit('reduce')
        }
      })
    }
  }
})

在组件中触发 action:

this.$store.dispatch('increment', { iscan: true })

这些就是 action 的使用方法。其实 action 最主要的作用就是请求接口,拿到需要的数据,然后触发 mutation 修改状态。

其实这一步在组件中也可以实现。我看过一些方案,常见的是在组件内写一个请求方法,当请求成功,直接通过 this.$store.commit 方法触发 mutation 来更新状态,完全用不到 action。

难道 action 可有可无吗?

也不是,在特定场景下确实需要 action 的,这个会在下一篇说。

状态模块化(module)

前面讲过,Vuex 是单一状态树,所有状态存放在一个对象上。同时 Vuex 有自己的模块化方案
,可以避免状态堆砌到一起,变的臃肿。

Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action。虽然状态注册在根组件,但是支持模块分割,相当于做到了与页面组件平级的“状态组件”。

为了区分,我们将被分割的模块称为子模块,暴露在全局的称为全局模块

我们来看基础用法:

new Vuex.Store({
  modules: {
    user: {
      state: {
        uname: 'ruims'
      },
      mutation: {
        setName(state, name) {
          state.name = name
        }
      }
    }
  }
})

上面定义了 user 模块,包含了一个 state 和一个 mutation。在组件中使用方法如下:

// 访问状态
this.$store.state.user.uname
// 更新状态
this.$store.commit('setName')

大家发现了,访问子模块的 state 要通过 this.$store.state.[模块名称] 这种方式去访问,触发 mutation 则与全局模块一样,没有区别。

action 与 mutation 原理一致,不细说。

命名空间

上面说到,子模块触发 mutation 和 action 与全局模块一致,那么假设全局模块和子模块中都有一个名为 setName 的 mutation。在组件中触发,哪个 mutation 会执行呢?

经过试验,都会执行。官方的说法是:为了多个模块能够对同一 mutation 或 action 作出响应。

其实官方做的这个兼容,我一直没遇到实际的应用场景,反而因为同名 mutation 导致误触发带来了不少的麻烦。可能官方也意识到了这个问题,索引后来也为 mutation 和 action 做了模块处理方案。

这个方案,就是命名空间。

命名空间也很简单,在子模块中加一个 namespaced: true 的配置即可开启,如:

new Vuex.Store({
  modules: {
    user: {
      namespaced: true,
      state: {}
    }
  }
})

开启命名空间后,触发 mutation 就变成了:

this.$store.commit('user/setName')

可见提交参数由 '[mutation]' 变成了 '[模块名称]/[mutation]'

模块化的槽点

上面我们介绍了 Vuex 的模块化方案,将单一状态树 store 分割成多个 module,各自负责本模块状态的存储和更新。

模块化是必要的,但是这个模块的方案,用起来总觉得有点别扭

比如,总体的设计是将 store 先分模块,模块下在包含 state,mutation,action。

那么按照正常理解,访问 user 模块下 state 应该是这样的:

this.$store.user.state.uname

但是实际 API 却是这样的:

this.$store.state.user.uname

这个 API 仿佛是在 state 中又各自分了模块。我没看过源码,但从使用体验上来说,这是别扭一。

除 state 外,mutation,action 默认注册在全局的设计,也很别扭

首先,官方说的多个模块对同一 mutation 或 action 作出响应,这个功能暂无找到应用场景。并且未配 namespace 时还要保证命名唯一,否则会导致误触发。

其次,用 namespace 后,触发 mutation 是这样的:

this.$store.commit('user/setName')

这个明显是将参数单独处理了,为什么不是这样:

this.$store.user.commit('setName')

总体感受就是 Vuex 模块化做的还不够彻底。

为什么吐槽

上面说的槽点,并不是为了吐槽而吐槽。主要是感觉还有优化空间。

比如 this.$store.commit 函数可以触发任何 mutation 来更改状态。如果一个组件复杂,需要操作多个子模块的状态,那么就很难快速的找出当前组件操作了哪些子模块,当然也不好做权限规定。

我希望的是,比如在 A 组件要用到 b, c 两个子模块的状态,不允许操作其他子模块,那么就可以先将要用到模块导入,比如这样写:

import { a, b } from this.$store
export default {
  methods: {
    test() {
      alert(a.state.uname) // 访问状态
      a.commit('setName')// 修改状态
    }
  }
}

这样按照模块导入,查询和使用都比较清晰。

下一步

前面我们详细介绍了状态管理的背景以及 Vuex 的使用,分享了关于官方 API 的思考。相信看到这里,你已经对状态管理和 Vuex 有了更深刻的认识和理解。

然而本篇我们只介绍了 Vuex 这一个方案,状态管理的其他方案,以及上面我们的吐槽点,能不能找到更优的实现方法,这些都等着我们去尝试。

下一篇文章我们继续深挖状态管理,对比 Vuex 和 React,Fluter 在状态管理实现上的差异,然后在 Vue 上集成 Mobx,打造我们优雅的应用。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer