这是我参与8月更文挑战的第9天,活动详情查看: 8月更文挑战
组件内状态管理流程
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `<div>{{ count }}</div>`,
// actions
methods: {
increment () {
this.count++
}
}
})
状态管理
state
驱动应用的数据源view
以声明方式将state
映射到视图actions
响应在view
上的用户输入导致的状态变化
问题背景
三种组件间通信方式
- 父组件给子组件传值
- 子组件给父组件传值
- 不相关组件之间传值
1. 父组件给子组件传值
- 子组件中通过
props
接收数据 - 父组件中给子组件通过相应属性传值
// Child.vue
export default {
// props: ['title'],
props: {
title: String
}
}
// Parent.vue
<template>
<div>
<child title="My first Child Title"></child>
</div>
</template>
import Child from './Child.vue'
export default {
components: {
Child
}
}
2. 子组件给父组件传值
- 通过
$emit
触发自定义事件
3. 不相关组件之间传值
- 通过
event-bus
// eventbus.js
import Vue from 'vue'
export default new Vue()
// 01.vue
import bus from './eventbus.js'
add () {
this.value++
bus.$emit('numchange', this.value)
}
// 02.vue
import bus from './eventbus'
bus.$on('numchange', value => {
console.log('num == ' + value)
})
通过 ref 获取子组件
- $root
- $parent
- $children
$refs
ref 两个作用
- 在普通
HTML
标签上使用ref
, 获取到的是DOM
- 在组件标签上使用
ref
, 获取到的是组件实例
简易的状态管理方案
所遇问题
- 多个视图依赖同一状态
- 来自不同视图的行为需要变更同一状态
// 简易的状态管理
export default {
debug: true,
state: {
user: {
name: 'xiaomao',
age: 18,
sex: '男'
}
},
setUserNameAction (name) {
if (this.debug) {
console.log('setUserNameAction triggered: ', name)
}
this.state.user.name = name
}
}
什么是 Vuex
Vuex
是专门为Vue.js
设计的状态管理库Vuex
采用集中式的方式存储需要共享的状态Vuex
的作用是进行状态管理,解决复杂组件通信,数据共享Vuex
集成到了devtools
中,提供了time-travel
时光旅行历史回滚功能
什么情况下使用 Vuex
- 非必要的情况不要使用
Vuex
- 大型的单页面应用程序
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
Vuex 核心概念
Store
State
Getter
Mutation
Action
Module
Vuex 的基本结构
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
mutations:{},
actions: {},
modules: {}
})
// 在 main.js 中
import store from './store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
1. State
// store.js
export default new Vuex.Store({
state: {
count: 0
},
mutations:{},
actions: {},
modules: {}
})
// App.vue
<template>
<div id="app">
count: {{ $store.state.count }}
</div>
</template>
<script>
import { mapState } from 'vuex'
//...
computed: {
...mapState(['count'])
// 或者
...mapState({
count: 'count'
})
}
</script>
2. Getter
// store.js
export default new Vuex.Store({
state: {
count: 0,
msg: 'Hello World'
},
getters: {
reverseMsg (state) {
return state.msg.split('').reverse().join('')
}
},
mutations:{},
actions: {},
modules: {}
})
// App.vue
<h2>Getter: reverseMsg: {{ $store.getter.reverseMsg }}</h2>
<h2>Getter: reverseMsg: {{ reverseMsg }}</h2>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['reverseMsg'])
}
}
</script>
3. Mutation
// store.js
mutations: {
add (state, payload) {
state.count += payload
}
}
// App.vue
<button @click="$store.commit('add', 2)">Mutation</button>
<script>
import { mapMutations } from 'vuex'
methods: {
...mapMutations(['add'])
}
</script>
4. Action
// store.js
actions: {
increateAsync (content, payload) {
setTimeout(() => {
content.commit('add', payload)
}, 2000)
}
}
// App.vue
<h2>Action</h2>
<button @click="$store.dispatch('increateAsync', 5)"></button>
<script>
import { mapAction } from 'vuex'
methods: {
...mapActions(['increateAsync'])
}
</script>
5. Module
为了模块更好的封装性,建议打开命名空间 namespaced: true
// store/modules/cart.js
// store/modules/products.js
// store/index.js
// store/index.js
modules: {
products,
cart
}
// App.vue
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState('products', ['products'])
}
}
</script>
Vuex 严格模式
记住不要在生产模式下开启严格模式,严格格式会深度检查状态树,来检查不合归的状态表现,会影响性能
// store.js
export default new Vuex.Store({
strict: true
})
手写 myVuex.js
// myvuex/index.js
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
}