Vuex 使用解析+原理模拟

9 篇文章 0 订阅
6 篇文章 0 订阅

Vuex 注册:

// main.js 中导入 store,并在 Vue 中注册
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

Vuex 配置:

// store 文件下 index.js 文件进行 vuex 的配置:
import Vue from 'vue'
import Vuex from 'vuex'// 导入 Vuex 对象

Vue.use(Vuex)// 调用 vuex 的 install 方法

// store 是一个构造函数,接受五个属性为参数
export default new Vuex.Store({
    // 状态管理仓库,将需要统一管理的状态存储到 state 中
    state: {},
    // Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
    getters: {},
    // 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
    mutations: {},
    // Action 提交的是 mutation,而不是直接变更状态; Action 可以包含任意异步操作
    actions: {},
    // Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
    modules: {},
})

简而言之:vuex 中有五个属性进行状态管理,

  • state 用于统一储存 需要管理的状态;
  • getters 相当于 vue 的computed,监听状态变化执行响应操作;
  • mutations 更改状态的同步操作;
  • actions 更改状态的异步操作,最终提交 mutations;
  • modules 可以简单理解为分模块状态管理,模块中具备 vuex 的所有功能;

Vuex 基本使用:

mapState , mapGetters , mapMutations 辅助函数:简化使用 $store.state…

  • state
// store 文件下 index.js
export default new Vuex.Store({
    state: {
        count: 0,
        msg: "hello VueX"
    }
})

// ---------- state 使用 -----------
// 1. $store.state.count 
// 2. 简化使用:可直接使用 count
import { mapState } from "vuex"
export default {
    //strict:true,// 严格模式开启,只是抛出异常,生产环境不建议开启严格模式,会深度检查状态树影响性能
    strict: process.env.NODE_ENV !== "production",
    computed: {
        // mapState 会返回一个对象,包含两个计算属性对应的方法如下:
        // {count: state => state.count,..}
        // mapState 的参数可以接收数组,也可以接收对象
        // ...mapState(['count','msg'])
        // 如果 data 中已存在 count msg 设置属性名
        ...mapState({ num: 'count', message: 'msg' })
    }
}
  • getters
// store 文件下 index.js
export default new Vuex.Store({
    state: {
        count: 0,
        msg: "hello VueX"
    },
    // getter 相当于组件的计算属性,对state的数据进行处理再展示
    getters: {
        reverseMsg(state) {
            return state.msg.split('').reverse().join('')
        }
    }

})

// ---------- getters 使用 -----------
// 1. $store.getters.reverseMsg 
// 2. 简化使用:可直接使用 reverseMsg
import { mapGetters } from "vuex";
export default {
    computed: {
        // mapGetters 会返回一个对象,包含两个计算属性对应的方法如下:
        // count: state => state.count
        // ...mapGetters(['reverseMsg','...'])
        // 如果 data 中已存在 reverseMsg 设置属性名
        ...mapGetters({ msg: 'reverseMsg', '...': '...' })

    }
}
  • mutations
// store 文件下 index.js
// 视图中修改状态 mutation
export default new Vuex.Store({
    // 点击按钮增加数值
    mutation: {
        // state 状态
        // payload 额外参数,可为对象
        increate(state, payload) {
            state.count += payload
        }
    }

})

// ---------- mutation 使用 ----------- devtools 中方便调试
// mutation 本质是方法,使用时需要commit方法提交,使用 map 函数将 mutation 映射到当前组建的 mes 中
// 每次点击按钮,count 值加一
// 1. @click = "$store.commit('increate',1)"
// 2. 简化使用:可直接使用  @click = "inc(1)"
// mapMutations 返回一个对象,对应 mutation 中的方法,不再是计算属性,需要放到 methods 中 
import { mapMutations } from "vuex";
export default {
    methods: {
        ...mapMutations({ inc: 'increate', '...': '...' })
    }
}
  • actions
// store 文件下 index.js
// 执行异步状态 actions
export default new Vuex.Store({
    actions: {
        // context 第一个参数:包含state commit getters 成员
        increateAsync(context, payload) {
            setTimeout(() => {
                // 提交到 mutations
                context.commit('increate', payload)
            }, 2000)
        }
    }
})

// ---------- action 使用 ----------- 
// 1. @click = "$store.dispatch.('increateAsync',1)"
// 2. 简化使用:可直接使用  @click = "inc(1)"
// mapActions 返回一个对象,对应 Actions 中的方法,不再是计算属性,需要放到 methods 中 
import { mapActions } from "vuex";
export default {
    methods: {
        ...mapActions({ inc: 'increateAsync', '...': '...' })
    }
}
  • modles
