【sduoj】Conoce Vuex por primera vez

2021SC@SDUSC


En las primeras generaciones de versiones, SDUOJ siempre se ha basado en la forma en que los componentes pasan parámetros para transferir y administrar datos en todo el sistema. Sin embargo, con las iteraciones de versiones posteriores, la escala del proyecto continúa aumentando y el método de sincronización de datos que se basa en los parámetros de paso de componentes ya no es confiable. Por lo tanto, elegimos usar Vuex para la administración global de almacenamiento de datos.

Vuex

Introducción a Vuex

Vuex es un patrón de gestión de estado desarrollado específicamente para aplicaciones Vue.js. Utiliza un almacenamiento centralizado para administrar el estado de todos los componentes de la aplicación y usa las reglas correspondientes para garantizar que el estado cambie de manera predecible. Vuex también está integrado en la extensión devtools de la herramienta de depuración oficial de Vue , que proporciona funciones de depuración avanzadas, como depuración de viaje en el tiempo sin configuración, importación y exportación de instantáneas de estado, etc.

instalación de vuex

En el proyecto, usamos NPM o Yarn para instalar dependencias. Escriba el comando en la consola:

npm install vuex --save
# 或
yarn add vuex

Presentamos Vuex

En un sistema de empaquetado modular, debemos instalar explícitamente Vuex Vue.use()a través de :

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

Idea principal

En el corazón de cada aplicación Vuex está la tienda. Una "tienda" es básicamente un contenedor que contiene la mayor parte del estado de su aplicación . Vuex difiere de los objetos globales simples de dos maneras:

  1. La tienda de estado de Vuex es reactiva. Cuando el componente Vue lee el estado de la tienda, si el estado de la tienda cambia, el componente correspondiente se actualizará de manera eficiente en consecuencia.
  2. No puede cambiar directamente el estado en la tienda. La única forma de cambiar el estado en la tienda es confirmar explícitamente la mutación . Esto nos permite rastrear fácilmente cada cambio de estado, lo que nos permite implementar algunas herramientas para ayudarnos a comprender mejor nuestra aplicación.

A continuación, mostraré el ejemplo más simple de Vuex. En este caso, solo creamos un objeto de estado y una mutación:

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'

Vue.use(Vuex)

const store = new Vuex.Store({
    
    
	state: {
    
    
		num: 1,
	},
	mutations: {
    
    
		add(state, num) {
    
    
			state.num += num;
		}
	}
})

new Vue({
    
    
	store,
	render: h => h(App)
}).$mount('#app')

Ahora, podemos usar store.statepara obtener el objeto de estado y store.commitllamar a confirmar para activar el cambio de estado.

<!-- 这个是App.vue -->
<template>
	<div>
		<div>
			{
   
   { $store.state.num }}
		</div>
		<div>
			<button @click="add(1)"> 点我加1 </button>
			<button @click="add(2)"> 点我加2 </button>
		</div>
	</div>
</template>

<script>
export default {
      
      
	methods: {
      
      
		add(num) {
      
      
			this.$store.commit('add', num);
		}
	}
}
</script>

Bueno, se crea una tienda Vuex tan simple. Echemos un vistazo más profundo a los cinco conceptos básicos más básicos de Vuex.

Estado

árbol de un solo estado

Vuex usa un solo árbol de estado , lo que significa que un objeto contiene todo el estado a nivel de aplicación. Hasta el momento existe como una "Fuente Única de la Verdad (SSOT) ". Esto también significa que cada aplicación contendrá solo una instancia de tienda. Un solo árbol de estado nos permite ubicar directamente cualquier fragmento de estado específico y obtener fácilmente una instantánea de todo el estado actual de la aplicación durante la depuración.
El árbol de un solo estado y la modularización no entran en conflicto, discutiremos cómo distribuir los eventos de cambio de estado y de estado a los submódulos más adelante.
Los datos almacenados en Vuex datasiguen , por ejemplo, los objetos de estado deben ser sencillos.

Obtenga el estado de Vuex en el componente Vue

La tienda de estado de Vuex responde , y la forma más fácil de leer el estado de una instancia de tienda es devolver algún estado en una propiedad calculada :

export default {
    
    
	... ,
	computed: {
    
    
		num() {
    
    
			return this.$store.state.num;
		}
	}
}

Cada vez que store.state.numcambia , la propiedad calculada se vuelve a evaluar y activa una actualización del DOM asociado.

captadores

A veces necesitamos derivar algún estado del estado en la tienda, como filtrar y contar la lista:

computed: {
    
    
	availableItemLength() {
    
    
		return this.$store.state.list.filter((item) => {
    
    return item.available}).length;
	}
}

Si se necesitaran varios componentes para usar esta propiedad, duplicaríamos la función o extraeríamos una función compartida e la importaríamos en varios lugares; ninguna de las dos formas es la ideal. Vuex nos permite definir en la tienda getter(puede considerarse como una propiedad calculada de la tienda). Al igual que las propiedades calculadas, el valor de retorno de un getter se almacena en caché de acuerdo con sus dependencias y solo se vuelve a calcular cuando cambia el valor de sus dependencias.
Getter acepta estado como su primer argumento:

const store = new Vuex.Store({
    
    
	state: {
    
    
		list: [
			{
    
     id: 0, value: '...', available: true },
			{
    
     id: 1, value: '...', available: false }
		]
	},
	getters: {
    
    
		availableItems(state) {
    
    
			return state.list.filter((item) => {
    
    return item.available});
		}
	}
})

acceso a través de atributos

Los captadores se exponen como store.gettersobjetos y puede acceder a los valores como propiedades:

this.$store.getters.availableItems // -> [{ id: 0, value: '...', available: true }]

Los captadores también pueden aceptar otros captadores como segundos argumentos:

const store = new Vuex.Store({
    
    
	state: {
    
    
		list: [
			{
    
     id: 0, value: '...', available: true },
			{
    
     id: 1, value: '...', available: false }
		]
	},
	getters: {
    
    
		availableItems(state) {
    
    
			return state.list.filter((item) => {
    
    return item.available});
		},
		availableItemLength(state, getters) {
    
    
			return getters.availableItems.length
		}
	}
})

Tenga en cuenta que los captadores se almacenan en caché como parte del sistema de reactividad de Vue cuando se accede a través de propiedades.

acceso a través del método

También podemos pasar parámetros a captadores haciendo que los captadores devuelvan una función. Útil al consultar matrices en la tienda.

getters: {
    
    
	// ... ,
	getItemById(state) => (id) => {
    
    
		return state.list.find((item) => item.id === id)
	}
}
this.$store.getters.getItemById(1) // -> [{ id: 1, value: '...', available: false }]

Tenga en cuenta que cuando se accede al getter a través del método, se llamará cada vez y el resultado no se almacenará en caché.

Mutaciones

La única forma de cambiar el estado en la tienda de Vuex es enviar una mutación. Las mutaciones en Vuex son muy similares a los eventos: cada mutación tiene un tipo de evento (tipo) y una función de devolución de llamada (controlador) . Esta función de devolución de llamada es donde realmente hacemos el cambio de estado y acepta el estado como el primer parámetro.
No puede llamar a un controlador de mutaciones directamente. Esta opción se parece más al registro de eventos: "Cuando se active una mutación addde , llame a esta función." Para activar un controlador de mutación, debe llamar al método store.commit con el tipo correspondiente.

const store = new Vuex.Store({
    
    
  state: {
    
    
    num: 0
  },
  mutations: {
    
    
    add(state) {
    
    
      // 变更状态
      state.num++
    }
  }
})

store.commit('add');

Enviar la carga útil (Payload)

Puede store.commitpasar un parámetro adicional, la carga útil de la mutación

