Работа с данными с vuex

Пример использования props и emit

Для начала, что бы понять для чего и как работает vuex создадим приложение, которое для обмена данными между компонентами использует props и emit.

main.js

/* jshint ignore:start */
import { createApp } from "vue"
import App from "./App.vue"

const app = createApp(App)

app.mount("#app")

App.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>App</p>
		</div>
		<app-counter :counter="counter"></app-counter>
		<app-action @counterUpdated="counter += $event"></app-action>
	</div>
</template>

<script>
	import Counter from "./Counter.vue"
	import Action from "./Action.vue"

	export default {
		components: {
			appCounter: Counter,
			appAction: Action
		},
		data () {
			return {
				counter: 0
			}
		}
	}
</script>

<style scoped></style>

Counter.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Counter</p>
			<h2>Counter {{ counter }}</h2>
		</div>
	</div>
</template>

<script>
	export default {
		props: ["counter"]
	}
</script>

<style scoped></style>

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				this.$emit("counterUpdated", val)
			}
		}
	}
</script>

<style scoped></style>

Что происходит в этом приложении, при нажатии кнопок наш счетчик прибавляет единицу или вычитает. Обмен данными в этом случае проходит с помощью props и emit через главный компонент.

 

Установка vuex

Документация vuex и сайт npm vuex.

Для установки через npm воспользуемся командой - npm install vuex@next --save.

После этого в разделе "dependencies" package.json появятся строка ""vuex": "^4.0.2"".

 

Работа с данными и переход на vuex

Рассмотрим как осуществляется переход от props и emit к vuex.

В проекте в папке "src" необходимо создать папку "store" и в ней файл с наименование нашего "хранилища данных", в данном случае назовем его index.js.

В store index.js пишем код, который подключает vuex и готовим конструкцию плагина.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"

const store = createStore({
	state () {
		return {
			counter: 1
		}
	}
})

export default store

Далее подключаем store vuex у нашему проекту.

main.js

/* jshint ignore:start */
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store/index.js"

const app = createApp(App)

app.use(store)
app.mount("#app")

В App.vue мы избавляемся от данных data - counter, и избавляемся от параметров передачи компонентов.

App.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>App</p>
		</div>
		<app-counter></app-counter>
		<app-action></app-action>
	</div>
</template>

<script>
	import Counter from "./Counter.vue"
	import Action from "./Action.vue"

	export default {
		components: {
			appCounter: Counter,
			appAction: Action
		},
		// data () {
		// 	return {
		// 		counter: 0
		// 	}
		// }
	}
</script>

<style scoped></style>

В Counter.vue отказываемся от props в пользу vuex.

Counter.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Counter</p>
			<h2>Counter {{ counter }}</h2>
		</div>
	</div>
</template>

<script>
	export default {
		//props: ["counter"]
		computed: {
			counter() {
				return this.$store.state.counter
			}
		}
	}
</script>

<style scoped></style>

В Action.vue отказываемся от emit в пользу vuex.

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				//this.$emit("counterUpdated", val)
				this.$store.state.counter += val
			}
		}
	}
</script>

<style scoped></style>

 

Использование getters

Getters позволяют внутри store делать дополнительные вычисления и обработку данных. Рассмотрим на примере использование getters.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"

const store = createStore({
	state () {
		return {
			counter: 0
		}
	},
	getters: {
		computedCounter(state) {
			return state.counter * 10
		}
	}
})

export default store

main.js

/* jshint ignore:start */
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store/index.js"

const app = createApp(App)

app.use(store)
app.mount("#app")

App.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>App</p>
		</div>
		<app-counter></app-counter>
		<app-action></app-action>
	</div>
</template>

<script>
	import Counter from "./Counter.vue"
	import Action from "./Action.vue"

	export default {
		components: {
			appCounter: Counter,
			appAction: Action
		},
	}
</script>

<style scoped></style>

Counter.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Counter</p>
			<h2>Counter {{ counter }}</h2>
		</div>
	</div>
</template>

<script>
	export default {
		computed: {
			counter() {
				return this.$store.getters.computedCounter
			}
		}
	}
