如何手写一个简易的 Vuex
前言
本文适合使用过Vuex的人阅读,来了解下怎么自己实现一个Vuex。
基本骨架
这是本项目的src/store/index.js文件,看看一般vuex的使用
importVuefrom'vue'
importVuexfrom'./myvuex'//引入自己写的vuex
import*asgettersfrom'./getters'
import*asactionsfrom'./actions'
importstatefrom'./state'
importmutationsfrom'./mutations'
Vue.use(Vuex)//Vue.use(plugin)方法使用vuex插件
//vuex导出一个类叫Store,并传入对象作为参数
exportdefaultnewVuex.Store({
state,
mutations,
actions,
getters,
})
Vue.use的用法:
安装Vue.js插件。如果插件是一个对象,必须提供install方法。如果插件是一个函数,它会被作为install方法。install方法调用时,会将Vue作为参数传入。这个方法的第一个参数是Vue构造器,第二个参数是一个可选的选项对象。
- 该方法需要在调用newVue()之前被调用。
- 当install方法被同一个插件多次调用,插件将只会被安装一次。
即是我们需要在./myvuex.js中导出install方法,同时导出一个类Store,于是第一步可以写出代码:
letVue=null
classStore{
constructor(options){}
}
functioninstall(_Vue){
Vue=_Vue//上面Store类需要能获取到Vue
}
exportdefault{
Store,
install,
}
install方法
当我们使用vuex的时候,每一个组件上面都有一个this.$store属性,里面包含了state,mutations,actions,getters等,所以我们也需要在每个组件上都挂载一个$store属性,要让每一个组件都能获取到,这里我们使用Vue.mixin(mixin),用法介绍如下:
全局注册一个混入,影响注册之后所有创建的每个Vue实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的Vue实例。
functioninstall(_Vue){
Vue=_Vue//install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue)
//实现每一个组件,都能通过this调用$store
Vue.mixin({
beforeCreate(){
//通过this.$options可以获取newVue({参数})传递的参数
if(this.$options&&this.$options.store){
//证明这个this是根实例,也就是newVue产生的那个实例
this.$store=this.$options.store
}elseif(this.$parent&&this.$parent.$store){
//子组件获取父组件的$store属性
this.$store=this.$parent.$store
}
},
})
}
state
由于Vuex是基于Vue的响应式原理基础,所以我们要让数据改变可刷新视图,则需要创建一个vue实例
classStore{
//options即是Vuex.Store({})传入的参数
constructor(options){
//vuex的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图
letvm=newVue({
data:{
state:options.state,
},
})
//state
this.state=vm.state
}
}
commit
我们使用vuex改变数据时,是触发commit方法,即是这样使用的:
this.$store.commit('eventName','参数');
所以我们要实现一个commit方法,把Store构造函数传入的mutations做下处理
classStore{
constructor(options){
//实现state...
//mutations
this.mutations={}//存储传进来的mutations
letmutations=options.mutations||{}
//循环取出事件名进行处理(mutations[事件名]:执行方法)
Object.keys(mutations).forEach(key=>{
this.mutations[key]=params=>{
mutations[key].call(this,this.state,params)//修正this指向
}
})
}
commit=(key,params)=>{
//key为要触发的事件名
this.mutations[key](params)
}
}
dispatch
跟上面的commit流程同理
classStore{
constructor(options={}){
//...
//actions
this.actions={}
letactions=options.actions||{}
Object.keys(actions).forEach(key=>{
this.actions[key]=params=>{
actions[key].call(this,this,params)
}
})
}
dispatch=(type,payload)=>{
this.actions[type](payload)
}
}
getters
getters实际就是返回state的值,在使用的时候是放在computed属性,每一个getter都是函数形式;
getters是需要双向绑定的。但不需要双向绑定所有的getters,只需要绑定项目中事件使用的getters。
这里使用Object.defineProperty()方法,它会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
classStore{
constructor(options={}){
//...
//getters
this.getters={}
letgetters=options.getters||{}
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters,key,{
get:()=>{
returngetters[key].call(this,this.state)
},
})
})
}
}
到此为止,已经可以使用我们自己写的vuex做一些基本操作了,但只能通过this.$store.xx的形式调用,故需要再实现方法。
map辅助函数
先来说说mapState
没有map辅助函数之前这样使用:
computed:{
count(){
returnthis.$store.state.count
}
}
当映射的计算属性的名称与state的子节点名称相同时,给mapState传一个字符串数组。
computed:{
//使用对象展开运算符将此对象混入到外部对象中
...mapState(['count'])
}
我们这里简单就只实现数组的情况
exportconstmapState=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(){
returnthis.$store.state[item]
}
})
returnobj
}
之后几个map辅助函数都是类似
- mapGetters
exportconstmapGetters=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(){
returnthis.$store.getters[item]
}
})
returnobj
}
- mapMutations
exportconstmapMutations=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(params){
returnthis.$store.commit(item,params)
}
})
returnobj
}
- mapActions
exportconstmapActions=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(payload){
returnthis.$store.dispatch(item,payload)
}
})
returnobj
}
完整代码
letVue=null
classStore{
constructor(options){
//vuex的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图
letvm=newVue({
data:{
state:options.state,
},
})
//state
this.state=vm.state
//mutations
this.mutations={}//存储传进来的mutations
letmutations=options.mutations||{}
Object.keys(mutations).forEach(key=>{
this.mutations[key]=params=>{
mutations[key].call(this,this.state,params)
}
})
//actions
this.actions={}
letactions=options.actions||{}
Object.keys(actions).forEach(key=>{
this.actions[key]=params=>{
actions[key].call(this,this,params)
}
})
//getters
this.getters={}
letgetters=options.getters||{}
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters,key,{
get:()=>{
returngetters[key].call(this,this.state)
},
})
})
}
commit=(key,params)=>{
this.mutations[key](params)
}
dispatch=(type,payload)=>{
this.actions[type](payload)
}
}
exportconstmapState=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(){
returnthis.$store.state[item]
}
})
returnobj
}
exportconstmapGetters=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(){
returnthis.$store.getters[item]
}
})
returnobj
}
exportconstmapMutations=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(params){
returnthis.$store.commit(item,params)
}
})
returnobj
}
exportconstmapActions=args=>{
letobj={}
args.forEach(item=>{
obj[item]=function(payload){
returnthis.$store.dispatch(item,payload)
}
})
returnobj
}
functioninstall(_Vue){
Vue=_Vue//install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue)
//实现每一个组件,都能通过this调用$store
Vue.mixin({
beforeCreate(){
//通过this.$options可以获取newVue({参数})传递的参数
if(this.$options&&this.$options.store){
//证明这个this是根实例,也就是newVue产生的那个实例
this.$store=this.$options.store
}elseif(this.$parent&&this.$parent.$store){
//子组件获取父组件的$store属性
this.$store=this.$parent.$store
}
},
})
}
exportdefault{
Store,
install,
}
以上就是如何手写一个简易的Vuex的详细内容,更多关于手写vuex的资料请关注毛票票其它相关文章!