ブログ
これまでに経験してきたプロジェクトで気になる技術の情報を紹介していきます。
vue3 vuex
Okuda
3 years
Vuexとは、Vue.jsアプリケーション用の状態管理ライブラリ
アプリケーションが保持するデータで、どのコンポーネントからでもVuex内に保持するデータへのアクセスが可能になリ、一元管理ができるもの
vuexのインストール
CDN
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@next"></script>
NPM
npm i vuex@next
package.json
//...
"dependencies": {
"vue-router": "^4.0.10",
"vuex": "^4.0.2" // 追加されている
}
//...
vueの準備
<html>
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@next"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
</body>
</html>
<script>
// appを作成
const app = {
data() {
return {
message: 'hello'
}
}
}
// vueにマウント
Vue.createApp(app)
.mount('#app')
</script>
vuex store作成
ストアには以下の4つコンセプトのがある
- ステート
- ゲッター
- ミューテーション
- アクション
//...
// storeを追加
const store = new Vuex.createStore({
/*
* ステート(データの入れ物)
* state:コンポーネントでいうdata
*/
state() {},
/*
* ゲッター(ステートから算出される値)
* getters:コンポーネントでいうcomputed的なもの
*/
getters() {},
/*
* ミューテーション(同期処理)
* mutations:コンポーネントでいうmethod(setter)
* stateを変更できるのはミューテーションのみ
* 同期処理でなければならない
* commitで呼び出す
*/
mutations() {},
/*
* アクション(非同期処理)
* actionのコミットを使うことでミューテーションを呼び出す(コンポーネントには無い概念)
* 状態を変更するのではなく、ミューテーションをコミット
* 任意の非同期処理を含むことができる
* dispatchで呼び出す
*/
actions() {},
})
// appを作成
const app = {
//...
}
Vue.createApp(app)
.use(store) // storeを追加
.mount('#app')
//...
ステート
- ステート(データの入れ物)
- state:コンポーネントでいうdata
ステートの設定と呼び出し
// storeを追加
const store = new Vuex.createStore({
// stateにcountを作成
state: {
count: 0
},
});
// appを作成
const app = {
data() {
return {
message: 'vuex!'
}
},
// ステートを呼び出し
computed: {
// 標準的な呼び出し
count1() {
return this.$store.state.count;
},
}
}
//...
<div id="app">
<h1>{{message}}</h1>
<h2>state</h2>
<!-- カウントを表示 -->
<div>{{count1}}</div>
</div>
mapState ヘルパ
mapState
ヘルパを使用する場合はいくつかの方法がある
// mapStateをインポート
import {
mapState
} from "vuex";
// storeを追加
const store = new Vuex.createStore({
// stateにcountを作成
state: {
count: 0
},
});
// appを作成
const app = {
data() {
return {
message: 'test!'
}
},
// ステートを呼び出し
computed: {
// 標準的な呼び出し
count1() {
return this.$store.state.count;
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapState({
// アロー関数
count2: (state) => state.count,
// 文字列では呼び出し
count3: "count",
}),
// 配列の文字列で記述
...Vuex.mapState([
"count"
]),
}
}
//...
<div id="app">
<h1>{{message}}</h1>
<h2>state</h2>
<div>count1: {{count1}}</div>
<div>count2: {{count2}}</div>
<div>count3: {{count3}}</div>
<div>count: {{count}}</div>
</div>
ゲッター
- ゲッター(ステートから算出される値)
- getters:コンポーネントでいうcomputed的なもの
ゲッターの設定と呼び出し
const store = new Vuex.createStore({
state: {
count: 0
// stateにtodos配列を作成
todos: [{
id: 1,
text: '1 text true',
done: true
},
{
id: 2,
text: '2 text true',
done: true
},
{
id: 3,
text: '3 text false',
done: false
}
]
},
// ゲッターを追加
getters: {
// done=trueのものを取得
doneTodos(state) {
return state.todos.filter(todo => todo.done)
},
// done=trueをカウント
doneTodosCount(state, getters) {
return getters.doneTodos.length
},
// idで取得するファンクションを返す
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
});
const app = {
//...
computed: {
//...
// 標準的な取得
doneTodos1() {
console.log(this.$store.getters)
return this.$store.getters.doneTodos
},
doneTodosCount1() {
return this.$store.getters.doneTodosCount
},
getTodoById1() {
return this.$store.getters.getTodoById
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapGetters({
doneTodos2: "doneTodos",
doneTodosCount2: "doneTodosCount",
getTodoById2: "getTodoById",
}),
// 配列の文字列で記述
...Vuex.mapGetters([
"doneTodos",
"doneTodosCount",
"getTodoById",
]),
}
}
//...
<div id="app">
<h2>getters</h2>
<!-- 標準的な取得 -->
<div>doneTodos1: {{doneTodos1}}</div>
<div>doneTodosCount1: {{doneTodosCount1}}</div>
<div>getTodoById1(3): {{getTodoById1(3)}}</div>
<!-- ヘルパをオブジェクトスプレッド演算子で記述 -->
<div>doneTodos2: {{doneTodos2}}</div>
<div>doneTodosCount2: {{doneTodosCount2}}</div>
<div>getTodoById2(3): {{getTodoById2(3)}}</div>
<!-- 配列の文字列で記述 -->
<div>doneTodos: {{doneTodos}}</div>
<div>doneTodosCount: {{doneTodosCount}}</div>
<div>getTodoById(3): {{getTodoById(3)}}</div>
</div>
ミューテーション
- mutations:コンポーネントでいうmethod(setter)
- stateを変更できるのはミューテーションのみ
- 同期処理でなければならない
- commitで呼び出す
ミューテーションの設定と呼び出し
const store = new Vuex.createStore({
//...
// ミューテーションを追加
mutations: {
// countを増加させる
increment(state, n = 1) {
state.count += n
}
}
});
const app = {
//...
methods: {
// mutations
// 標準的な取得
increment1(n) {
this.$store.commit("increment", n);
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapMutations({
increment2: "increment",
}),
// 配列の文字列で記述
...Vuex.mapMutations(["increment"]),
},
}
//...
<div id="app">
<h2>mutations</h2>
<button @click="increment1(1)">increment1(1)</button>
<button @click="increment2(10)">increment2(10)</button>
<button @click="increment(100)">increment(100)</button>
</div>
アクション
- アクション(非同期処理)
- actionのコミットを使うことでミューテーションを呼び出す(コンポーネントには無い念)
- 状態を変更するのではなく、ミューテーションをコミット
- 任意の非同期処理を含むことができる
- dispatchで呼び出す
アクションの設定と呼び出し
const store = new Vuex.createStore({
//...
//アクションを追加
actions: {
delayIncrement(context, n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('increment', n)
resolve()
}, 2000)
})
}
}
});
const app = {
//...
methods: {
// 標準的な取得
delayIncrement1(n) {
this.$store.dispatch("delayIncrement", n);
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapActions({
delayIncrement2: "delayIncrement",
}),
// 配列の文字列で記述
...Vuex.mapActions(["delayIncrement"]),
},
}
//...
<div id="app">
<h2>actions</h2>
<button @click="delayIncrement1(1)">delayIncrement1(1)</button>
<button @click="delayIncrement2(10)">delayIncrement2(10)</button>
<button @click="delayIncrement(100)">delayIncrement(100)</button>
</div>
モジュール
モジュールを作成して分けることができる
モジュールの作成
以下の部分を moduleA
に書き換えます
//...
const store = new Vuex.createStore({
namespaced: true,
state: {
//...
},
getters: {
//...
},
mutations: {
//...
},
actions: {
//...
}
});
このように編集
//...
const moduleA = {
// 名前をストア別に区別できるようにネームスペースを使う
namespaced: true,
state: {
//...
},
getters: {
//...
},
mutations: {
//...
},
actions: {
//...
}
};
モジュールをマージしてストアを作成
const moduleA = {
//...
};
// モジュールBも追加
const moduleB = {
// 名前をストア別に区別できるようにネームスペースを使う
namespaced: true,
state: {
count: 99999,
},
}
// モジュールをマージしてストアを作成
const store = new Vuex.createStore({
// モジュールに登録
modules: {
moduleA,
moduleB,
}
})
呼び出しにネームスペースを追加
モジュールしてネームスペースで区別できるようにしたので、呼び出し方も変更する
//...
// ステートを呼び出し
computed: {
// state
// 標準的な呼び出し
count1() {
return this.$store.state.moduleA.count
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapState('moduleA', {
// アロー関数
count2: (state) => state.count,
// 文字列では呼び出し
count3: "count",
}),
// 配列の文字列で記述
...Vuex.mapState('moduleA', [
"count"
]),
// moduleB
...Vuex.mapState("moduleB", {
countB: "count"
}),
// getters
// 標準的な取得
doneTodos1() {
return this.$store.getters["moduleA/doneTodos"];
},
doneTodosCount1() {
return this.$store.getters["moduleA/doneTodosCount"]
},
getTodoById1() {
return this.$store.getters["moduleA/getTodoById"]
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapGetters('moduleA', {
doneTodos2: "doneTodos",
doneTodosCount2: "doneTodosCount",
getTodoById2: "getTodoById",
}),
// 配列の文字列で記述
...Vuex.mapGetters('moduleA', [
"doneTodos",
"doneTodosCount",
"getTodoById",
]),
},
methods: {
// mutations commit
// 標準的な取得
increment1(n) {
this.$store.commit("moduleA/increment", n)
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapMutations("moduleA", {
increment2: "increment",
}),
// 配列の文字列で記述
...Vuex.mapMutations("moduleA", ["increment"]),
// action dispatch
// 標準的な取得
delayIncrement1(n) {
this.$store.dispatch("moduleA/delayIncrement", n);
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapActions("moduleA", {
delayIncrement2: "delayIncrement",
}),
// 配列の文字列で記述
...Vuex.mapActions("moduleA", ["delayIncrement"]),
},
//...
<!-- 追加 -->
<h2>moduleB</h2>
<div>moduleB count: {{countB}}</div>
参考コード
<html>
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@next"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
<h2>state</h2>
<div>count1: {{count1}}</div>
<div>count2: {{count2}}</div>
<div>count3: {{count3}}</div>
<div>count: {{count}}</div>
<h2>getters</h2>
<div>doneTodos1: {{doneTodos1}}</div>
<div>doneTodosCount1: {{doneTodosCount1}}</div>
<div>getTodoById1(3): {{getTodoById1(3)}}</div>
<div>doneTodos2: {{doneTodos2}}</div>
<div>doneTodosCount2: {{doneTodosCount2}}</div>
<div>getTodoById2(3): {{getTodoById2(3)}}</div>
<div>doneTodos: {{doneTodos}}</div>
<div>doneTodosCount: {{doneTodosCount}}</div>
<div>getTodoById(3): {{getTodoById(3)}}</div>
<h2>mutations</h2>
<button @click="increment1(1)">increment1(1)</button>
<button @click="increment2(10)">increment2(10)</button>
<button @click="increment(100)">increment(100)</button>
<h2>actions</h2>
<button @click="delayIncrement1(1)">delayIncrement1(1)</button>
<button @click="delayIncrement2(10)">delayIncrement2(10)</button>
<button @click="delayIncrement(100)">delayIncrement(100)</button>
<h2>moduleB</h2>
<div>moduleB count: {{countB}}</div>
</div>
</body>
</html>
<script>
const moduleA = {
namespaced: true,
state: {
count: 0,
// stateにtodos配列を作成
todos: [{
id: 1,
text: '1 text true',
done: true
},
{
id: 2,
text: '2 text true',
done: true
},
{
id: 3,
text: '3 text false',
done: false
}
],
},
getters: {
// done=trueのものを取得
doneTodos(state) {
return state.todos.filter(todo => todo.done)
},
// done=trueをカウント
doneTodosCount(state, getters) {
return getters.doneTodos.length
},
// idで取得するファンクションを返す
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
},
mutations: {
increment(state, n = 1) {
state.count += n
}
},
actions: {
delayIncrement(context, n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('increment', n)
resolve()
}, 2000)
})
}
}
};
const moduleB = {
namespaced: true,
state: {
count: 99999,
},
}
const store = new Vuex.createStore({
// モジュールに登録
modules: {
moduleA,
moduleB,
}
})
// appを作成
const app = {
data() {
return {
message: 'vuex!'
}
},
// ステートを呼び出し
computed: {
// state
// 標準的な呼び出し
count1() {
return this.$store.state.moduleA.count
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapState('moduleA', {
// アロー関数
count2: (state) => state.count,
// 文字列では呼び出し
count3: "count",
}),
// 配列の文字列で記述
...Vuex.mapState('moduleA', [
"count"
]),
// moduleB
...Vuex.mapState("moduleB", {
countB: "count"
}),
// getters
// 標準的な取得
doneTodos1() {
return this.$store.getters["moduleA/doneTodos"];
},
doneTodosCount1() {
return this.$store.getters["moduleA/doneTodosCount"]
},
getTodoById1() {
return this.$store.getters["moduleA/getTodoById"]
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapGetters('moduleA', {
doneTodos2: "doneTodos",
doneTodosCount2: "doneTodosCount",
getTodoById2: "getTodoById",
}),
// 配列の文字列で記述
...Vuex.mapGetters('moduleA', [
"doneTodos",
"doneTodosCount",
"getTodoById",
]),
},
methods: {
// mutations commit
// 標準的な取得
increment1(n) {
this.$store.commit("moduleA/increment", n)
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapMutations("moduleA", {
increment2: "increment",
}),
// 配列の文字列で記述
...Vuex.mapMutations("moduleA", ["increment"]),
// action dispatch
// 標準的な取得
delayIncrement1(n) {
this.$store.dispatch("moduleA/delayIncrement", n);
},
// ヘルパをオブジェクトスプレッド演算子で記述
...Vuex.mapActions("moduleA", {
delayIncrement2: "delayIncrement",
}),
// 配列の文字列で記述
...Vuex.mapActions("moduleA", ["delayIncrement"]),
},
}
Vue.createApp(app)
.use(store) // storeを追加
.mount('#app')
</script>
vue3 vuex
vue3 vuex
2021-11-18 10:36:04
2021-11-19 09:09:31
コメントはありません。