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
})
})
}