Vuex 状态管理 及 模拟实现

273 阅读1分钟

Vuex 状态管理

1)状态管理

  • state,驱动应用的数据源
  • view,以声明的方式将 state 映射到视图
  • action,响应在 view 上的用户输入导致的状态变化

2)组件间通信方式回顾

  • 父组件给子组件传值
    • 子组件通过 props 接收数据
    • 父组件中给子组件通过相应属性传值
  • 子组件给父组件传值
  • 不相干组件之间传值
    • eventbus
    • ref
      • 在普通 HTML 标签上使用 ref, 获取到的是 DOM
      • 在组件标签上使用 ref, 获取到的是组件实例

3)Vuex

  • Store
  • State
  • Getter
  • Mutation
  • Action
  • Module
// App.vue
<template>
  <div id="app">
    <h1>Vuex - Demo</h1>
    <!-- count:{{ $store.state.count }} <br>
    msg: {{ $store.state.msg }} -->
    <!-- count:{{ count }} <br>
    msg: {{ msg }} -->

    count:{{ num }} <br>
    msg: {{ message }}

    <h2>Getter</h2>
    <!-- reverseMsg: {{ $store.getters.reverseMsg }} -->
    reverseMsg: {{ reverseMsg }}

    <h2>Mutation</h2>
    <!-- <button @click="$store.commit('increate', 2)">Mutation</button> -->
    <button @click="increate(3)">Mutation</button>

    <h2>Action</h2>
    <!-- <button @click="$store.dispatch('increateAsync', 5)">Action</button> -->
    <button @click="increateAsync(6)">Action</button>

    <h2>Module</h2>
    <!-- products: {{ $store.state.products.products }} <br>
    <button @click="$store.commit('setProducts', [])">Mutation</button> -->
    products: {{ products }} <br>
    <button @click="setProducts([])">Mutation</button>

    <h2>strict</h2>
    <button @click="$store.state.msg = 'Lagou'">strict</button>
  </div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
  computed: {
    // count: state => state.count
    // ...mapState(['count', 'msg'])
    ...mapState({ num: 'count', message: 'msg' }),
    ...mapGetters(['reverseMsg']),
    ...mapState('products', ['products'])
  },
  methods: {
    ...mapMutations(['increate']),
    ...mapActions(['increateAsync']),
    ...mapMutations('products', ['setProducts'])
  }
}
</script>
<style>

</style>

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state: {
    count: 0,
    msg: 'Hello Vuex'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  },
  modules: {
    products,
    cart
  }
})


// store/modules/carts.js
const state = {}
const getters = {}
const mutations = {}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}


// store/modules/products.js
const state = {
  products: [
    { id: 1, title: 'iPhone 11', price: 8000 },
    { id: 2, title: 'iPhone 12', price: 10000 }
  ]
}
const getters = {}
const mutations = {
  setProducts (state, payload) {
    state.products = payload
  }
}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

4)Vuex 插件

  • Vuex 的插件就是一个函数
  • 这个函数接收一个 store 的参数
// store/index.js
const myPlugin = store = > {
    // 当 store 初始化后调用
    store.subscribe((mutation, state) => {
        // 每次 mutation 之后调用
        // mutation 的格式为 {type, payload}
        if (mutation.type.startsWith('cart/')) {
      window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))
    }
    })
}

const store = new Vuex.Store({
    // ...
    plugins: [myplugin]
})

5)模拟 Vuex

// myvuex/index.js
let _Vue = null
class Store {
  constructor (options) {
    const {
      state = {},
      getters = {},
      mutations = {},
      actions = {}
    } = options
    this.state = _Vue.observable(state)
    this.getters = Object.create(null)
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](state)
      })
    })
    this._mutations = mutations
    this._actions = actions
  }

  commit (type, payload) {
    this._mutations[type](this.state, payload)
  }

  dispatch (type, payload) {
    this._actions[type](this, payload)
  }
}

function install (Vue) {
  _Vue = Vue
  _Vue.mixin({
    beforeCreate () {
      if (this.$options.store) {
        _Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
  Store,
  install
}