vuex4


vuex-4

起始

  • vue4是為vue3做的適配,vue2用低於vue4版本的
  • vuex Stores 是具有響應性的,state的更新是有效且及時的
  • state是不能直接改變的,需要提交mutations,留下記錄。
import {createApp} from 'vue'
import {createStore} from 'vuex'

const store = createStore({
    state () {
        return {
            count: 0
        }
    },
    mutations: {
        increment (state) {
            state.count++
        }
    }
})
const app = createApp({})
app.use(store)

通過store.state.count或this.\(store.state.count獲取值,store.commit('increment')或this.\)store.commit('increment')更改state

核心觀點

state

  • vuex使用單一聲明樹,與data具有相同的規則
  • 可通過計算屬性獲取state的值
const Counter = {
    template: `<div>{{count}}</div>`,
    computed: {
        count() {
            // this.$store.state.count
            return store.state.count
        }
    } 
}
  • mapState能同時獲得多個state屬性
import {mapState} from 'vuex'
export default {
 computed: mapState({
    count: state => state.count,
    // 同上獲取值
    countAlias: 'count',
    // 將state值轉換為this的函數
    countPlusLocalState (state) {
    return state.count + this.localCount
}
})
}

當state中的屬性名稱和計算屬性名稱相同時,可用字符串數組接收

computed: mapState(['count'])

同其他計算屬性混合在一起時需用到對象傳播符...

computed: {
localComputed(){},
...mapState({})
}

Getter

  • stores的計算屬性
  • 對state中的屬性進行處理
const store = createStore({
 state: {
        todos: [
            {id: 1, text: '', done: true},
            {id:2 ,text: '', done: false}
        ]
    },
 getters: {
        doneTodos (state) {
            return state,todos.filter(todo => todo.done)
        }
    }
})
  • 獲取屬性值store.getters.doneTodos
  • getters可接受其他getters作為第二個參數
  • 以一個函數作為參數
getters: {
 getTodoById: (state) => (id) => {
        return state.todo.find(todo => todo.id === id)
    }
}
  • mapGetters 簡單的映射成為本地計算屬性
import {mapGetters} from 'vuex'
export default {
    computed: {
        ...mapGetters(['doneTodosCount','anotherGetter'])
//      ...mapGetters({doneCount: 'doneTodosCount'})
    }
}

Mutations

  • 對state中的屬性進行更新,以state作為第一個參數
  • 通過store.commit進行提交,執行該方法
  • commit中的負載參數作為mutations的第二個參數
  • commit的一個可替換方法
store.commit({
    type: 'increnment',
    amount: 10
})
mutations: {
    increment(state,payload){
        state.count += payload.amount
    }
}
  • 以一個常量作為函數名
mutations: {
    [SOME_MUTATION] (state) {}
}
  • 其內的函數必須是同步的
  • mapMutations幫助store.commit與組件內的方法進行映射
import {mapMutations} from 'vuex'

export default{
    methods: {
        // map `this.increment()` to `this.$store.commit('increment')`
        // `mapMutations` also supports payloads:
        // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
        ...mapMutations(['increment','incrementBy']),
        ...mapMutations({
        add: 'increment'
        })
    }
}

Actions

  • 代替Mutations,在actions可提交Mutations
  • actions可執行異步操作,其觸發方法store.dispatch
  • context.commit、context.state、context.getters、其他的actions的context.dispatch
actions: {
    increment(context) {
    context.commit('increment')
},
increment ({commit}) {
commit('increment')
}
}

負載參數

actions: {
    incrementAsync({commit}){
        setTimeout(() => {
    commit('increment')
        },1000)
    }
}
store.dispatch('incrementAsync', {
        amount:10
    })
store.dispatch({
    type:'incrementAsync',
    amount:10
})
actions: {
    checkout({commit, state}, products) {
        const saveCardItems = [...state.card.added]
        commit(types.CHECKOUT_REQUEST)
        shop.buyProducts(
        products,
        // 捕獲成功
        () => commit(types.CHECKOUT_SUCCESS),
        //捕獲失敗
        () => commit(tpes.CHECKOUT_FAILURE,saveCardItems)    
)
}
}
  • 組件中使用mapActions,組件方法與actions方法進行映射
import {mapActions} from 'vuex'
export default {
    methods: {
       ...mapActions(['increment','incrementBy']),
        ...mapActions({
        add: 'increment'
        })
    }
}
  • actions會返回異步函數Promise
actions: {
    actionsA({commit}) {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
            commit('someMutation')
            resolve()
            },1000)
        })
    }
     actionB ({ dispatch, commit }) {
        return dispatch('actionA').then(() => {
          commit('someOtherMutation')
        })
      }
}

store.dispatch('actionA').then(() => {
  // ...
})


  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for `actionA` to finish
    commit('gotOtherData', await getOtherData())
  }

