Vue最核心的两个功能:数据驱动和组价化。
组件化开发的优势就是:更快的开发效率和更好的可维护性
状态管理包含以下几部分:
- state:驱动应用的数据源
- view:以声明的方式将state映射到视图
- actions:响应在view上的用户输入导致的状态变化
什么是vuex
- Vuex是专门为Vue.js设计的状态管理库
- 它采用集中式的方式存储需要共享的数据
- 从使用的角度,他就是一个JavaScript库
- 他的作用是进行状态管理,解决复杂组件通信,数据共享
什么情况下使用Vuex
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
- 注:Vuex不要来用,否则会让应用变得很麻烦
state
Vuex使用单一状态数,用一个对象就包含了全部的应用层级状态。使用 mapState 简化 State 在视图中的使用,mapState 返回计算属性
// 该方法是 vuex 提供的,所以使用前要先导入
import { mapState } from 'vuex'
// mapState 返回名称为 count 和 msg 的计算属性
// 在模板中直接使用 count 和 msg
computed: {
// ...mapState(['count', 'msg']),
// 如果当前视图中已经有了 count 和 msg,如果使用上述方式的话会有命名冲突,解决的方式:
...mapState({ num: count, message: msg })
}
Getter
Getter 就是 store 中的计算属性,使用 mapGetter 简化视图中的使用
import { mapGetter } from 'vuex'
computed: {
...mapGetter(['reverseMsg']), // 改名,在模板中使用 reverse
...mapGetter({ reverse: 'reverseMsg' })
}
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
使用 Mutation 改变状态的好处是,集中的一个位置对状态修改,不管在什么地方修改,都可以追踪到状态的修改。可以实现高级的 time-travel 调试功能
import { mapMutations } from 'vuex'
methods: {
...mapMutations(['increate']), // 传对象解决重名的问题
...mapMutations({ increateMut: 'increate' })
}
Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
import { mapActions } from 'vuex'
methods: {
...mapActions(['increate']), // 传对象解决重名的问题
...mapActions({ increateAction: 'increate' })
}
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
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/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({
// 所有的状态变更必须通过mutations,但这仅仅只是约定
// 开启严格模式之后如果在组件中直接修改state会抛出错误
// 不要在生产环境下开启严格模式,严格模式会深度检查状态数,来检查不和规的改变,会影响性能
strict: process.env.NODE_ENV !== 'production',
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
}
})
在组件中使用
computed: {
...mapState('cart', ['cartProducts']), // 开启命名空间后,第一个参数是命名空间的名称,第二个参数才是要引入的属性集合
...mapGetters('cart', ['totalCount', 'totalPrice'])
},
methods: {
...mapMutations('cart', ['deleteFromCart'])
}
本地存储
Vuex插件介绍
- Vuex的插件就是一个函数
- 这个函数接收一个store的参数(在这个函数里面注册一个参数,让他在所有的mutation结束之后执行)
定义插件在store实例之前
然后再store里面注册插件
插件中的mutation的结构
模拟实现Vuex
let _Vue = null
class Store {
constructor (options) {
const {
state = {},
getters = {},
mutations = {},
actions = {}
} = options
this.state = _Vue.observable(state)
this.getters = Object.create(null) // 把创建对象的原型设置为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
}
知识点:
- Object.create(null)
在Vue和Vuex的源码中,作者都使用了Object.create(null)来初始化一个新对象。为什么不用更简洁的{}呢?- Object.create()的定义
Object.create(proto,[propertiesObject])
- proto:新创建对象的原型对象
- propertiesObject:可选。要添加到新对象的可枚举(新添加的属性是其自身的属性,而不是其原型链上的属性)的属性。
- Object.create()的定义
- Object.create(null)和{}的区别
分别打印Object.create(null)和{}
从上图可以看到,{} 新创建的对象继承了Object自身的方法,如hasOwnProperty、toString等,在新对象上可以直接使用。Object.create(null) 新创建的对象原型链上没有任何属性,也就是没有继承Object的任何东西,此时如果我们调用toString()会报Uncaught TypeError的错误。第一个参数使用了null。也就是说将null设置成了新创建对象的原型,自然就不会有原型链上的属性 - Object.create(null)的使用场景
- 在我们使用for…in循环的时候会遍历对象原型链上的属性,使用create(null)就不必再对属性进行检查了,当然,我们也可以直接使用Object.keys[]
- 你需要一个非常干净且高度可定制的对象当作数据字典的时候;
- 想节省hasOwnProperty带来的一丢丢性能损失并且可以偷懒少些一点代码的时候