// ...
mutations: {
    
    
	add(state, n) {
    
    
		state.num += n;
	}
}
this.$store.commit('add', 2)

En la mayoría de los casos, la carga útil debe ser un objeto, que puede contener varios campos y documentar las mutaciones de manera más legible:

// ...
mutations: {
    
    
	add(state, payload) {
    
    
		state.num += payload.numToAdd;
	}
}
this.$store.commit('add', {
    
    
	numToAdd: 10
})

Envío de estilo de objeto

Otra forma de enviar una mutación es usar directamente un objeto que contenga typela propiedad

this.$store.commit({
    
    
	type: 'add',
	numToAdd: 10
})

La mutación debe seguir las reglas de respuesta de Vue

Ahora que el estado en la tienda de Vuex es reactivo, cuando cambiamos el estado, los componentes de Vue que monitorean el estado se actualizan automáticamente. Esto también significa que las mutaciones en Vuex también deben seguir las mismas precauciones que al usar Vue:

  1. Es una buena idea inicializar todas las propiedades requeridas en su tienda con anticipación.
  2. Debe usarlo cuando necesite agregar nuevas propiedades a un objeto Vue.set(obj, 'newProp', 123)o reemplazar un objeto antiguo por uno nuevo.

La mutación debe ser una función síncrona.

Esto es muy importante: las mutaciones deben ser funciones sincrónicas.
Una vez que las operaciones asincrónicas estén involucradas en nuestra lógica de código, usemos Action .

Comportamiento

La acción es similar a la mutación, la diferencia es:

  • Las acciones envían mutaciones en lugar de cambiar directamente el estado.
  • La acción puede contener operaciones asíncronas arbitrarias.

Registremos una acción simple:

const store = new Vuex.Store({
    
    
  state: {
    
    
    num: 0
  },
  mutations: {
    
    
    add(state) {
    
    
      state.num++
    }
  },
  actions: {
    
    
    increment (context) {
    
    
      context.commit('add')
    }
  }
})

Las funciones de acción aceptan un objeto de contexto con los mismos métodos y propiedades que las instancias de la tienda, por lo que puede llamar para context.commitenviar una mutación context.stateo context.getterspara obtener el estado y los captadores a través de y .

Distribuir acción

Las acciones son desencadenadas por store.dispatchel método :

this.$store.dispatch('add')

A primera vista parece superfluo, ¿no sería más conveniente para nosotros distribuir la mutación directamente? No realmente, ¿recuerdas la restricción que tienen las mutaciones para ejecutarse sincrónicamente ? ¡La acción no tiene restricciones! Podemos realizar operaciones asíncronas dentro de una acción :

actions: {
    
    
  addAsync ({
     
      commit }) {
    
    
    setTimeout(() => {
    
    
      commit('add')
    }, 1000)
  }
}

Las acciones admiten la misma carga útil y distribución de objetos:

// 以载荷形式分发
this.$store.dispatch('addAsync', {
    
    
  amount: 10
})

// 以对象形式分发
this.$store.dispatch({
    
    
  type: 'addAsync',
  amount: 10
})

Promesas de acción

La acción suele ser asincrónica, entonces, ¿cómo sabe cuándo termina la acción? Más importante aún, ¿cómo podemos combinar múltiples acciones para manejar procesos asincrónicos más complejos?
En primer lugar, debe comprender que store.dispatches posible manejar la Promesa devuelta por la función de controlador de la acción desencadenada y store.dispatchaun así devolver la Promesa.

actions: {
    
    
  actionA ({
     
      commit }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      setTimeout(() => {
    
    
        commit('mutationA')
        resolve()
      }, 1000)
    })
  }
}

De esta forma, podemos implementar operaciones como esta:

actions: {
    
    
  // ...
  actionB ({
     
      dispatch, commit }) {
    
    
    return dispatch('actionA').then(() => {
    
    
      commit('mutationB')
    })
  }
}

Módulos