modules

  • 每一個模塊都有獨立的state、mutations、actions
  • 每個模塊都有本地的state接收參數
const miduleA = {}
const muduleB = {}
const store = createStore({
    modules: {
    a: moduleA,
    b: moduleB
}
})
store.state.a
store.state.b
  • 在actions中本地context.state,根state:context.rootState,({ state, commit, rootState })
  • 在getters中,root state (state, getters, rootState)
  • 模塊的命名空間, namespaced: true,getters、actions將會接收本地化的getters、dispatch、commit
  • { root: true }作為dispatch和commit的第三個參數.
  • 注冊全局actions
{
  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
  }),
  ...mapGetters([
    'some/nested/module/someGetter', // -> this['some/nested/module/someGetter']
    'some/nested/module/someOtherGetter', // -> this['some/nested/module/someOtherGetter']
  ])
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}
// 簡化
{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}
import { createNamespacedHelpers } from 'vuex'

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

export default {
  computed: {
    // look up in `some/nested/module`
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // look up in `some/nested/module`
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}
// get namespace value via plugin option
// and returns Vuex plugin function
export function createPlugin (options = {}) {
  return function (store) {
    // add namespace to plugin module's types
    const namespace = options.namespace || ''
    store.dispatch(namespace + 'pluginAction')
  }
}
  • 動態注冊模塊
import { createStore } from 'vuex'

const store = createStore({ /* options */ })

// register a module `myModule`
store.registerModule('myModule', {
  // ...
})

// register a nested module `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})
// 移除模塊
store.unregisterModule(moduleName)
// 檢測是否有該模塊
store.hasModule(moduleName)
//actions, mutations and getters都會加入到store中,但是state沒有
store.registerModule('a', module, { preserveState: true })
  • state會造成state污染,所以采用data的形式,
state: () => ({
    foo:'bar'
})

深層次

應用結構

├── index.html
├── main.js
├── api
│ └── ... # abstractions for making API requests
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # where we assemble modules and export the store
├── actions.js # root actions
├── mutations.js # root mutations
└── modules
├── cart.js # cart module
└── products.js # products module

Composition API

import { computed } from 'vue'
import { useStore } from 'vuex'

export default {
  setup () {
    const store = useStore()

    return {
      // access a state in computed function
      count: computed(() => store.state.count),

      // access a getter in computed function
      double: computed(() => store.getters.double)
        
    // access a mutation
      increment: () => store.commit('increment'),
    
       // access an action
       asyncIncrement: () => store.dispatch('asyncIncrement')

    }
  }
}

Plugins

const myPlugin = (store) => {
  // called when the store is initialized
  store.subscribe((mutation, state) => {
    // called after every mutation.
    // The mutation comes in the format of `{ type, payload }`.
  })
}
const store = createStore({
  // ...
  plugins: [myPlugin]
})
  • 使用mutations、actions
export default function createWebSocketPlugin (socket) {
  return (store) => {
    socket.on('data', data => {
      store.commit('receiveData', data)
    })
    store.subscribe(mutation => {
      if (mutation.type === 'UPDATE_DATA') {
        socket.emit('update', mutation.payload)
      }
    })
  }
}
const plugin = createWebSocketPlugin(socket)

const store = createStore({
  state,
  mutations,
  plugins: [plugin]
})
  • 快照
const myPluginWithSnapshot = (store) => {
  let prevState = _.cloneDeep(store.state)
  store.subscribe((mutation, state) => {
    let nextState = _.cloneDeep(state)

    // compare `prevState` and `nextState`...

    // save state for next mutation
    prevState = nextState
  })
}
  • 快照只能在開發環境中使用
const store = createStore({
  // ...
  plugins: process.env.NODE_ENV !== 'production'
    ? [myPluginWithSnapshot]
    : []
})
  • logger插件,createVuexLogger全局方法
import { createLogger } from 'vuex'

const store = createStore({
  plugins: [createLogger()]
})
  • 嚴格模式
const store = createStore({
  // ...
  strict: true
})
const store = createStore({
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

表單處理

  • 第一種方式
<input :value="message" @input="updateMessage">
// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}
  • 第二種方式
<input v-model="message">
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

動態加載和熱重新加載所有模塊

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

// Load all modules.
function loadModules() {
  const context = require.context("./modules", false, /([a-z_]+)\.js$/i)

  const modules = context
    .keys()
    .map((key) => ({ key, name: key.match(/([a-z_]+)\.js$/i)[1] }))
    .reduce(
      (modules, { key, name }) => ({
        ...modules,
        [name]: context(key).default
      }),
      {}
    )

  return { context, modules }
}

const { context, modules } = loadModules()

Vue.use(Vuex)

const store = new Vuex.Store({
  modules
})

if (module.hot) {
  // Hot reload whenever any module changes.
  module.hot.accept(context.id, () => {
    const { modules } = loadModules()

    store.hotUpdate({
      modules
    })
  })
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM