简易版Vuex源码实现

CodeCipherPro
• 阅读 1595

Vuex使用

1、注册Vuex插件

import Vuex from 'vuex'
Vue.use(Vuex)

2、创建Store实例

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state, payload) {
      state.count += payload
    } 
  },
  actions: {
    delayAdd(context, payload) {
      setTimeout(() => {
        context.commit("add", payload)
      }, 1000)
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  }
})

3、挂载到Vue根实例中

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

4、组件中使用

this.$store.state.count
this.$store.getters.doubleCount
this.$store.commit('add', 1)
this.$store.dispatch('')

总结,Vuex需要做以下这些事

  • 实现install方法
  • 在Vue根实例化时,给Vue根实例挂载store对象
  • store.state和getter都是数据响应式,并且不能直接修改的state和getter的值,只能通过commit和dispatch
  • 实现commit,同步修改state的值
  • 实现dispatch,支持异步修改state的值

源码实现

let Vue
class Store {
  constructor(options) {
    this._vm = null
    this._mutations = options.mutations
    this._actions = options.actions
    this._wrappedGetters = options.getters
    this.getters = {}
    const storeComputed = {}
    const self_store = this
    Object.keys(this._wrappedGetters).forEach((key) => {
      // 遍历取出getters的每个方法
      const gettersFn = self_store._wrappedGetters[key]
      // 转换为computed, 即返回一个无参数的高阶函数
      storeComputed[key] = () => {
        return gettersFn(self_store.state)
      }
      // 定义getter为 只读属性,并且可枚举
      Object.defineProperty(self_store.getters, key, {
        get: () => self_store._vm[key],
        enumerable: true
      })
    })
    // new一个新的Vue实例_vm,并在_vm做数据响应式处理
    this._vm = new Vue({
      data: {
        $$state: options.state // 加$$表示不代理到Vue实例属性上
        // Vue初始化在对数据做响应式处理时,还会将数据代理到实例
      },
      computed: {
        ...storeComputed
      }
    })
    console.log(this._vm)
  }
  get state () {
    return this._vm._data.$$state
  }
  set state(v) {
    throw new Error(`use store.replaceState() to explicit replace store state.`)
  }
  // 这里需要使用箭头函数 防止this改变
  // 或者使用this.commit = this.commit.bind(this) 绑定this
  commit = (type, payload) => {
    // 根据type 从 mutations里面找对应的方法
    const fn = this._mutations[type]
    if (!fn) return
    fn.call(this, this.state, payload)
  }
  dispatch  = (type, payload) => {
    // 根据type 从 actions里面找对应的方法
    const fn = this._actions[type]
    if (!fn) return
    fn.call(this, {commit: this.commit, state: this.state}, payload)
  }
}
function install(vue) {
  /* 
     1、判断是否安装过插件
     2、保存vue构造函数
     3、通过混入的方式,在根实例创建时挂载store实例
    */
    //  1、判断是否安装过插件
    if (Store.installed) {
      return
    }
    Store.installed = true
    //2、保存vue构造函数
    Vue = vue
    //通过混入的方式,在根实例创建时挂载store实例
    Vue.mixin({
      beforeCreate() {
        if (this.$options.store) {
          Vue.prototype.$store = this.$options.store
        }
      },
    })
}
export default { Store, install }

源码实现思路

  • 执行install方法时,通过Vue.mixin混入的方式,在Vue实例化时,在beforeCreate生命周期里,给Vue原型挂载store对象Vue.prototype.$store = store
  • Store构造函数里保存传入的配置选项,便于获取state、mutations、actions、getters等
  • store.state实现:通过new一个新的Vue实例_vm, 把state存于_vm.data,state就变为响应式数据。重写state的set和get,防止直接修改state的值,只能通过commit和dispatch修改state的值,get则从_vm.data获取
  • commit方法实现:commit需要绑定this为store实例,防止dispatch调用是this指向改变。通过传入的参数type,从mutations里取出函数执行,并传入state参数
  • dispatch方法实现:与commit方法类似,通过传入的参数type,从actions里取出函数执行,并将commit方法和state存于context作为参数传入
  • getters实现:遍历getters的属性,都转换为_vm的computed里面,即返回无参的高阶函数,并使用Object.defineProperty为每个key重新定义get方法,无需定义set,防止直接修改getter的值
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Chase620 Chase620
4年前
vue: 解决vuex页面刷新数据丢失问题
一、问题描述1、一般在登录成功的时候需要把用户信息,菜单信息放置vuex中,作为全局的共享数据。但是在页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。2、我在一个组件(例如登录组件页面)中登录了后,其它页面要怎
Dax Dax
4年前
veux的使用
1)说说什么是vuex(下定义)2)vuex解决了哪些问题,为什么要用(必要性)3)怎么使用vuex(使用方法)4)描述vuex原理,提升答案深度(深层原理升华答案)vuex的流程图分析:我们可以看到图中虚线框包裹起来的部分就是vuex的三个组成部分(Action,Mutations,state),我们来简单的捋一下整个流程:首先vue的组件在响应用户行为交
Souleigh ✨ Souleigh ✨
5年前
Vuex 4 正式发布:打包现在与 Vue 3 一致
Vuex是一个专门为Vue.js应用程序开发的状态管理模式。Vuex采用集中式存储管理应用的所有组件组件的状态。换句话说,Vuex就是帮开发者存储多个组件共享的数据,方便开发者对其读取的更改的工具。Vuex4正式版本现已发布。Vuex4的改进重点是兼容性。Vuex4支持Vue3,并提供了与Vuex3完全相同的
九路 九路
5年前
通过一个简单实例了解vuex
简单说明什么是vuex,vuex怎么使用,什么场景下适合使用vuex,vuex文档(https://links.jianshu.com/go?tohttps%3A%2F%2Fvuex.vuejs.org%2Fzh%2F)中都有介绍。看完文档之后,都知道vuex的核心有State、Getter、Mutation、Action、Modul
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这