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