Vuex: 在 Vue 项目中添加 vuex 的 store 支持

在 vue项目里,结合 nx 添加 store 的支持
更新于: 2023-05-24 08:54:21

cheatsheet

关键点解释说明
store 可以被注入到 this.$store 中
const app = new Vue({ ...App, store: nx.$store });
actions 可以同步,可以异步-
mutation 必须同步-
不要直接改变 state,而是通过 dispatch操作
methods: {
  async handleClick() {
    const res = await nx.$dispatch('user/login');
    console.log('res: ', res);
  }
}

关注点

  • App.vue: 有些人会将这个添加到 new Vue 中,这样可以在 this.$store 中调用到了
  • store/index.js: 
  • store/modules/user.js: 具体的store 模块
src/shared/store/
├── index.js
└── modules
    └── user.js

App.vue,其实我并没有添加什么

import Vue from "vue";
import App from "./App";

import "./uni.promisify.adaptor";
import "./shared/bootstrap";

Vue.config.productionTip = false;
App.mpType = "app";

// start app
const app = new Vue({ ...App, store: nx.$store });
app.$mount();

store/index.js: 管理 modules 里的多个 store,利用 require.context 管理多个 store

import Vue from "vue";
import Vuex from "vuex";

const modulesFiles = require.context("./modules", true, /\.js$/);

// Auto import modules from modules folder
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
  const value = modulesFiles(modulePath);
  modules[moduleName] = value.default;
  return modules;
}, {});

Vue.use(Vuex);

nx.$store = new Vuex.Store({
  modules,
});

store/modules/user.js: 具体的 user 数据逻辑。

export default {
  namespaced: true,
  actions: {
    async login({ commit }, data) {
      commit('sets', { loading: true });
      const res = await nx.$api.profile(data);
      commit('sets', { loading: false, profile: res });
    },
  },
  state() {
    return {
      loading: false,
      profile: null
    };
  },
  mutations: {
    // common mutations
    sets(state, data) {
      nx.forIn(data, (key, value) => nx.set(state, key, value));
    }
  }
};

使用

# 调用 profile
nx.$store.dispatch('user/login')
# 取数据
nx.$store.state.user.profile

简化

简单封装,简化 API 的调用

  • nx.$store: 真正的项目 store,与 this.$store 指向同一个引用
  • nx.$get: 可以根据 path取得真正的 state 里的值
  • nx.$patch: 相当于 dispatch
  • nx.$mapStore: 直接将 path 中的最后一段,映射为 VueInstance 实际 computed 中的值
// store
nx.$store = new Vuex.Store({
  modules
});

// get
nx.$get = (inKey, inDefault) => {
  const state = nx.$store.state;
  if (!inKey) return state;
  return nx.get(state, inKey, inDefault);
};

// dispatch(参考pinia的patch)
nx.$patch = (inPath, inPayload) => {
  return nx.$store.dispatch(inPath, inPayload);
};

// mapState
// {
//   loading: () => nx.$get('user.loading'),
//   username: () => nx.$get('user.profile.login')
// },

nx.$mapState = (inKeys) => {
  return inKeys.reduce((res, item) => {
    const keys = item.split('.');
    const lastKey = keys[keys.length - 1];
    res[lastKey] = () => nx.$get(item);
    return res;
  }, {});
};

实际使用

<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view>
      <text>loading: {{ String(loading) }}</text>
      <text class="title">{{ title }} - {{ login }}</text>
    </view>
    <button :disabled="loading" size="mini" type="primary" @click="handleClick">
      点击
    </button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      title: 'Hello'
    };
  },
  computed: {
    // user.profile.login -> login
    // user.loading -> loading
    // 其它语法糖: user.profile.login:username,即最后一个key,loading应该映射成为 username
    ...nx.$mapState(['user.profile.login', 'user.loading'])
  },
  onLoad() {},
  methods: {
    async handleClick() {
      const res = await nx.$dispatch('user/login');
      console.log('res: ', res);
    }
  }
};
</script>

参考