State状态 #

什么是 State? #

State 是 Vuex store 的核心,它是应用中唯一的数据源。Vuex 使用单一状态树,这意味着一个应用仅包含一个 store 实例。

单一状态树 #

概念 #

单一状态树让我们能够直接定位任一特定的状态片段,在调试过程中也能轻易地取得整个当前应用状态的快照。

javascript
const store = createStore({
  state: {
    // 应用中所有的状态都在这里
    user: null,
    products: [],
    cart: [],
    settings: {}
  }
})

优势 #

text
单一状态树优势
├── 集中管理 ──── 所有状态在一处管理
├── 易于调试 ──── 状态快照清晰
├── 响应式 ───── 状态变化自动更新视图
└── 可预测 ───── 状态变更可追踪

定义 State #

基本定义 #

javascript
import { createStore } from 'vuex'

const store = createStore({
  state: {
    // 基本类型
    count: 0,
    message: 'Hello Vuex',
    isActive: true,
    
    // 对象
    user: {
      name: 'John',
      age: 25
    },
    
    // 数组
    todos: [
      { id: 1, text: 'Learn Vuex', done: true },
      { id: 2, text: 'Build App', done: false }
    ]
  }
})

初始化 State #

javascript
// 方式一:直接定义
const store = createStore({
  state: {
    count: 0
  }
})

// 方式二:使用函数返回(推荐用于 SSR)
const store = createStore({
  state() {
    return {
      count: 0,
      user: null
    }
  }
})

访问 State #

在组件中访问 #

直接访问 #

vue
<template>
  <div>
    <p>{{ $store.state.count }}</p>
    <p>{{ $store.state.user.name }}</p>
  </div>
</template>

使用计算属性 #

vue
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ userName }}</p>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count
    },
    userName() {
      return this.$store.state.user.name
    }
  }
}
</script>

mapState 辅助函数 #

基本用法 #

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    // 使用对象展开运算符
    ...mapState({
      // 箭头函数
      count: state => state.count,
      
      // 传字符串参数
      user: 'user',
      
      // 使用 this 访问组件局部状态
      fullName(state) {
        return `${state.user.firstName} ${this.localLastName}`
      }
    })
  }
}

数组语法 #

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    // 当映射的计算属性名称与 state 子节点名称相同时
    ...mapState([
      'count',
      'user',
      'todos'
    ])
  }
}

重命名 #

javascript
import { mapState } from 'vuex'

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

组合式 API 中使用 #

Vue 3 setup 语法 #

vue
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ user.name }}</p>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useStore } from 'vuex'

export default {
  setup() {
    const store = useStore()
    
    return {
      count: computed(() => store.state.count),
      user: computed(() => store.state.user)
    }
  }
}
</script>

封装组合式函数 #

javascript
// composables/useUser.js
import { computed } from 'vue'
import { useStore } from 'vuex'

export function useUser() {
  const store = useStore()
  
  const user = computed(() => store.state.user)
  const isLoggedIn = computed(() => store.getters.isLoggedIn)
  
  const login = async (credentials) => {
    await store.dispatch('login', credentials)
  }
  
  const logout = () => {
    store.commit('SET_USER', null)
  }
  
  return {
    user,
    isLoggedIn,
    login,
    logout
  }
}
vue
<!-- 使用封装的组合式函数 -->
<script>
import { useUser } from '@/composables/useUser'

export default {
  setup() {
    const { user, isLoggedIn, login, logout } = useUser()
    
    return {
      user,
      isLoggedIn,
      login,
      logout
    }
  }
}
</script>

State 结构设计 #

扁平化设计 #

javascript
// 推荐:扁平化结构
state: {
  users: {
    byId: {
      1: { id: 1, name: 'John' },
      2: { id: 2, name: 'Jane' }
    },
    allIds: [1, 2]
  },
  posts: {
    byId: {
      101: { id: 101, title: 'Post 1' },
      102: { id: 102, title: 'Post 2' }
    },
    allIds: [101, 102]
  }
}

// Getter 中获取数据
getters: {
  allUsers: state => state.users.allIds.map(id => state.users.byId[id]),
  userById: state => id => state.users.byId[id]
}

避免深层嵌套 #

javascript
// 不推荐:深层嵌套
state: {
  app: {
    data: {
      users: {
        list: {
          items: []
        }
      }
    }
  }
}

// 推荐:扁平结构
state: {
  users: [],
  posts: [],
  comments: []
}

关系数据设计 #

javascript
state: {
  // 实体数据
  users: {
    byId: {},
    allIds: []
  },
  posts: {
    byId: {},
    allIds: []
  },
  
  // 关系映射
  userPosts: {
    1: [101, 102],  // 用户 1 的文章
    2: [103]        // 用户 2 的文章
  }
}

getters: {
  userPosts: state => userId => {
    const postIds = state.userPosts[userId] || []
    return postIds.map(id => state.posts.byId[id])
  }
}

State 最佳实践 #

1. 初始化所有状态 #

javascript
// 推荐:初始化所有状态
state: {
  user: null,
  posts: [],
  loading: false,
  error: null
}

// 不推荐:未初始化
state: {
  user: undefined  // 可能导致响应式问题
}

2. 使用常量命名 #

javascript
// 状态命名清晰
state: {
  isLoading: false,
  hasError: false,
  errorMessage: ''
}

3. 分离关注点 #

javascript
// 按功能组织状态
state: {
  // 用户相关
  user: null,
  isLoggedIn: false,
  
  // 产品相关
  products: [],
  categories: [],
  
  // UI 状态
  loading: false,
  error: null
}

常见问题 #

1. 直接修改 State #

javascript
// 错误:直接修改
this.$store.state.count = 10

// 正确:通过 mutation
this.$store.commit('SET_COUNT', 10)

2. 响应式丢失 #

javascript
// 问题:添加新属性不触发响应
this.$store.state.user.newProp = 'value'

// 解决:使用 Vue.set 或替换整个对象
// 在 mutation 中
mutations: {
  ADD_PROP(state, { key, value }) {
    state.user = { ...state.user, [key]: value }
  }
}

3. 数组响应式问题 #

javascript
// 问题:通过索引修改
state.array[0] = newValue

// 解决:使用 splice 或替换
mutations: {
  UPDATE_ARRAY(state, { index, value }) {
    state.array.splice(index, 1, value)
    // 或
    state.array = state.array.map((item, i) => 
      i === index ? value : item
    )
  }
}

总结 #

State 是 Vuex 的核心,掌握 State 的使用是学习 Vuex 的第一步:

  • 单一状态树:所有状态集中在一处
  • 响应式:状态变化自动更新视图
  • mapState:简化状态映射
  • 合理设计:扁平化、初始化、清晰命名

继续学习 mapState辅助函数,深入了解状态映射技巧。

最后更新:2026-03-28