什么是Vuex
- Vuex是专门为Vue.js设计的状态管理库
- Vuex采用集中式的方式存储需要共享的状态
- Vuex的作用时进行状态管理,解决复杂组件通信,数据共享
- Vuex集成到了devtools中,提供了time-travel时光旅行历史回滚功能
什么情况下使用Vuex
- 非必要的情况下不要使用Vuex(项目不大,组件间数据共享不多)
- 大型的单页应用程序
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
Vuex核心概念
- Store:Vuex核心,每一个Vuex仅有一个store
- State:Vuex的状态(单一状态树),是响应式的
- Getter:Vuex中的计算属性
- Mutation:同步更改状态
- Action:异步操作
- Module:Vuex模块
Vuex的使用
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
strict: process.env.NODE_ENV !== 'production', // 非生产环境开启严格模式,不允许在mutation外修改state
state: {
count: 0,
msg: 'Hello Vuex'
},
getters: {
reverseMsg (state) {
return state.msg.split('').reverse().join('')
}
},
mutations: {
increate (state, payload) {
state.count += payload
}
},
actions: {
increateAsync (context, payload) {
setTimeout(() => {
context.commit('increate', payload)
}, 2000)
}
},
modules: {
products,
cart
}
})
store/modules/products.js
const state = {
products: [
{ id: 1, title: 'iPhone 11', price: 8000 },
{ id: 2, title: 'iPhone 12', price: 10000 }
]
}
const getters = {}
const mutations = {
setProducts (state, payload) {
state.products = payload
}
}
const actions = {}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
store/modules/cart.js
const state = {}
const getters = {}
const mutations = {}
const actions = {}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
App.vue
<template>
<div id="app">
<h1>Vuex - Demo</h1>
<!-- count:{{ $store.state.count }} <br>
msg: {{ $store.state.msg }} -->
<!-- count:{{ count }} <br>
msg: {{ msg }} -->
count:{{ num }} <br>
msg: {{ message }}
<h2>Getter</h2>
<!-- reverseMsg: {{ $store.getters.reverseMsg }} -->
reverseMsg: {{ reverseMsg }}
<h2>Mutation</h2>
<!-- <button @click="$store.commit('increate', 2)">Mutation</button> -->
<button @click="increate(3)">Mutation</button>
<h2>Action</h2>
<!-- <button @click="$store.dispatch('increateAsync', 5)">Action</button> -->
<button @click="increateAsync(6)">Action</button>
<h2>Module</h2>
<!-- products: {{ $store.state.products.products }} <br>
<button @click="$store.commit('setProducts', [])">Mutation</button> -->
products: {{ products }} <br>
<button @click="setProducts([])">Mutation</button>
<h2>strict</h2>
<button @click="$store.state.msg = 'Lagou'">strict</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
computed: {
// count: state => state.count
// ...mapState(['count', 'msg'])
...mapState({ num: 'count', message: 'msg' }),
...mapGetters(['reverseMsg']),
...mapState('products', ['products'])
},
methods: {
...mapMutations(['increate']),
...mapActions(['increateAsync']),
...mapMutations('products', ['setProducts'])
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
模拟Vuex
let _Vue = null
class Store {
constructor (options) {
const {
state = {},
getters = {},
mutations = {},
actions = {}
} = options
this.state = _Vue.observable(state)
this.getters = Object.create(null)
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](state)
})
})
this._mutations = mutations
this._actions = actions
}
commit (type, payload) {
this._mutations[type](this.state, payload)
}
dispatch (type, payload) {
this._actions[type](this, payload)
}
}
function install (Vue) {
_Vue = Vue
_Vue.mixin({
beforeCreate () {
if (this.$options.store) {
_Vue.prototype.$store = this.$options.store
}
}
})
}
export default {
Store,
install
}