State 状态

State 是 Vuex 的核心,存储应用的所有状态数据。理解如何定义和访问状态,是使用 Vuex 的第一步。

单一状态树

Vuex 使用单一状态树,一个对象包含全部应用状态:

const store = new Vuex.Store({
  state: {
    count: 0,
    user: {
      id: 1,
      name: '张三',
      email: 'zhangsan@example.com'
    },
    todos: [
      { id: 1, text: '学习 Vue', done: true },
      { id: 2, text: '学习 Vuex', done: false }
    ],
    products: [],
    cart: []
  }
})

访问 State

直接访问

export default {
  computed: {
    count() {
      return this.$store.state.count
    },
    user() {
      return this.$store.state.user
    }
  }
}

mapState 辅助函数

import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['count', 'user', 'todos'])
  }
}

对象展开

import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      count: state => state.count,
      user: state => state.user,
      myTodos: 'todos'
    })
  }
}

使用别名

import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      currentCount: 'count',
      currentUser: 'user'
    })
  }
}

定义 State

基本类型

state: {
  count: 0,
  message: 'Hello',
  isActive: true
}

对象类型

state: {
  user: {
    id: null,
    name: '',
    email: ''
  },
  settings: {
    theme: 'light',
    language: 'zh-CN'
  }
}

数组类型

state: {
  todos: [],
  products: [],
  notifications: []
}

嵌套结构

state: {
  entities: {
    users: {
      byId: {
        1: { id: 1, name: '张三' },
        2: { id: 2, name: '李四' }
      },
      allIds: [1, 2]
    },
    posts: {
      byId: {
        1: { id: 1, title: '文章1', authorId: 1 },
        2: { id: 2, title: '文章2', authorId: 2 }
      },
      allIds: [1, 2]
    }
  }
}

响应式特性

添加新属性

直接添加属性不会触发响应式更新

state.user.age = 25

应该使用 Vue.set 或对象展开:

Vue.set(state.user, 'age', 25)

state.user = { ...state.user, age: 25 }

删除属性

Vue.delete(state.user, 'age')

state.user = {
  ...state.user,
  age: undefined
}

数组操作

state.todos.push(newTodo)

state.todos.pop()

state.todos.splice(index, 1)

state.todos = state.todos.filter(todo => !todo.done)

实战示例

用户状态

state: {
  user: {
    id: null,
    name: '',
    email: '',
    avatar: '',
    role: ''
  },
  token: null,
  isLoggedIn: false
}
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState('user', ['user', 'isLoggedIn']),
    
    userName() {
      return this.user.name || '游客'
    },
    
    isAdmin() {
      return this.user.role === 'admin'
    }
  }
}

购物车状态

state: {
  cart: {
    items: [],
    total: 0,
    count: 0
  }
}
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState('cart', ['cart']),
    
    cartItems() {
      return this.cart.items
    },
    
    cartTotal() {
      return this.cart.total
    },
    
    cartCount() {
      return this.cart.count
    },
    
    hasItems() {
      return this.cart.items.length > 0
    }
  }
}

分页状态

state: {
  pagination: {
    page: 1,
    pageSize: 10,
    total: 0,
    totalPages: 0
  }
}
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['pagination']),
    
    currentPage() {
      return this.pagination.page
    },
    
    hasNextPage() {
      return this.pagination.page < this.pagination.totalPages
    },
    
    hasPrevPage() {
      return this.pagination.page > 1
    }
  }
}

加载状态

state: {
  loading: {
    users: false,
    products: false,
    orders: false
  }
}
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['loading']),
    
    isLoading() {
      return Object.values(this.loading).some(v => v)
    }
  }
}

状态初始化

从本地存储恢复

const store = new Vuex.Store({
  state: {
    user: JSON.parse(localStorage.getItem('user')) || null,
    token: localStorage.getItem('token') || null,
    theme: localStorage.getItem('theme') || 'light'
  }
})

从 API 初始化

const store = new Vuex.Store({
  state: {
    config: null
  },
  actions: {
    async initApp({ commit }) {
      const config = await api.getConfig()
      commit('setConfig', config)
    }
  }
})

store.dispatch('initApp')

状态持久化

vuex-persistedstate

import createPersistedState from 'vuex-persistedstate'

const store = new Vuex.Store({
  plugins: [
    createPersistedState({
      key: 'my-app',
      paths: ['user', 'token', 'settings']
    })
  ]
})

手动持久化

const store = new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    setUser(state, user) {
      state.user = user
      localStorage.setItem('user', JSON.stringify(user))
    },
    clearUser(state) {
      state.user = null
      localStorage.removeItem('user')
    }
  }
})

组件与 State

计算属性缓存

export default {
  computed: {
    user() {
      return this.$store.state.user
    },
    userName() {
      return this.user?.name || '游客'
    }
  }
}

模板中使用

<template>
  <div>
    <p>用户名:{{ user.name }}</p>
    <p>邮箱:{{ user.email }}</p>
    <p>待办事项:{{ todos.length }} 项</p>
  </div>
</template>

条件渲染

<template>
  <div v-if="isLoggedIn">
    欢迎,{{ user.name }}
  </div>
  <div v-else>
    请先登录
  </div>
</template>

小结

State 要点:

  1. 单一状态树:所有状态集中在一个对象中
  2. 访问方式:$store.state 或 mapState
  3. 响应式:注意添加/删除属性的方式
  4. 初始化:从本地存储或 API 恢复状态
  5. 持久化:使用插件或手动保存