// 导入 vuex 的模块,模块的内容与主模块相同
import cart from "./module/cart";
import products from "./module/products";
export default new Vuex.Store({
    modules: {
        cart,
        products
    }

})

// ---------- modules 使用 ----------- 
// 1. @click = "$store.commit('setNums',[])

// 2. cart 存储在 $store.state 中,调用使用 $store.state.cart... 调用;mutation 调用如上
// 3. 命名空间:如 modules 中的 mutation 的方法与 state 中 mutation 的方法重名

import { mapState } from "vuex"
export default {
    computed: {

        ...mapState(['setCart']),// 主模块中管理的状态 setCart
        ...mapState("cart", ["setCart"]) // cart 中管理的状态 setCart
    },
    methods: {
        ...mapMutations("cart", { setc: 'setCart', '...': '...' })
    }

模拟简易 vuex

组件中的使用:我们将只实现以下 4 个属性功能

<template>
  <div id="app">
    <h1>Vuex - Demo</h1>
    <h2>State</h2>  
    count:{{ $store.state.count }} <br>msg: {{ $store.state.msg }}
    <h2>Getter</h2> {{ $store.getters.reverseMsg }}
    <h2>Mutation</h2>// 修改状态调用$store.commit
    <button @click="$store.commit('increate', 2)">Mutation</button>  
    <h2>Action</h2>// 执行异步操作调用 $store.dispatch 分发action
    <button @click="$store.dispatch('increateAsync', 5)">Action</button> 
  </div>
</template>

根据配置我们知道 Vuex 返回一个对象:

  1. 使用 Vue.use() 调用 Vuex 的 install 方法,
  2. Vuex 中包含 Store 构造函数,接受 5 个属性为参数
import Vuex from 'vuex' // 导入 Vuex 对象
Vue.use(Vuex) // 调用 vuex 的 install 方法
export default new Vuex.Store({}) // store 是一个构造函数,接受五个属性为参数

vuex 基本结构:

// 在install 我们可以获取到 vue 的构造函数,后面我们还会在 store 使用到 vue 的构造函数,所以提前定义 _Vue
let _Vue = null
// 定义 Store 类
class Store {}
// 所有的插件都有一个 install 方法,vuex 的install 方法将 Store 挂载到 vue.$store 上
// install 需要接受两个参数 一个Vue 一个额外的选项,因为我们模拟的是简易的 Vuex 所以只接受 Vue 参数
function install (Vue) {
  _Vue = Vue
}
// 导出内容
export default {
  Store,
  install
}

install 实现:

function install (Vue) {
  _Vue = Vue
  _Vue.mixin({  // 将 Store 注入 vue 实例,使 Store 在所有组件中可以通过 $store 访问
    beforeCreate () {
      // this 是 vue 实例,如果是组件实例则没有 store 对象 
      if (this.$options.store) {
        _Vue.prototype.$store = this.$options.store
      }
    }
  })
}

Store 类实现:

class Store {
  // Store 接受 5 个属性参数
  constructor(options){
    const { 
      state = {}, // 设置默认值{},防止用户未传入该属性时报错
      getters = {},
      mutations = {},
      actions = {}
    } = options // 参数中的属性结构
    // state
    this.state = _Vue.observable(state) // 将state属性设置响应式数据
    // getters
    this.getters = Object.create(null)
    Object.keys(getters).forEach(key => {
      // 创建 getters 对象,遍历对象属性,利用 definedProperty 将 key 注册到 this.getters 对象中
      Object.defineProperty(this.getters, keys, {
        get: () => getters[key](state)
      })
    })
    // 将 mutations,actions 存储到对应的属性中,在 commit ,dispatch 中要获取
    this._mutations = mutations
    this._actions = actions
  }
  // commit 方法
  commit( type, payload){
    this._mutations[type](this.state, payload)
  }
  diapatch( type, payload){
    this._actions[type](this, payload)
  }
}

总结:

  • state:利用 vue 的 observable 将 state 中的状态设置为响应式数据
  • getters:利用 Object.defineProperty() 将 getters 中定义的方法挂载到 store 上,传入 state 并执行响应方法
  • mutations,action :则是通过 commit 与 dispatch 方法,进行调用内部方法

结语:以上内容全学习时手敲记录,无复制粘贴,全原创,希望可以给各位小伙伴带来收获,如有错误的地方或有疑问欢迎留言,感谢阅读!

祝各位前端程序猿前程似锦,一路向北!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值