Vuex基操与原理

Posted by Mars . Modified at

Vuex 用于统一管理Vue app中各组件的状态,好处有二:

① 避免了属性父传子,子改父的麻烦操作。尤其是组件数目多、嵌套深的情况。

② 可以统一拦截修改状态的操作,从而进行一些记录,做到修改状态有迹可循,方便查错维护。

Store 仓库

vuex通过创建Store,并作为插件应用在Vue app实例上,来达到统一管理app实例中组件状态的目的。

import { createStore } from 'vuex'
import { createApp } from 'vue'
import App from 'App.vue'

const app = createApp(App)
const store = createStore({
    state(){
        // define states here.
    },
    mutations: {
        // define mutations here.
    }
})
app.use()

vuex的方法全部类似vue, createStore()传入的对象也与根组件配置对象形式相同,就像只是换了属性名字。data换为state,methods换为mutations,computed换为getter。

在app.use(store)执行后,app根组件实例上会被挂载一个$store全局对象,这样全部组件都可以在内部通过this.$store引用这个store内的数据。

在Vue3 组合式API的setup()函数中,this不指向组件实例,也就不能用this.$store访问。

此时需要从vuex引入useStore 方法,并在setup中声明 const store = useStore();

这样,通过这个变量store就可以访问vuex store了。

State 状态

state是vuex中保存的状态,不能直接修改,必须通过mutations中定义的方法进行修改。

store中的state都是响应式的,在组件中可以通过computed()计算属性来引入,并保持响应式。

mapState()方法可以从vuex中引入,用来方便一次性获取多个store中的state,并返回给computed。

Mutations 变更(突变)

vuex store 配置的mutations属性中声明对state的各种操作方式。

它们应该是修改state的唯一方式,虽然通过store.state.xx直接修改也是可以的,不会报错,但是这样就不能对组件修改操作进行拦截,也有悖设计原则,无法确定哪个组件修改了state,对debug产生困扰。

// store config
// mutation函数在被调用的时候,State永远作为第一个参数传入,之后的参数可以自定义
mutations: {
  increment (state, n) {
    state.count += n
  }
}

// components
{
    store.commit('increment', 2)
}

官方说明: mutation应该必须为同步函数。

就像mutation的含义一样,它是直接变更state的。如果产生异步,则会出现违背设计的未知问题。

mapMutations 用来在子组件的methods中,一次性commit多个mutation。

getters 计算状态

getters相当于vue组件中的computed计算属性。只不过它是在store中定义的,全局组件都可以使用。

getters的设置目的是定义全局计算属性,免去每次在组件中引入并声明的繁琐。

{
    getters: {
        // 对state中的arr.good 为 true的子数组进行返回
        goodArr: state.arr.filter((arr)=>{return arr.good === true})_
    }
}

同样,mapGetters方法用于一次性获取多个getter属性到组件。

Actions 动作

Actions 和 Mutations 差不多,官方提出他们的两点区别如下:

  1. Mutations直接修改status,而Actions是commit mutations,相当于间接通过mutations修改state;
  2. Actions可以是异步函数。

actions在触发的时候,会传入一个参数context,相当于整个store的上下文环境引用。

可以通过这个context,获取store内的任意属性。比如:context.state, context.mutation。

因此也可以使用解构语法,在传入context的时候直接获取想要的方法:

actions: {
  increment ({ commit }) {
    // 解构获取了store.commit
    commit('increment')
  }
}

不同于mutation的commit,actions的触发方式是dispatch。

// trigger an action inside a component.
this.store.dispatch('action1')

actions可以是异步的,因为它们是通过一个个的mutation来修改state的。不论什么时候触发mutation,只要最终触发了的不同mutation互不影响,store.state的最终状态就一样。

但是,触发修改同一state的多个mutation,触发的先后顺序可能造成state最终的结果不同。比如一个state为价格10,先+3再×2,和先×2再+3,结果不一致。

这时,就需要对actions进行设计,让它触发mutation的先后顺序可控。具体见官网下例:

// assuming `getData()` and `getOtherData()` return Promises

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for `actionA` to finish
    commit('gotOtherData', await getOtherData())
  }
}
Keywords: Vue
previousPost nextPost
已经有 1000000 个小伙伴看完了这篇推文。