</script>

<style scoped></style>

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				this.$store.state.counter += val
			}
		}
	}
</script>

<style scoped></style>

 

Использование mutations

Рассмотрим на примере для чего могут быть использованы mutations.

В Action.vue откажемся от "this.$store.state.counter += val" в пользу использования mutations.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"

const store = createStore({
	state () {
		return {
			counter: 0
		}
	},
	mutations: {
		changeCounter(state, payload) {
			state.counter += payload
		}
	},
	getters: {
		computedCounter(state) {
			return state.counter * 10
		}
	}
})

export default store

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				//this.$store.state.counter += val
				this.$store.commit("changeCounter", val)
			}
		}
	}
</script>

<style scoped></style>

 

Использование actions

Рассмотрим на примере использование actions для осуществления паузы при нажатии на кнопки.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"

const store = createStore({
	state () {
		return {
			counter: 0
		}
	},
	mutations: {
		changeCounter(state, payload) {
			state.counter += payload
		}
	},
	actions: {
		asyncChangeCounter(context, payload) {
			setTimeout(() => {
				context.commit("changeCounter", payload)
			}, 1000)
		}
	},
	getters: {
		computedCounter(state) {
			return state.counter * 10
		}
	}
})

export default store

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				//this.$store.commit("changeCounter", val)
				this.$store.dispatch("asyncChangeCounter", val)
			}
		}
	}
</script>

<style scoped></style>

 Для передачи параметров мы используем определенное написание ключей, как в примере ниже.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"

const store = createStore({
	state () {
		return {
			counter: 0
		}
	},
	mutations: {
		changeCounter(state, payload) {
			state.counter += payload
		}
	},
	actions: {
		asyncChangeCounter(context, payload) {
			setTimeout(() => {
				context.commit("changeCounter", payload.counterValue)
			}, payload.timeoutDelay)
		}
	},
	getters: {
		computedCounter(state) {
			return state.counter * 10
		}
	}
})

export default store

Action.vue

<template>
	<div>
		<div class="container text-center pt-5">
			<p>Action</p>
			<button class="btn btn-success" @click="updateCounter(1)">Add</button>
			<button class="btn btn-danger" @click="updateCounter(-1)">Subtract</button>
		</div>
	</div>
</template>

<script>
	export default {
		methods: {
			updateCounter(val) {
				//this.$store.commit("changeCounter", val)
				this.$store.dispatch("asyncChangeCounter", {
					counterValue: val,
					timeoutDelay: 1000
				})
			}
		}
	}
</script>

<style scoped></style>

Чаще всего action описывается вот так:

Пример:

	actions: {
		asyncChangeCounter({commit}, payload) {
			setTimeout(() => {
				commit("changeCounter", payload.counterValue)
			}, payload.timeoutDelay)
		}
	},

 

Оптимизация с помощью модулей

Vuex позволяет разбивать store на множество модулей для удобства. Как это делается показано в простом примере ниже.

Для каждого модуля создаем отдельный файл в папке "store" с его параметрами store, а в основном файле store - index.js подключаем эти модули.

index.js

/* jshint ignore:start */
import { createStore } from "vuex"
import counter from "./counter.js"

const store = createStore({
	modules: {
		counter: counter
	}
})

export default store

counter.js

/* jshint ignore:start */
export default {
	state () {
		return {
			counter: 0
		}
	},
	mutations: {
		changeCounter(state, payload) {
			state.counter += payload
		}
	},
	actions: {
		asyncChangeCounter(context, payload) {
			setTimeout(() => {
				context.commit("changeCounter", payload.counterValue)
			}, payload.timeoutDelay)
		}
	},
	getters: {
		computedCounter(state) {
			return state.counter * 10
		}
	}
}

 

Так же с моим портфолио можно ознакомиться на любой из представленной социальной сети, на своих страницах я публикую посты о своих работах, заданиях и целях.

Для связи со мной можно воспользоваться любой социальной сетью,
или написать на почту:

С моим резюме можно ознакомиться по ссылке:

© 2020-2024 Портфолио Юдина Александра г.Пенза. Все права защищены