Debido al uso de un solo árbol de estado, todo el estado de la aplicación se concentrará en un objeto relativamente grande. Cuando la aplicación se vuelve muy compleja, el objeto de la tienda puede volverse bastante inflado.
Para solucionar los problemas anteriores, Vuex nos permite dividir la tienda en módulos . Cada módulo tiene su propio estado, mutación, acción, captador e incluso submódulos anidados, separados de la misma manera de arriba a abajo:

const moduleA = {
    
    
	state: () => ({
    
     ... }),
	mutations: {
    
     ... },
	actions: {
    
     ... },
	getters: {
    
     ... }
}

const moduleB = {
    
    
	state: () => ({
    
     ... }),
	mutations: {
    
     ... },
	actions: {
    
     ... }
}

const store = new Vuex.Store({
    
    
	modules: {
    
    
		a: moduleA,
		b: moduleB
	}
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

estado local del módulo

Para mutaciones y captadores dentro de un módulo, el primer argumento recibido es el objeto de estado local del módulo .

const moduleA = {
    
    
	state: () => ({
    
    
		count: 0
	}),
	mutations: {
    
    
		increment (state) {
    
    
			// 这里的 `state` 对象是模块的局部状态
			state.count++
		}
	},
	getters: {
    
    
		doubleCount (state) {
    
    
			return state.count * 2
		}
	}
}

De manera similar, para las acciones dentro del módulo, el estado local context.statese expone y el estado del nodo raíz es context.rootState:

const moduleA = {
    
    
	// ...
	actions: {
    
    
		incrementIfOddOnRootSum ({
     
      state, commit, rootState }) {
    
    
			if ((state.count + rootState.count) % 2 === 1) {
    
    
				commit('increment')
			}
		}
	}
}

Para el getter dentro del módulo, el estado del nodo raíz se expondrá como el tercer parámetro:

const moduleA = {
    
    
	// ...
	getters: {
    
    
		sumWithRootCount (state, getters, rootState) {
    
    
			return state.count + rootState.count
		}
	}
}

Espacios de nombres

De forma predeterminada, las acciones, mutaciones y captadores dentro de un módulo se registran en el espacio de nombres global ; esto permite que varios módulos respondan a la misma mutación o acción.
Si desea que su módulo tenga un mayor grado de encapsulación y reutilización, puede convertirlo en un módulo con un espacio de nombres namespaced: trueagregando . Cuando se registra un módulo, todos sus captadores, acciones y mutaciones se nombrarán automáticamente de acuerdo con la ruta de registro del módulo. Por ejemplo:

const store = new Vuex.Store({
    
    
	modules: {
    
    
		account: {
    
    
			namespaced: true,
			// 模块内容(module assets)
			state: () => ({
    
     ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
			getters: {
    
    
				isAdmin () {
    
     ... } // -> getters['account/isAdmin']
			},
			actions: {
    
    
				login () {
    
     ... } // -> dispatch('account/login')
			},
			mutations: {
    
    
				login () {
    
     ... } // -> commit('account/login')
			},
			// 嵌套模块
			modules: {
    
    
				// 继承父模块的命名空间
				myPage: {
    
    
					state: () => ({
    
     ... }),
					getters: {
    
    
						profile () {
    
     ... } // -> getters['account/profile']
					}
				},
				// 进一步嵌套命名空间
				posts: {
    
    
					namespaced: true,
					state: () => ({
    
     ... }),
					getters: {
    
    
						popular () {
    
     ... } // -> getters['account/posts/popular']
					}
				}
			}
		}
	}
})

Los captadores y acciones habilitados para espacios de nombres reciben getterlocalizados dispatchy commit. En otras palabras, no necesita agregar prefijos de nombre de espacio adicionales dentro del mismo módulo cuando usa los activos del módulo. No es necesario modificar el código dentro del módulo después de cambiar el atributo de espacio de nombres.

Supongo que te gusta

Origin blog.csdn.net/qq_53126706/article/details/121683706
Recomendado
Clasificación