vuex と永続性の使用

亚连
亚连オリジナル
2018-06-09 18:04:541888ブラウズ

この記事では主に vuex の使い方と状態を永続化する方法の詳細な説明を紹介しますので、参考にしてください。

Vuex は、Vue.js アプリケーション専用に開発された状態管理パターンです。集中ストレージを使用してアプリケーションのすべてのコンポーネントのステータスを管理し、対応するルールを使用してステータスが予測可能な方法で変化することを保証します。

私たちが vuex に出会ったとき、これが最初に見た公式のガイダンスでした。

この文から次の情報が得られます:

1. vuex は vue に特化した Flux であり、vue なしでも vuex を使用できます。逆に、reduxは存在せず、vueでもreactでもreduxが使えることがわかります。 vuex の「機能」はここに反映されており、redux には「汎用性」があります

2. vue 内のすべてのコンポーネントのステータスが vuex に存在することを示す

3。コンポーネントの状態の変化を追跡できます。

1. プロジェクト内の vuex ディレクトリの構築

上の図は、この記事の vue プロジェクト全体のスケルトンの一部です。

vuex は単一の状態ツリーを使用し、vue アプリケーションにはストア インスタンスが 1 つだけ含まれます。したがって、ストアを vue インスタンスにマウントすると、this.$store を通じて vuex のさまざまな部分を取得できます。

2.index.js

import vue from 'vue'
import vuex from 'vuex'
import mutations from './mutation'
import getters from './getter'

vue.use(vuex)

const state = {
 isLoading:false
}

export default new vuex.Store({
 state,
 getters,
 mutations
})

インデックスファイルでは、vuexに保存する必要がある状態の初期値を定義します。

たとえば、上記の状態オブジェクトに isLoading 属性を保存しました。この属性は、バックエンド API をリクエストしたときに表示される読み込み効果を識別するために使用する予定です。これは、リクエストが完了すると表示されなくなります。ユーザーの待ち心理。

3.Mutation (mutation.js)

一般的に、私たちのプロジェクトで最も一般的に使用されるメソッドは、mutation.js のメソッドです。 vuex でストアの状態を変更する唯一の方法は、ミューテーションを送信することであるためです。

vuex では、各ミューテーションには文字列イベント タイプ (ミューテーション タイプ) とコールバック関数 (ハンドラー) があります。

このコールバック関数は 2 つのパラメーターを受け入れることができ、最初のパラメーターは状態、2 番目のパラメーターは突然変異のペイロードです。

//...
mutations: {
 /**
 * @method:只传入state,修改loading状态
 * @param {bool} isLoading:loading状态
 */ 
 changeLoading(state) {
 state.isLoading = !state.isLoading
 }
}
store.commit('changeLoading')

mutations: {
 /**
 * @method:传入state和payload,修改loading状态
 * @param {bool} isLoading:loading状态
 */ 
 changeLoading(state,payload) {
 state.isLoading = payload.isLoading
 }
}
store.commit('changeLoading',{isLoading: true})

ミューテーションを送信するもう 1 つの方法は、type 属性を含むオブジェクトを直接使用することですが、上記の方法で処理した方がコードが読みやすくなるため、この方法はあまりお勧めしません。

store.commit({
 type: 'changeLoading',
 isLoading: true
})

4.mutation-types.js

複数人のコラボレーションが必要なプロジェクトでは、ミューテーション イベント タイプの代わりに定数を使用できます。これは、さまざまな Flux 実装で非常に一般的なパターンです。また、これらの定数を別のファイルに保存すると、共同開発が明確になります。

// mutation-types.js
export const CHANGE_LOADING= 'CHANGE_LOADING'

// mutation.js
import { CHANGE_LOADING} from './mutation-types'

export default{
 [CHANGE_LOADING](state,payload){
  state.isLoading = payload.isLoading
 },
}

mutation-typeでのイベントの種類の定義は、大まかに自分で定義した以下の仕様に従ってます

1. ミューテーションはイベントに似ているので、動詞で始まります

2 単語はアンダースコアで結ばれます

3 、vuex に保存された状態は RECORD でマークされます

4. ローカルにキャッシュされた状態は SAVE でマークされます

もちろん、突然変異を通じて突然変異の意図を知ることができる限り、この仕様を自分で定義することもできます。タイプ、良好です。

5. Getter (getter.js)

たとえば、上で述べたように、非同期リクエスト中にマスク レイヤーを使用して読み込みを表示する必要がある場合があります。私の読み込みでは、状態に基づいて読み込みステータスを表示する必要があります。ゲッターを使用せずに、計算されたプロパティを使用して処理することを選択します。

