vuex原理篇,以能否造轮子衡量学习效果
源代码链接
背景
使用vuex进行数据状态统一管理,已经有一段时间了。但是除了看文档粘贴、复制api之外,貌似并没有什么收获。
对于一些需要深入了解的内容:
actions怎么实现的
mutations怎么实现的
getters怎么实现的
...
于是乎,赶紧的,学呀学,输出一些手写vuex核心代码的过程。
Vuex核心api实践
归根结底,vuex
,就是一个vue的插件。
参考vue的插件机制
要实现的就是Vue.use(Vuex),这是vue安装插件的机制,需要Vuex对外暴露一个install方法,会把Vue传递给install这个函数
话不多说,开始...
项目准备
用vue-cli创建一个基本的项目
demo,github传送门
1. Vue插件实现
Vue插件要暴露install方法。并且在Vue.prototype
上挂载$store
。然后导出install
和Store
实例即可
class Store {
constructor() {
this.name = 'dajun'
}
}
function install(Vue) {
Vue.prototype.$store = new Store()
}
export default { Store, install }
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
console.log('store===>', store)
new Vue({
store,
render: h => h(App),
}).$mount('#app')
App.vue
created() {
console.log('this.$store', this.$store)
},
2. store的实现
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
console.log('store===>', store)
new Vue({
store,
render: h => h(App),
}).$mount('#app')
看看main.js
,store是通过new Vue传入
通过什么来让每个组件都能获取$store
实例呢?
我们需要使用mixin
在beforeCreate
来挂载,这样才能通过this.$option
获取传递进来的store
。
然后区分根组件和子组件
// vue插件机制
function install (vue) {
Vue = vue
Vue.mixin({
beforeCreate() {
// 根组件才有store
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
// 子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
这里有几个疑问点:
- mixin的作用是将mixin的内容混合到Vue的初始参数
$options
中; - 为什么是
beforeCreate
?这部分,要了解生命周期的初始化,created
时,$option
已经初始化好了。
关于几个疑问点: 有群友问到一个问题,我觉得很有意思,就是说直接通过$store.state.xx = ""。可以吗?其实这样赋值也不会有问题,而且state依旧是响应式的。那么为什么用commit来多此一举呢?
- vuex能够记录每一次state的变化记录,保存状态快照,实现时间漫游/回滚之类的操作。
关于全局变量
- 没有记录,二是不容易被发现
3. state
单项数据渲染:单纯的传参
class Store {
constructor(options = {}) {
this.state = options.state
}
}
import Vue from 'vue'
import Vuex from './myVuex'
Vue.use(Vuex)
const vuexObj = new Vuex.Store({
state: {
num: 0
}
})
export default vuexObj
打印侃侃, 没有问题
考虑到,state应该是响应式的,接着弄...
4. 响应式
比较简单的方式,就是借助Vue的响应式, new Vue()
并传入响应式data
let Vue
class Store {
constructor(options = {}) {
this.vm = new Vue({
data:{
state: options.state
}
})
}
}
// vue插件机制
let install = function (vue) {
Vue = vue
vue.mixin({
beforeCreate() {
// 根组件才有store
if (this.$options && this.$options.store) {
vue.prototype.$store = this.$options.store
} else {
// 子组件
vue.prototype.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
Store,
install
}
5. getter
可以使用Object.defineProperty
代理一个getter
,获取getter
的值,执行函数计算。最后挂载到$store.getters
defineGetters(options) {
this.getters = {}
let getters = options.getters || {}
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters, key, {
get:()=>{
console.log('this.state', this.state)
return getters[key](this.state)
}
})
})
}
const vuexObj = new Vuex.Store({
state: {
num: 0
},
getters: {
getNum(state) {
return state.num
}
},
})
6. mutation
参考一下vuex
中mutaion
的使用,只需要记录函数,commit的时候更新数据
class Store {
constructor(options = {}) {
// 增加响应式
this.vm = new Vue({
data:{
state: options.state
}
})
// mutations
this.defineMutations(options)
}
defineMutations(options) {
this.mutations = {}
let mutations = options.mutations || {}
Object.keys(mutations).forEach(mutationName=>{
this.mutations[mutationName] = (arg) => {
mutations[mutationName](this.state, arg)
}
})
}
commit = (method, arg) => {
console.log(`commit:mutations:${method}===>`, method)
this.mutations[method](arg)
}
// 为了能直接访问state
get state() {
return this.vm.state
}
}
7. action
action的实现,基本和mutation类似
class Store {
constructor(options = {}) {
// 增加响应式
this.vm = new Vue({
data:{
state: options.state
}
})
// actions
this.defineActions(options)
}
defineActions(opotions) {
this.actions = {}
let actions = opotions.actions
Object.keys(actions).forEach(actionName => {
this.actions[actionName] =(arg) => {
// 箭头函数,不绑定this。这里this就是$store实例
actions[actionName](this, arg)
}
})
}
dispatch(method, arg) {
console.log( `dispatch:actions:${method}===>`, method)
this.actions[method](arg)
}
commit = (method, arg) => {
console.log(`commit:mutations:${method}===>`, method)
this.mutations[method](arg)
}
// 为了能直接访问state
get state() {
return this.vm.state
}
}
注意点:
{commit}
就是对this
,store
实例的解构
最终效果
定眼一看,确实有点儿简陋(轮子再小,也是轮子?)
当然,还有mapState
,mapMutations
,modules
的实现。感兴趣,大家可以自行实践一下
demo链接
最后,附上demo链接