Vuex module

墨龙吟 2020-04-24

Vuex 允许我们将 store 分割成模块(module)

每个模块拥有自己的 state、mutation、action、getter、

甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块的局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象

const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit(‘increment‘)
      }
    }
  }
}

对于模块内部的 getter,根节点状态会作为第三个参数暴露出来

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

命名空间

默认情况下 注册在全局命名空间的 这样使得多个模块能够对同一 mutation 或 action 作出响应。

可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。

在带命名空间的模块内访问全局内容(Global Assets)

{ root: true } 作为第三参数传给 dispatchcommit 即可

dispatch(‘someOtherAction‘) // -> ‘foo/someOtherAction‘
        dispatch(‘someOtherAction‘, null, { root: true }) // -> ‘someOtherAction‘

        commit(‘someMutation‘) // -> ‘foo/someMutation‘
        commit(‘someMutation‘, null, { root: true }) // -> ‘someMutation‘

在带命名空间的模块注册全局 action

若需要在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中。例如:

{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch(‘someAction‘)
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> ‘someAction‘
        }
      }
    }
  }
}

带命名空间的绑定函数

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    ‘some/nested/module/foo‘, // -> this[‘some/nested/module/foo‘]()
    ‘some/nested/module/bar‘ // -> this[‘some/nested/module/bar‘]()
  ])
}

上面的例子可以简化为【如果有的方法在全局里,有的在模块中,则使用上面未简化版本

computed: {
  ...mapState(‘some/nested/module‘, {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions(‘some/nested/module‘, [
    ‘foo‘, // -> this.foo()
    ‘bar‘ // -> this.bar()
  ])
}

可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数

import { createNamespacedHelpers } from ‘vuex‘

const { mapState, mapActions } = createNamespacedHelpers(‘some/nested/module‘)

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      ‘foo‘,
      ‘bar‘
    ])
  }
}

模块动态注册

在 store 创建之后,你可以使用 store.registerModule 方法注册模块:

import Vuex from ‘vuex‘

const store = new Vuex.Store({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule(‘myModule‘, {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule([‘nested‘, ‘myModule‘], {
  // ...
})
保留 state

当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。

模块重用

如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。

实际上这和 Vue 组件内的 data 是同样的问题

使用一个函数来声明模块状态(仅 2.3.0+ 支持):

const MyReusableModule = {
  state () {
    return {
      foo: ‘bar‘
    }
  },
  // mutation, action 和 getter 等等...
}

module.js

import Vue from ‘vue‘;
import ‘es6-promise/auto‘
import Vuex from ‘vuex‘;

Vue.use(Vuex);

const moduleA = {
    namespaced: true,
    state: {
        name: ‘ModuleA‘,
        count: 0,
        price: 0,
        total: 0,
    },
    getters: {
        //計算moduleA中的count狀態值與root store中的count狀態值 之間的差值
        countDistanceWithRootCount(state, getters, rootState) {
            return state.count - rootState.count;
        },
    },
    mutations: {
        increment(state) {
            state.count++;
            state.total = state.price * state.count;
        },
        setName(state, payload) {
            state.name = payload.name;
        },
        calculate(state, payload) {
            state.total = state.price * payload;
        },
    },
    actions: {
        //儅大於主節點的count值時,使用主節點的count
        useRootCountIfCountOverClocked({state, commit, rootState}) {
            if (state.count > rootState.count) {
                commit(‘calculate‘, rootState.count);
            }
        },
    },
};


const moduleStore = new Vuex.Store({
    state: {
        count: 10,
    },
    modules: {
        accountA: moduleA,
    },
    mutations: {
        calculate(state, payload) {
            //root store中同名的calculate方法
            console.log(‘called!‘);
        },
    },

});
export default moduleStore;

app.js

/**
 * First we will load all of this project‘s JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */

require(‘./bootstrap‘);

window.Vue = require(‘vue‘);

// import store from ‘./Vuex/index‘;
import store from ‘./Vuex/module.js‘;

/**
 *
 * The following block of code may be used to automatically register your
 * Vue components. It will recursively scan this directory for the Vue
 * components and automatically register them with their "basename".
 *
 * Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
 */

// const files = require.context(‘./‘, true, /\.vue$/i)
// files.keys().map(key => Vue.component(key.split(‘/‘).pop().split(‘.‘)[0], files(key).default))

Vue.component(‘example-component‘, require(‘./components/ExampleComponent.vue‘).default);
Vue.component(‘store-component‘, require(‘./components/StoreComponent.vue‘).default);
Vue.component(‘module‘, require(‘./components/ModuleStore.vue‘).default);
/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */

const app = new Vue({
    el: ‘#app‘,
    store,
});

ModuleStore.vue:

<template>
    <div>
        <table>
            <tr>
                <th>name</th>
                <th>count</th>
                <th>price</th>
                <th>total</th>
            </tr>
            <tr>
                <td>{{name}}</td>
                <td>{{count}}</td>
                <td>{{price}}</td>
                <td>{{total}}</td>
            </tr>
        </table>
    </div>
</template>

<script>
    // import {mapState, mapActions, mapMutations, mapGetters} from ‘vuex‘;
    import {createNamespacedHelpers} from ‘vuex‘;

    const {mapState, mapActions, mapMutations, mapGetters} = createNamespacedHelpers(‘accountA‘);

    export default {
        name: "ModuleStore",
        data: function () {
            return {
                pName: ‘‘,
                pCount: 0,
                pPrice: 0,
                pTotal: 0,
            }
        },
        computed: {
            // ...mapState(‘accountA‘, [
            //     ‘name‘,
            //     ‘count‘,
            //     ‘price‘,
            //     ‘total‘,
            // ]),
            ...mapState([
                ‘name‘,
                ‘count‘,
                ‘price‘,
                ‘total‘,
            ]),
            ...mapActions([
                ‘useRootCountIfCountOverClocked‘,
            ]),

        },


    }
</script>

<style scoped>

</style>

web.php:

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get(‘/‘, function () {
    return view(‘welcome‘);
});

Auth::routes();

Route::get(‘/home‘, ‘‘)->name(‘home‘);
Route::get(‘/store-test‘, function (Request $request) {
    return view(‘store‘);
});
Route::get(‘/module‘, function (Request $request) {
    return view(‘module‘);
});

访问:http://vuex.test/module

Vuex module

相关推荐