首先,Vuex有以下几个特征
一、主要特征
是一个对象
js
# 咱们使用的时候 都是通过
new Vuex.Store({xxx})
看到.Store
这 Vuex
十有八九就是一个对象了
有两个属性
其一是上面说的Store
,其二就是install
总结起来,Vuex
是一个对象,包含了两个属性,Store类
和install方法
二、准备工作
通过vue-cli
创建一个项目吧。项目结构反正我的是这样
项目地址已经放到github上了 有什么问题恳请各位大佬不吝赐教
三、分析流程
我们平常在Vue项目中使用,一般三步走。
第一:安装依赖
bash
npm i vuex
第二:引入vuex包
js
import Vuex from 'vuex'
第三:执行Vue.use()以及实例化
js
new Vuex.Store({xxxx})
Vue.use(Vuex)
四、大体框架
首先vuex
是一个对象,然后一个两个属性,一个Store
,一个install
js
class Store{
}
let install = function(){
}
let vuex = {
Store,
install
}
export defaul vuex
开始install方法
install是为Vue.use(vuex)
服务的,不然平常咱们项目中,this.$store.state.xx
,this.$store.getters.xx
怎么来的
主要作用:咱们vue
的每一个组件都可以拥有store
实例
话不多说,直接开干
js
let install = function(Vue){
Vue.mixin({
beforeCreate(){
if(this.$options.store){
this.$store = this.$options.store
}else {
this.$store = this.$parent && this.$parent.store
}
}
})
}
解释下
首先入参有一个Vue,可能刚开始都不是很明白,这是Vue.use
执行时带过来的。就是Vue
的实例
其次mixin的作用是将mixin的内容混入到vue的options中
再者生命周期函数为什么选用beforeCreate,因为created的时候,options已经初始化好了,没必要了
最后,如果是根组件,直接将传入的store挂载到根组件上,属性名$store
,如果不是,通过$parent
来进行获取
五、咱们的state
本次咱们主要实现state
、getter
、mutation
、action
等,有了一个思路,可以举一反三
js
class Store{
constructor(options){
this.state = options.state || {}
}
}
定义store文件
js
export default {
state:{
a:1
}
}
Vue文件中使用
html
<div>{{ this.$store.state.a }}</div>
接下来,启动项目,在浏览器中访问,就可以试试啦,一定要动手,不要觉得自己会了会了。
但是的但是,state是响应式的哦,所以还需要实现下state的响应式。
js
class Store{
constructor(options){
this.vm = new Vue({
data:{
state:options.state || {}
}
})
}
}
这样的话,咱们访问state数据就变成了this.$store.vm.state.xxx
,但是我们平常访问的样子都是this.$store.state.xxx
。我们需要稍微修改下
js
class Store{
constructor(options){
this.vm = new Vue({
data:{
state:options.state || {}
}
})
}
// 新添加的
get state(){
return this.vm.state
}
}
现在访问this.$store.state.xxx
就可以了,也实现了响应式。
六、咱们的getter
话不多说,直接上代码
js
class Store{
constructor(options){
this.vm = new Vue({
data:{
state:options.state || {}
}
})
// 新添加的
let getters = options.getters || {}
this.getters = {}
Object.keys(getters).forEach(item => {
Object.defineProperty(this.getters, item, {
get: () => {
return getters[item](this.state)
}
})
})
}
get state(){
return this.vm.state
}
}
说白了,通过this.$store.getters.xx
访问就是执行了getters
对应的函数。所以,为什么访问getters时不需要带
() ,原因就一行代码return getters[item](this.state)
,通过Object.defineProperty
get属性来实现的
七、咱们的mutation
js
class Store{
constructor(options){
this.vm = new Vue({
data:{
state:options.state || {}
}
})
let getters = options.getters || {}
this.getters = {}
Object.keys(getters).forEach(item => {
Object.defineProperty(this.getters, item, {
get: () => {
return getters[item](this.state)
}
})
})
// 新添加的
let mutations = options.mutations || {}
this.mutations = {}
Object.keys(mutations).forEach(item => {
this.mutations[item] = (args) => {
mutations[item](this.state, args)
}
})
}
get state(){
return this.vm.state
}
// 新添加的
commit(type,payload){
this.mutations[type](payload)
}
}
我们在平时的项目中通过什么来触发mutations
中的方法
答案当然是commit
。实现也比较简单,初始化时遍历传入进来的mutations
,遍历给this.mutations
赋值,存储类型
以及对应的回调函数
。通过this.$store.commit('xx',xx)
来触发时,调用对应的函数即可。这里没有做类型不存在的处理。
八、咱们的action
js
class Store{
constructor(options){
this.vm = new Vue({
data:{
state:options.state || {}
}
})
let getters = options.getters || {}
this.getters = {}
Object.keys(getters).forEach(item => {
Object.defineProperty(this.getters, item, {
get: () => {
return getters[item](this.state)
}
})
})
let mutations = options.mutations || {}
this.mutations = {}
Object.keys(mutations).forEach(item => {
this.mutations[item] = (args) => {
mutations[item](this.state, args)
}
})
// 新添加的
let actions = options.actions || {}
this.actions = {}
Object.keys(actions).forEach(item => {
this.actions[item] = (args) => {
// 此处传的是this
actions[item](this, args)
}
})
}
get state(){
return this.vm.state
}
// 新添加的
dispatch(type, payload) {
this.actions[type](payload)
}
commit(type,payload){
this.mutations[type](payload)
}
}
还记得项目中的写法吗?
js
actions:{
// { commit } 就是 store实例 所以上面要用this
// 反过来想 commit 是store的方法 所以你说自定义vuex是不是要传入this
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
调用的话跟平常项目一样,通过dispatch方法来触发,this.$store.dispacth('xxx',xxx)
但是此时启动项目,调用的话会报错
即找不到mutations
这个属性,那么问题来了,通过commit
方法来进行调用是正常的,dispatch
方法就不行了吗?十有八九是this
指向问题。
有两种方法
第一种:改commit为箭头函数
js
commit:(type,payload)=>{
this.mutations[type](payload)
}
箭头函数想必大家都太清楚了,不清楚可以自行查阅。
第二种:绑定this
js
constructor(){
// ....
const store = this
const { commit } = this
this.commit = function (type, payload) {
// this指向绑定住了
commit.call(store, type, payload)
}
}
通过call
方法把commit的this指向牢牢的绑定在store上,这样就不存在mutations
报错的情况了。
九、总结
以上就是个人重写vuex的一些小小见解,有不足之处,请各位大佬及时指正。