阿里云主机折上折
  • 微信号
Current Site:Index > Create and use a Store

Create and use a Store

Author:Chuan Chen 阅读数:15249人阅读 分类: Vue.js

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.