Create and use a Store
In Vue.js, the Store is the core tool for state management, particularly suitable for data sharing and reactive updates in complex applications. Vuex is the officially recommended state management library for Vue, which manages the state of all components in a centralized storage.
Installing and Configuring Vuex
First, install Vuex via npm or yarn:
npm install vuex --save
# or
yarn add vuex
Create a Store instance in your project:
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount: state => state.count * 2
}
})
Then import and inject it into the Vue instance in main.js
:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
Core Concepts Explained
State
State is the data source in the Store, similar to data
in components:
state: {
user: {
name: 'John',
age: 30
},
todos: [
{ id: 1, text: 'Learn Vuex', done: true },
{ id: 2, text: 'Build app', done: false }
]
}
Accessing State in components:
computed: {
count() {
return this.$store.state.count
},
todos() {
return this.$store.state.todos
}
}
Mutations
Mutations are the only way to modify State and must be synchronous functions:
mutations: {
increment(state, payload) {
state.count += payload.amount
},
addTodo(state, todo) {
state.todos.push(todo)
}
}
Committing Mutations in components:
methods: {
addTodo() {
this.$store.commit('addTodo', {
id: 3,
text: 'New todo',
done: false
})
}
}
Actions
Actions handle asynchronous operations and change state by committing Mutations:
actions: {
fetchUser({ commit }, userId) {
return axios.get(`/api/users/${userId}`)
.then(response => {
commit('setUser', response.data)
})
}
}
Dispatching Actions in components:
created() {
this.$store.dispatch('fetchUser', 123)
}
Getters
Getters are equivalent to computed properties for the Store:
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
activeTodosCount: (state, getters) => {
return state.todos.length - getters.doneTodos.length
}
}
Using Getters in components:
computed: {
doneTodos() {
return this.$store.getters.doneTodos
}
}
Modular Organization
For large applications, the Store can be split into modules:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
Accessing module states requires the module name:
this.$store.state.a // -> moduleA's state
this.$store.state.b // -> moduleB's state
Composition API Usage
Using the Store with Vue 3's Composition API:
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
setup() {
const store = useStore()
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
function increment() {
store.commit('increment')
}
return {
count,
doubleCount,
increment
}
}
}
Practical Application Example
Building a shopping cart Store:
// store/modules/cart.js
export default {
state: {
items: [],
checkoutStatus: null
},
mutations: {
pushItemToCart(state, product) {
state.items.push({
id: product.id,
title: product.title,
price: product.price,
quantity: 1
})
},
incrementItemQuantity(state, productId) {
const cartItem = state.items.find(item => item.id === productId)
cartItem.quantity++
}
},
actions: {
addProductToCart({ state, commit }, product) {
if (state.items.some(item => item.id === product.id)) {
commit('incrementItemQuantity', product.id)
} else {
commit('pushItemToCart', product)
}
}
},
getters: {
cartTotalPrice: state => {
return state.items.reduce((total, item) => {
return total + item.price * item.quantity
}, 0)
}
}
}
Using the shopping cart in a component:
<template>
<div>
<h2>Shopping Cart</h2>
<ul>
<li v-for="item in cartItems" :key="item.id">
{{ item.title }} - {{ item.price }} × {{ item.quantity }}
</li>
</ul>
<p>Total: {{ cartTotalPrice }}</p>
<button @click="addToCart(product)">Add to Cart</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
props: ['product'],
computed: {
...mapState('cart', ['items']),
...mapGetters('cart', ['cartTotalPrice']),
cartItems() {
return this.items
}
},
methods: {
...mapActions('cart', ['addProductToCart']),
addToCart(product) {
this.addProductToCart(product)
}
}
}
</script>
Plugin Development
You can develop Vuex plugins to extend functionality, such as persistent storage:
const localStoragePlugin = store => {
store.subscribe((mutation, state) => {
localStorage.setItem('vuex-state', JSON.stringify(state))
})
}
const store = new Vuex.Store({
// ...
plugins: [localStoragePlugin]
})
Strict Mode
Enable strict mode during development to detect non-standard State modifications:
const store = new Vuex.Store({
// ...
strict: process.env.NODE_ENV !== 'production'
})
Testing the Store
Example of testing a Mutation:
import mutations from './mutations'
describe('mutations', () => {
it('increment should increase count by 1', () => {
const state = { count: 0 }
mutations.increment(state)
expect(state.count).toBe(1)
})
})
Example of testing an Action:
import actions from './actions'
describe('actions', () => {
it('incrementAsync commits increment after delay', async () => {
const commit = jest.fn()
await actions.incrementAsync({ commit })
expect(commit).toHaveBeenCalledWith('increment')
})
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:陈川的代码茶馆🍵
下一篇:组合式Store写法