computed: {
 loadingTxt () {
 return this.$store.state.isLoading ? '加载中' : '已完成';
 }
}

ただし、読み込みは多くのコンポーネントで使用する必要があります。次に、関数をコピーするか、共有関数に抽出して複数の場所にインポートする必要がありますが、どちらの方法も理想的ではありません。

Getterを使えば全てが美しくなります。

//getter.js
export default {
 loadingTxt:(state) =>{
  return state.isLoading ? '加载中' : '已完成';
 } 
};

計算されたプロパティと同様に、ゲッターの戻り値は依存関係に従ってキャッシュされ、依存関係の値が変更された場合にのみ再計算されます。

そして、Getter は 2 番目のパラメーターとして他のゲッターを受け入れることもできます:

export default {
 loadingTxt:(state) =>{
  return state.isLoading ? '加载中' : '已完成';
 },
  isLoading:(state,getters) => {
  return 'string' === typeof getters.loadingTxt ? true : false;
 } 
};

ストア内のゲッターは、mapGetter 補助関数を通じてローカルに計算されたプロパティにマップできます

//组件中
import { mapGetters } from 'vuex'

export default {
 data(){
 return {
  //...
 }
 },
 computed: {
 // 使用对象展开运算符将 getter 混入 computed 对象中
 ...mapGetters([
  'loadingTxt',
  'isLoading',
  // ...
 ])
 }
}

6.Action (action.js)

関数アクション ミューテーションと同様に、どちらもストア内の状態を変更しますが、アクションとミューテーションには 2 つの違いがあります:

1. アクションは主に非同期操作を処理しますが、アクションはそのような制限を受けません。つまり、アクションでは同期操作と非同期操作の両方を処理できます

2. アクションは状態を変更し、最後にミューテーションを送信します

商品を追加するときは、ショッピング カートを例に挙げます。最初にバックエンドと通信する必要があります。これには SKU が関係するか、ユーザーが SKU を追加しただけで注文しなかった場合に発生します。

バックグラウンドの追加が成功すると、フロントエンドは新しく追加された製品を表示します。失敗した場合は、追加が失敗したことをユーザーに伝える必要があります。

const actions = {
 checkout ({ 
   state,
      commit,
   //rootState 
      }, products) {
 const savedCartItems = [...state.added]
 commit(SET_CHECKOUT_STATUS, null)
 // 置空购物车
 commit(SET_CART_ITEMS, { items: [] })
 shop.buyProducts(
  products,
   //成功
  () => commit(SET_CHECKOUT_STATUS, 'successful'),
  //失败
  () => {
  commit(SET_CHECKOUT_STATUS, 'failed')
  commit(SET_CART_ITEMS, { items: savedCartItems })
  }
 )
 }
}

7.module

当我们的项目足够大的时候,单一的状态树这个时候就会显得很臃肿了。因为需要用vuex进行状态管理的状态全部集中在一个state对象里面。

所以,当一个东西大了以后,我们就要想办法进行分割,同样的道理,我们熟知的分冶法和分布式其实也是基于这样的一个思想在里面。而vuex提供了module,我们就可以去横向的分割我们的store。

比如说,我在项目中需要去做一个购物车这样的东西,这在电商的项目中也是常见的需求。

//shopCart.js
import shop from '../../api/shop'
import {
 ADD_TO_CART,
 SET_CART_ITEMS,
 SET_CHECKOUT_STATUS
} from '../mutation-types'

const state = {
 added: [],
 checkoutStatus: null
}

/**
 * module getters
 * @param {Object} state:模块局部state
 * @param {Object} getters:模块局部getters,会暴露到全局
 * @param {Object} rootState:全局(根)state
 */
const getters = {
 checkoutStatus: state => state.checkoutStatus,
 cartProducts: (state, getters, rootState) => {
 return state.added.map(({ id, quantity }) => {
  const product = rootState.products.all.find(product => product.id === id)
  return {
  title: product.title,
  price: product.price,
  quantity
  }
 })
 },
 cartTotalPrice: (state, getters) => {
 return getters.cartProducts.reduce((total, product) => {
  return total + product.price * product.quantity
 }, 0)
 }
}

/**
 * module actions
 * @param {Object} state:模块局部state
 * @param {Object} getters:模块局部getters,会暴露到全局
 * @param {Object} rootState:全局(根)state
 */
