【sduoj】처음으로 Vuex에 대해 알아보기

2021SC@SDUSC


처음 몇 세대의 버전에서 SDUOJ는 항상 전체 시스템에서 데이터를 전송하고 관리하기 위해 매개 변수를 전달하는 구성 요소 방식에 의존해 왔습니다. 그러나 후속 버전 반복으로 프로젝트의 규모가 계속 증가하고 구성 요소 전달 매개 변수에 의존하는 데이터 동기화 방법이 더 이상 신뢰할 수 없기 때문에 글로벌 데이터 스토리지 관리를 위해 Vuex를 사용하기로 결정했습니다.

Vuex

Vuex 소개

Vuex 는 Vue.js 애플리케이션을 위해 특별히 개발된 상태 관리 패턴 입니다 . 중앙 집중식 스토리지를 사용하여 애플리케이션의 모든 구성 요소 상태를 관리하고 해당 규칙을 사용하여 상태가 예측 가능한 방식으로 변경되도록 합니다. Vuex는 또한 Vue의 공식 디버깅 도구인 devtools extension 에 통합 되어 제로 구성 시간 이동 디버깅, 상태 스냅샷 가져오기 및 내보내기 등과 같은 고급 디버깅 기능을 제공합니다.

Vuex 설치

프로젝트에서는 NPM 또는 Yarn을 사용하여 종속성을 설치합니다. 콘솔에 다음 명령을 입력합니다.

npm install vuex --save
# 或
yarn add vuex

Vuex 소개

모듈 패키징 시스템에서는 Vue.use()다음을 통해 .

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

Vue.use(Vuex)

핵심 아이디어

모든 Vuex 애플리케이션의 중심에는 스토어가 있습니다. "스토어"는 기본적으로 대부분의 애플리케이션 상태를 보관하는 컨테이너입니다 . Vuex는 두 가지 면에서 일반 전역 객체와 다릅니다.

  1. Vuex의 상태 저장소는 반응적입니다. Vue 컴포넌트가 스토어에서 상태를 읽을 때 스토어의 상태가 변경되면 그에 따라 해당 컴포넌트가 효율적으로 업데이트됩니다.
  2. 상점에서 직접 상태를 변경할 수 없습니다. 저장소에서 상태를 변경하는 유일한 방법은 명시적으로 변경을 커밋하는 것입니다 . 이를 통해 모든 상태 변경을 쉽게 추적할 수 있으므로 애플리케이션을 더 잘 이해하는 데 도움이 되는 몇 가지 도구를 구현할 수 있습니다.

아래에서 가장 간단한 Vuex 예제를 보여드리겠습니다. 이 경우에는 상태 객체와 변형만 생성합니다.

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')

이제 상태 객체를 store.state가져오고 store.commit커밋을 호출하여 상태 변경을 트리거하는 데 사용할 수 있습니다.

<!-- 这个是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>

이렇게 간단한 Vuex Store가 생성됩니다. Vuex의 가장 기본적인 다섯 가지 핵심 개념에 대해 자세히 살펴보겠습니다.

상태

단일 상태 트리

Vuex는 단일 상태 트리를 사용합니다 . 즉, 하나의 객체에 모든 애플리케이션 수준 상태가 포함됩니다. 지금까지는 "Single Source of Truth (SSOT) "로 존재합니다 . 이는 또한 각 응용 프로그램에 하나의 저장소 인스턴스만 포함됨을 의미합니다. 단일 상태 트리를 사용하면 특정 상태 조각을 직접 찾고 디버깅 중에 전체 현재 애플리케이션 상태의 스냅샷을 쉽게 얻을 수 있습니다.
단일 상태 트리와 모듈화는 충돌하지 않습니다. 나중에 상태 및 상태 변경 이벤트를 하위 모듈에 배포하는 방법에 대해 논의할 것입니다.
Vuex에 저장된 데이터는 Vue 인스턴스와 동일한 규칙을 data따릅니다 . 예를 들어 상태 객체는 일반이어야 합니다.

Vue 구성 요소에서 Vuex 상태 가져오기

Vuex의 상태 저장소는 반응형 이며 저장소 인스턴스에서 상태를 읽는 가장 쉬운 방법은 계산된 속성 에서 일부 상태를 반환하는 것입니다 .

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

store.state.num변경될 때마다 계산된 속성이 재평가되고 연결된 DOM의 업데이트가 트리거됩니다.

게터

때로는 목록을 필터링하고 계산하는 것과 같이 상점의 상태에서 일부 상태를 파생해야 합니다.

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

이 속성을 사용하기 위해 여러 구성 요소가 필요한 경우 함수를 복제하거나 공유 함수를 추출하여 여러 위치에서 가져옵니다. 어느 쪽이든 이상적이지 않습니다. Vuex를 사용하면 스토어에서 정의할 수 있습니다 getter(스토어의 계산된 속성으로 간주될 수 있음). 계산된 속성과 마찬가지로 getter의 반환 값은 종속성에 따라 캐시되며 종속 값이 변경될 때만 다시 계산됩니다.
Getter는 상태를 첫 번째 인수로 받아들입니다.

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});
		}
	}
})

속성을 통한 액세스

게터는 store.getters객체 속성으로 값에 액세스할 수 있습니다.

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

게터는 다른 게터를 두 번째 인수로 허용할 수도 있습니다.

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
		}
	}
})

getter는 속성을 통해 액세스할 때 Vue의 반응성 시스템의 일부로 캐시됩니다.

방법을 통한 액세스

게터가 함수를 반환하도록 하여 게터에 매개변수를 전달할 수도 있습니다. 저장소에서 배열을 쿼리할 때 유용합니다.

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