const actions = {
 checkout ({ 
   state,
      commit,
   //rootState 
      }, products) {
 const savedCartItems = [...state.added]
 commit(SET_CHECKOUT_STATUS, null)
 // 置空购物车
 commit(SET_CART_ITEMS, { items: [] })
 shop.buyProducts(
  products,
   //成功
  () => commit(SET_CHECKOUT_STATUS, 'successful'),
  //失败
  () => {
  commit(SET_CHECKOUT_STATUS, 'failed')
  commit(SET_CART_ITEMS, { items: savedCartItems })
  }
 )
 }
}

/**
 * module mutations
 * @param {Object} state:模块局部state
 * @param payload:mutation的载荷
 */
const mutations = {
 //用id去查找商品是否已存在,
 [ADD_TO_CART] (state, { id }) {
 state.checkoutStatus = null
 const record = state.added.find(product => product.id === id)
 if (!record) {
  state.added.push({
  id,
  quantity: 1
  })
 } else {
  record.quantity++
 }
 },
 [SET_CART_ITEMS] (state, { items }) {
 state.added = items
 },
 [SET_CHECKOUT_STATUS] (state, status) {
 state.checkoutStatus = status
 }
}

export default {
 state,
 getters,
 actions,
 mutations
}

在module的定义的局部state,getters,mutation,action中,后三个都会暴露到全局的store中去,这样使得多个模块能够对同一 mutation 或 action 作出响应。就不需要在其他的模块中去定义相同的mutation或action了。

而这里的state是局部的。这也导致后来的持久化无法去处理用module分割后的state。

如同上面的module =》shopCart,

当我们无论是在index.js里面或者其他的module中,shopCart里面的getters或者action或者mutations,我们都可以去使用。

//test.js
const state = {
 isTest:false
};

const getters = {
 isTest :state => state.isTest,
 checkTestStatus:(state,getters) => {
 return getters.checkoutStatus;
 }
};

export default {
 state,
 getters,
}
//组件中
...mapGetters([
 'checkTestStatus'
])
//...
created(){
 this.checkTestStatus ;//null
}

如果说,我就想让我的module里面的定义的全部都是独享的。我们可以使用module的命名空间,通过设置namespaced: true。

//test.js
const getters = {
 // 在这个模块的 getter 中,`getters` 被局部化了
 // 你可以使用 getter 的第四个参数来调用 `rootGetters`
 someGetter (state, getters, rootState, rootGetters) {
 getters.someOtherGetter // 'test/someOtherGetter'
 rootGetters.someOtherGetter // 'someOtherGetter'
 },
 someOtherGetter: state => { ... }
};

const actions = {
 // 在这个模块中, dispatch 和 commit 也被局部化了
 // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
 someAction ({ dispatch, commit, getters, rootGetters }) {
 getters.someGetter // 'test/someGetter'
 rootGetters.someGetter // 'someGetter'

 dispatch('someOtherAction') // 'test/someOtherAction'
 dispatch('someOtherAction', null, { root: true }) // 'someOtherAction'

 commit('someMutation') // 'test/someMutation'
 commit('someMutation', null, { root: true }) // 'someMutation'
 },
 someOtherAction ({ state,commit }, payload) { ... }
}

export default {
 namespaced: true,
 state,
 getters,
 actions,
 mutations
}

8.持久化state的工具:vuex-persistedstate

用过vuex的肯定会有这样一个痛点,就是刷新以后vuex里面存储的state就会被浏览器释放掉,因为我们的state都是存储在内存中的。

而像登录状态这样的东西,你不可能一刷新就让用户重新去登录吧!所以,我们会去选择把状态存储到本地。

这样一来问题貌似是解决了,但是当我们需要使用的时候,我们就需要不断的从本地,通过getStore这样的方法去取得我们state。如果需要更新的话,我们又要在mutation里面通过setStore这样的方法去处理它。

虽然,我们的setStore都是在操作了state以后再去调用的,也就是说无论是通过vuex的logger或者vue的dev tool我们都是可以对local里面的状态进行跟踪的,但是,我们无法保证我们每次都记着去写setStore。

这样一来,在共享state的组件中,我们的代码可能就会是这样的。

import { getStore } from '@/util'
//组件中
mounted(){
 this.foo = getStore('foo');
 this.bar = getStore('bar');
 //.... 
}

那么,如何去改进呢?

我们能想到的就是,能不能让state不是保存在内存中,而是存储在本地。

而vuex-persistedstate做了这样的事情,它帮我们将store里面的state映射到了本地环境中。这样一来,我通过提交mutation改变的state,会动态的去更新local里面对应的值。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在bootstrap中如何实现table支持高度百分比

在vue 2.x 中使用axios如何封装的get 和post方法

使用node应用中timing-attack存在哪些安全漏洞

在vue组件传递对象中实现单向绑定,该怎么做?

以上がvuex と永続性の使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。