메서드를 통해 getter에 액세스하면 매번 호출되며 결과는 캐시되지 않습니다.

돌연변이

Vuex 스토어에서 상태를 변경하는 유일한 방법은 변형을 제출하는 것입니다. Vuex의 변형은 이벤트와 매우 유사합니다. 각 변형에는 문자열 이벤트 유형(유형) 과 콜백 함수(핸들러)가 있습니다 . 이 콜백 함수는 실제로 상태를 변경하는 곳이며 상태를 첫 번째 매개변수로 받아들입니다.
변형 처리기를 직접 호출할 수 없습니다. 이 옵션은 이벤트 등록과 비슷합니다: "유형 add의 이 함수를 호출합니다." 변형 핸들러를 깨우려면 해당 유형으로 store.commit 메서드를 호출해야 합니다.

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

store.commit('add');

페이로드 제출(Payload)

store.commit추가 매개변수인 변이의 페이로드를 전달할 수 있습니다.

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

대부분의 경우 페이로드는 여러 필드를 포함할 수 있고 변형을 보다 읽기 쉽게 문서화할 수 있는 개체여야 합니다.

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

개체 스타일 제출

변형을 제출하는 또 다른 방법은 type속성을

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

돌연변이는 Vue의 응답 규칙을 따라야 합니다.

이제 Vuex 스토어의 상태가 반응형이므로 상태를 변경하면 상태를 모니터링하는 Vue 구성 요소가 자동으로 업데이트됩니다. 이는 또한 Vuex의 변형이 Vue를 사용할 때와 동일한 예방 조치를 따라야 함을 의미합니다.

  1. 사전에 스토어에서 모든 필수 속성을 초기화하는 것이 좋습니다.
  2. 객체에 새 속성을 추가하거나 Vue.set(obj, 'newProp', 123)이전 객체를 새 객체로 교체해야 할 때 사용해야 합니다.

돌연변이는 동기 함수여야 합니다.

이것은 매우 중요합니다. 돌연변이는 동기 함수여야 합니다. 코드 논리에 비동기 작업이 포함되면 Action 을
사용하겠습니다 .

행위

동작은 돌연변이와 비슷하지만 차이점은 다음과 같습니다.

  • 작업은 상태를 직접 변경하는 대신 변형을 제출합니다.
  • 작업에는 임의의 비동기 작업이 포함될 수 있습니다.

간단한 작업을 등록해 보겠습니다.

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

액션 함수는 스토어 인스턴스와 동일한 메서드 및 속성을 가진 컨텍스트 객체를 허용하므로 호출하여 context.commit변형을 제출 context.state하거나 context.getters및 를 통해 상태 및 게터를 가져올 수 있습니다.

배포 작업

작업은 store.dispatch다음 메서드 .

this.$store.dispatch('add')

언뜻 보기에 쓸데없어 보이는데 뮤테이션을 직접 배포하는 편이 더 편하지 않을까요? 실제로는 돌연변이가 동기적으로 실행되어야 한다는 제한 사항을 기억하십니까 ? 행동에 제약이 없다! 액션 내에서 비동기 작업을 수행할 수 있습니다.

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

작업은 동일한 페이로드 및 개체 배포를 지원합니다.

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

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

Action 적 Promise

작업은 일반적으로 비동기식이므로 작업이 끝나는 시점을 어떻게 알 수 있습니까? 더 중요한 것은 더 복잡한 비동기 프로세스를 처리하기 위해 여러 작업을 결합하는 방법입니다. 먼저
트리거된 작업의 핸들러 함수에서 반환된 Promise를 처리하고 여전히 Promise를 반환하는 것이 가능하다는 것을 이해해야 합니다.store.dispatchstore.dispatch

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

이러한 방식으로 다음과 같은 작업을 구현할 수 있습니다.

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

모듈

단일 상태 트리를 사용하기 때문에 애플리케이션의 모든 상태는 상대적으로 큰 개체에 집중됩니다. 응용 프로그램이 매우 복잡해지면 저장소 개체가 상당히 커질 수 있습니다.
위의 문제를 해결하기 위해 Vuex를 사용하면 저장소를 모듈 로 분할할 수 있습니다 . 각 모듈에는 고유한 상태, 변형, 작업, getter 및 중첩된 하위 모듈이 있으며 동일한 방식으로 위에서 아래로 구분됩니다.

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 的状态

모듈의 로컬 상태

모듈 내부의 변형 및 게터의 경우 수신된 첫 번째 인수는 모듈의 로컬 상태 객체 입니다 .

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

마찬가지로 모듈 내부의 작업에 대해 로컬 상태는 를 통해 context.state노출되며 루트 노드의 상태는 다음과 같습니다 context.rootState.

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

모듈 내부의 getter의 경우 루트 노드 상태가 세 번째 매개변수로 노출됩니다.

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

네임스페이스

기본적으로 모듈 내부의 작업, 변형 및 게터는 전역 네임스페이스 에 등록됩니다 . 이를 통해 여러 모듈이 동일한 변형 또는 작업에 응답할 수 있습니다.
모듈의 캡슐화 및 namespaced: true재사용 . 모듈이 등록되면 모듈 등록 경로에 따라 모든 getter, 작업 및 변형의 이름이 자동으로 지정됩니다. 예를 들어:

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']
					}
				}
			}
		}
	}
})

네임스페이스 사용 getter 및 작업은 지역화된 getter, dispatch및 를 수신합니다 commit. 즉, 모듈 자산을 사용할 때 동일한 모듈 내에 추가 공간 이름 접두사를 추가할 필요가 없습니다. namespaced 속성을 변경한 후 모듈 내부의 코드를 수정할 필요가 없습니다.

추천

출처blog.csdn.net/qq_53126706/article/details/121683706