Быстрый старт Vuex для рукописного ввода Easy Vuex

внешний интерфейс Vuex
Быстрый старт Vuex для рукописного ввода Easy Vuex

Быстрый старт Vuex -> Простой рукописный ввод Vuex

предисловие

Прежде всего, спасибо за вашу поддержку Xiaolang в течение этого времени, Xiaolang будет продолжать усердно работать.

Сегодняшняя статья оVuex, все пользуютсяVueне незнакомый

Сегодня мы впервыеVuexУзнайте об этом, затем расскажите об основном использовании, а затем мы реализуем простойVuex

Наконец, я надеюсь, что каждый сможет дать Сяолануотличный

img

Прошлые основные моменты:

От понимания к глубокому виртуальному DOM и реализации алгоритма сравнения

Написание простого ответа vue поможет вам понять принцип ответа.

От использования до самостоятельной реализации простого Vue Router — просто взгляните на это

Необходимые базовые знания о фронтенд-интервью, хотя и немногочисленные, но вы не можете не знать

1. Введение

VuexПлагин управления состоянием

существуетVueСамое главное этоуправляемый даннымиисоставной, каждый компонент имеет свой собственныйdata ,templateиmethods, dataэто данные, которые мы также называем состоянием, посредствомmethodsОчень удобно изменять состояние для обновления представления в одном компоненте, но в реальной разработке, когда несколько компонентов (и многоуровневая вложенность компонентов) имеют одно и то же состояние, параметры будут передаваться в это время.Очень сложно, мы представим его здесьVuexОсуществлять управление состоянием, отвечающее за коммуникацию в компоненте, чтобы облегчить обслуживание кода.

Основная проблема, которую решает Vuex

  • Несколько представлений зависят от одного и того же состояния
  • Действия из разных представлений должны изменять одно и то же состояние

Преимущества использования Vuex

  • способенvuexЦентрализованное управление общими данными, простота разработки и последующего обслуживания
  • Он может эффективно реализовать обмен данными между компонентами и повысить эффективность разработки.
  • существуетvuexДанные в отзывчивы

2. Базовое использование Vuex

Сначала добавьте плагин Vuex в Vue.

пройти черезvue-cliДобавленVuexПосле этого в проектеsrcВ каталоге будет еще одинstoreкаталог, будетindex.js

конечно тоже черезnpmустановитьVuex

npm install vuex --save

Включите строгий режим в среде разработки, чтобы изменение данных обрабатывалось путем мутации.

Среду можно задать в скриптах файла package.json, а когда мы находимся в среде разработки, можно включить строгий режим

Способ включить строгий режим также очень прост с помощью одной строки кода.

strict:products.env.NODE_ENV !== 'production'

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  // 在开发环境开启严格模式 这样修改数据 就必须通过 mutation 来处理
  strict:products.env.NODE_ENV !== 'production',
  // 状态
  state: {
  },
  // 用来处理状态
  mutations: {
  },
  // 用于异步处理
  actions: {
  },
  // 用来挂载模块
  modules: {
  }
})

Чтобы использовать магазин, просто смонтируйте магазин в Vue.

После подключения хранилища к Vue все компоненты могут напрямую получать глобальные данные из хранилища.

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  // 挂载到vue 中
  store,
  render: (h) => h(App),
}).$mount('#app')

1.state

добавить данные в состояние

Состояние, которым мы должны поделиться, записано в объекте состояния.

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: '张三',
    age: 21,
  },
  mutations: {},
  actions: {},
  modules: {},
})

Получить данные в состоянии в компоненте

получитьstateЕсть два способа

1. Используйте напрямуюthis.$store.state[属性], (thisможно опустить)

<template>
  <div id="app">
    {{ this.$store.state.name }}
    {{ this.$store.state.age }}
  </div>
</template>

2. ИспользуйтеmapState

пройти черезmapStateПучокstoreСопоставленный с вычисляемым свойством компонента, он эквивалентен его наличию внутри компонента.stateсвойства в

знаю, почему...Расширьте его, когда это будет реализовано?mapStateзнать, когда

<template>
  <div id="app">
    {{ name }}
    {{ age }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState
import { mapState } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 store 映射到当前组件的计算属性
    ...mapState(['name', 'age'])
  }
}
</script>

<style  scoped>
</style>

Уведомление

Когда значение в хранилище имеет то же состояние, что и текущий компонент, мы можем передать объект вместо массива в методе mapState и псевдоним состояния в объекте

computed: {
    // name2 和 age2 都是别名
    ...mapState({ name2: 'name', age2: 'age'}])
}

2.Mutation

StoreСостояние in не может работать с ним напрямую, мы должны использоватьMutationиди прямоStoreХоть это и выглядит немного громоздко, централизованно отслеживать изменения данных удобно.

stateОбновление должно бытьMutationиметь дело с

Теперь мы определяем метод в мутациях

Если метод, который вы хотите определить, может быть измененStoreВ состоянии обязательным параметром являетсяstate

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: '张三',
    age: 21,
  },
  mutations: {
    // 在这里定义 方法
    /**
     *
     * @param {*} state 第一个参数是 Store 中的状态(必须传递)
     * @param {*} newName 传入的参数 后面是多个
     */
    changeName(state, newName) {
      // 这里简单举个例子 修改个名字
      state.name = newName
    },
  },
  actions: {},
  modules: {},
})

Использование методов в мутациях в компонентах

Также есть два способа запуска в компонентеmutationsметод в

1.this.$store.commit() запускает

существуетmethodsОпределите метод в методе и активируйте его в этом методеmutationsметод в

<template>
  <div id="app">
    <button @click="handleClick">方式1 按钮使用 mutation 中方法</button>
    {{ name }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState
import { mapState } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 store 映射到当前组件的计算属性
    ...mapState(['name', 'age'])
  },
  methods: {
    handleClick() {
      // 触发 mutations 中的 changeName
      this.$store.commit('changeName', '小浪')
    }
  },
}
</script>

<style  scoped>
</style>

2. ИспользуйтеmapMutations

<template>
  <div id="app">
    <button @click="changeName('小浪')">方式2 按钮使用 mutation 中方法</button>
    {{ name }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 store 映射到当前组件的计算属性
    ...mapState(['name', 'age'])
  },
  methods: {
	// 将 mutations 中的 changeName 方法映射到 methods 中,就能直接使用了 changeName 了
    ...mapMutations(['changeName'])
  },
}
</script>

<style  scoped>
</style>

3.Action

ActionиMutationразница

ActionОн также используется для обработки задач, но обрабатывает асинхронные задачи, а асинхронные задачи должны использоватьAction,пройти черезActionкурокMutationКосвенно изменяет состояние и не может использоваться напрямуюMutationИзмените асинхронную задачу напрямую

доActionопределить асинхронный метод для вызоваMutationметод в

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: '张三',
    age: 21,
  },
  mutations: {
    // 在这里定义 方法
    /**
     *
     * @param {*} state 第一个参数是 Store 中的状态(必须传递)
     * @param {*} newName 传入的参数 后面是多个
     */
    changeName(state, newName) {
      // 这里简单举个例子 修改个名字
      state.name = newName
    },
  },
  actions: {
    /**
     *
     * @param {*} context 上下文默认传递的参数
     * @param {*} newName 自己传递的参数
     */
    // 定义一个异步的方法 context是 store
    changeNameAsync(context, newName) {
      // 这里用 setTimeout 模拟异步
      setTimeout(() => {
        // 在这里调用 mutations 中的处理方法
        context.commit('changeName', newName)
      }, 2000)
    },
  },
  modules: {},
})

В компоненте есть асинхронный метод в Action и есть два пути

1.this.$store.dispatch()

<template>
  <div id="app">
    <button @click="changeName2('小浪')">方式1 按钮使用 action 中方法</button>
    {{ name }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState mapMutations
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 store 映射到当前组件的计算属性
    ...mapState(['name', 'age'])
  },
  methods: {
    changeName2(newName) {
      // 使用 dispatch 来调用 actions 中的方法
      this.$store.dispatch('changeNameAsync', newName)
    }
  },
}
</script>

<style  scoped>
</style>

2. ИспользуйтеmapActions

<template>
  <div id="app">
    <button @click="changeNameAsync('小浪')">
      方式2 按钮使用 action 中方法
    </button>
    {{ name }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState mapMutations mapActions
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 store 映射到当前组件的计算属性
    ...mapState(['name', 'age'])
  },
  methods: {
    // 映射 actions 中的指定方法 到 methods中,就可以在该组件直接使用
    ...mapActions(['changeNameAsync'])
  },
}
</script>

<style  scoped>
</style>

4.Getter

Введение

GetterАналогично вычисляемым свойствам, но наш источник данныхVuexсерединаstate, поэтому используйтеVuexсерединаGetterЧто нужно сделать

Сценарии применения

нужноstateСделайте некоторую простоту упаковки и отобразите ее в представлении

Давайте сначала напишем геттер

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: '张三',
    age: 21,
  },
  getters: {
    // 在这里对 状态 进行包装
    /**
     *
     * @param {*} state 状态 如果要使用 state 里面的数据,第一个参数默认就是 state ,名字随便取
     * @returns
     */
    decorationName(state) {
      return `大家好我的名字叫${state.name}今年${state.age}岁`
    },
  },
})

КонечноGetterЕсть также два способа импорта

1.this.$store.getters[名称]

<template>
  <div id="app">
    {{ this.$store.getters.decorationName }}
  </div>
</template>

2. ИспользуйтеmapGetters

<template>
  <div id="app">
    {{ decorationName }}
  </div>
</template>

<script>

// 从 Vuex 中导入 mapGetters
import { mapGetters } from 'vuex'
export default {
  name: 'App',
  computed: {
    // 将 getter 映射到当前组件的计算属性
    ...mapGetters(['decorationName'])
  },
}
</script>

5.Module

Чтобы избежать в сложном проектеstateДанные в становятся раздутыми,VuexразрешитьStoreРазделены на разные модули, каждый модуль имеет свой собственныйstate,getter,action,mutation

Мы создаем новый здесьanimal.jsдокумент

/* animal.js */

const state = {
  animalName: '狮子',
}
const mutations = {
  setName(state, newName) {
    state.animalName = newName
  },
}

//导出
export default {
  state,
  mutations,
}

существуетstore/index.jsсерединаmodulesустановить этот модуль

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 Vuex 插件
import Vuex from 'vuex'
// 引入模块
import animal from './animal'

// 把 Vuex 注册到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    animal,
  },
})

Затем мы можем использовать его в компоненте

<template>
  <div id="app">
    {{ this.$store.state.animal.animalName }}
    <button @click="$store.commit('setName', '老虎')">改名</button>
  </div>
</template>

$store.state[在module中挂载的模块名][挂载的模块里的属性]

Считаете ли вы эту модель сложной?

img

добавить пространство имен

На самом деле, вы также можете использоватьmapXXXМетод сопоставлен, но способ записи немного отличается.Сначала добавьте один в экспорт.Пространства имен namespaced: true

/* animal.js */

const state = {
  animalName: '狮子',
}
const mutations = {
  setName(state, newName) {
    state.animalName = newName
  },
}

export default {
  // 开启命名空间 方便之后使用 mapXXX
  namespaced: true,
  state,
  mutations,
}

способ 2

<template>
  <div id="app">
    {{ animalName }}
    <button @click="setName('老鹰')">改名</button>
  </div>
</template>

<script>

// 从 Vuex 中导入 mapState mapMutations
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'App',
  computed: {
    // mapState 使用方式和之前有些许不同,第一个是module挂载的模块名
    // 第二个参数是 animal 模块中的 state 属性
    ...mapState('animal', ['animalName'])
  },
  methods: {
    // mapMutations 使用方式也和之前有些许不同,第一个是module挂载的模块名
    // 第二个参数是 animal 模块中的 mutation 方法
    ...mapMutations('animal', ['setName'])
  },
}
</script>


3. Смоделируйте простой Vuex

Мы представили вышеVuexОсновное использование , теперь давайте напишем простое самостоятельноVuex

Я наполню код комментариями, чтобы все могли посмотреть. Кода очень мало. Если вам интересно, терпеливо смотрите ヽ( ̄▽ ̄)ノ

1.index.js

Сделать базовую полку

мы вsrcСоздадим собственный каталогVuexпапку и добавьтеindex.jsфайл, мы хотим имитировать этоVuexвыложу сюда

/* my-vuex/index.js */

// 保存一个全局的 Vue 之后会用到
let _Vue = null

// Store 类
class Store {
  // 先完成构造方法,构造方法接收一个对象
  constructor(options) {
  	//...待实现
  }
}

// 因为Vuex 需要 Vue.use() 安装,所以我们必须要有个 install 方法 传入 Vue
// 第二个参数是一个可选对象
function install(Vue) {
	//... 待实现
}

// 导出 install 和 Store
export default {
  install,
  Store,
}

2. метод установки

Поскольку плагин Vuex требует установки Vue.use(), у нас должен быть метод установки, первый параметр передается Vue.

// 第二个参数是一个可选对象
function install(Vue) {
  // 保存到全局 _Vue
  _Vue = Vue
  // 全局注册混入 这样在所有的组件都能使用 $store
  _Vue.mixin({
    // beforeCreate vue初始化阶段
    // 在 beforeCreate 这个时候把 $store 挂载到 Vue 上
    beforeCreate() {
      // 判断 Vue 传递的对象是否有 store 需要挂载
      // this.$options  是new Vue() 传递的对象
      if (this.$options.store) {
        // 把 store 挂载到 Vue 原型上
        _Vue.prototype.$store = this.$options.store
      }
    },
  })
}

3. Продолжаем добиватьсяStoreсвоего рода

Сначала выполните базовый метод строительства

/* my-vuex/index.js */

// 保存一个全局的 Vue 之后会用到
let _Vue = null

// Store 类
class Store {
  // 先完成构造方法,构造方法接收一个对象
  constructor(options) {
    // 赋初值
    const state = options.state || {}
    const mutations = options.mutations || {}
    const actions = options.actions || {}
    const getters = options.getters || {}
}

//...install

Далее реализуемstate,getters,mutations,actions,commit,dispatchБар

(゚▽゚)/

3.state

Это очень просто, просто вызовите его напрямуюVueизobservableПучокstateстать отзывчивым

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
    // 1.实现state 把 state 中的数据转为 响应式,直接用 Vue 中的 observable
    this.state = _Vue.observable(state)
  }
}

4.getters

для каждогоgettersМетод внутри добавляетget

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
    // 2.实现 getters 这里为什么不知直接 把this.getters 赋值 {} 而是 Object.create(null)
    // 好处是不用考虑会和原型链上的属性重名问题
    this.getters = Object.create(null)
    // 我们要为 getters 添加一个 get 方法,这里就要使用 数据劫持
    // 先拿到 getters 中每一个 方法
    Object.keys(getters).forEach((key) => {
      // 第一个参数是给谁添加 ,第二个是添加的属性名,第三个对象里面可以设置很多参数
      // 比如 可枚举,可配置,get,set
      Object.defineProperty(this.getters, key, {
        // 为 this.getters 每一项都添加 一个 get 方法
        get: () => {
          // 还记得吗,getters 中的方法 默认把 state传入进去,改变this指向
          return getters[key].call(this, this.state)
        },
      })
    })
  }
}

5.mutations

изменить здесьthisнаправление

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
    // 3.实现 mutations
    // 先遍历 mutaions 中的对象进行改变 this指向
    this.mutations = {}
    Object.keys(mutations).forEach((key) => {
      this.mutations[key] = (params) => {
        // 改变this指向 ,默认是要传入 state
        mutations[key].call(this, this.state, params)
      }
    })
  }
}

6.actions

На самом деле, с вышеизложеннымmutationsМетод обработки аналогичен, но параметры передаются разные, и нужно передавать контекстcontextэтоStoreПример , вотthis

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
    // 4.实现 actions
    // 和 mutations 一样我们需要重新改变 this 指向
    this.actions = {}
    Object.keys(actions).forEach((key) => {
      this.actions[key] = (params) => {
        // 改变this指向 ,默认是要传入 store也就是 this
        actions[key].call(this, this, params)
      }
    })
  }
}

7.commit

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
  }
  // 5.实现commit 方法
  // 用于 触发mutations中的方法
  // 第一个参数是事件名 ,第二个是参数
  commit = (eventName, params) => {
    this.mutations[eventName](params)
  }
}

8.dispatch

dispatchиcommitдостичь почти

/* my-vuex/index.js */

// Store 类
class Store {
  constructor(options) {
    //...其他细节
  }
  // 6.实现 dispatch 方法
  // 用于 触发actions中的异步方法
  // 第一个参数是事件名 ,第二个是参数
  dispatch = (eventName, params) => {
    this.actions[eventName](params)
  }
}

Ну, это почти здесь, версия для нищихVuexТак он родился, давайте напишем пример, чтобы протестировать его.

9. Тестовый пример

Импортируйте то, что мы написали первымVuex

/* src/store/index.js */

// 导入 Vue
import Vue from 'vue'
// 导入 我们自己写的 Vuex 插件
import Vuex from '../my-vuex/index'
// 把 Vuex 安装到到Vue 上
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: '张三',
    age: 21,
  },
  mutations: {
    changeName(state, newName) {
      // 这里简单举个例子 修改个名字
      state.name = newName
    },
  },
  actions: {
    changeNameAsync(context, newName) {
      // 这里用 setTimeout 模拟异步
      setTimeout(() => {
        // 在这里调用 mutations 中的处理方法
        context.commit('changeName', newName)
      }, 2000)
    },
  },
  getters: {
    decorationName(state) {
      return `大家好我的名字叫${state.name}今年${state.age}岁`
    },
  },
})

Простоvueкомпоненты

<template>
  <div id="app">
    <h1>我是 state 测试:{{ this.$store.state.name }}</h1>
    <h1>我是 getters 测试:{{ this.$store.getters.decorationName }}</h1>
    <button @click="$store.commit('changeName', 'mutations 按钮')">
      mutations 按钮
    </button>
    <button @click="$store.dispatch('changeNameAsync', 'actions 按钮')">
      actions 按钮
    </button>
  </div>
</template>

<script>
          
export default {
  name: 'App',
}
</script>

<style  scoped>

</style>

В mian.js монтирование осталось прежним.

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  // 挂载到vue 中
  store,
  render: (h) => h(App),
}).$mount('#app')

Предварительный просмотр примера

27

Это еще не конец, лучше просто реализовать несколькоmapXXX, почти то же самое

img

10.mapSate

...mapSate(['age',['name']]),Наконецcomputedчто вы получаетеage: 21, name : '张三'Таким образом, его можно использовать непосредственно в компоненте

const mapState = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function() {
      return this.$store.state[item]
    }
  })
  return obj
}

11.mapMutations

const mapMutations = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function(params) {
      return this.$store.commit(item, params)
    }
  })
  return obj
}

12.mapActions

const mapActions = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function(params) {
      return this.$store.dispatch(item, params)
    }
  })
  return obj
}

13.mapGetters

const mapGetters = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function() {
      return this.$store.getters[item]
    }
  })
  return obj
}

14. Экспорт и использование

Наконец, экспорт

// 导出
export { mapState, mapMutations, mapActions, mapGetters }

Используйте тот же метод, что и раньше

<template>
  <div id="app">
    <button @click="changeName('狗子')">mapMutations</button>
    <button @click="changeNameAsync('狗2子')">mapMutations</button>
    {{ decorationName }}
    {{ age }}
  </div>
</template>

<script>
// 导入
import { mapState, mapMutations, mapActions, mapGetters } from './my-vuex/index'

export default {
  name: 'App',
  computed: {
    ...mapState(['age']),
    ...mapGetters(['decorationName'])
  },
  methods: {
    ...mapMutations(['changeName']),
    ...mapActions(['changeNameAsync'])
  },
}
</script>
...
</style>

3. Заключение

Хорошо, мы здесь, оVuexНа этом статья окончена, начинаем сVuexЧто это такое, как его использовать и простая практическая реализацияVuexМы все закончили, надеюсь, вы все получите что-то

Далее моделируем завершенныйVuexКод выложен, приветствую всех, общайтесь больше, если что не так, укажите пожалуйста

img

моделированиеVuexполный код

/* my-vuex/index.js */

// 保存一个全局的 Vue 之后会用到
let _Vue = null

// Store 类
class Store {
  // 先完成构造方法,构造方法接收一个对象
  constructor(options) {
    // 赋初值
    const state = options.state || {}
    const mutations = options.mutations || {}
    const actions = options.actions || {}
    const getters = options.getters || {}
    // 1.实现state 把 state 中的数据转为 响应式,直接用 Vue 中的 observable
    this.state = _Vue.observable(state)

    // 2.实现 getters 这里为什么不知直接 把this.getters 赋值 {} 而是 Object.create(null)
    // 好处是不用考虑会和原型链上的属性重名问题
    this.getters = Object.create(null)
    // 我们要为 getters 添加一个 get 方法,这里就要使用 数据劫持
    // 先拿到 getters 中每一个 方法
    Object.keys(getters).forEach((key) => {
      // 第一个参数是给谁添加 ,第二个是添加的属性名,第三个对象里面可以设置很多参数
      // 比如 可枚举,可配置,get,set
      Object.defineProperty(this.getters, key, {
        // 为 this.getters 每一项都添加 一个 get 方法
        get: () => {
          // 还记得吧,getters 中的方法 默认把 state传入进去,改变this指向
          return getters[key].call(this, this.state)
        },
      })
    })

    // 3.实现 mutations
    // 先遍历 mutaions 中的对象进行改变 this指向
    this.mutations = {}
    Object.keys(mutations).forEach((key) => {
      this.mutations[key] = (params) => {
        // 改变this指向 ,默认是要传入 state
        mutations[key].call(this, this.state, params)
      }
    })

    // 4.实现 actions
    // 和 mutations 一样我们需要重新改变 this 指向
    this.actions = {}
    Object.keys(actions).forEach((key) => {
      this.actions[key] = (params) => {
        // 改变this指向 ,默认是要传入 store也就是 this
        actions[key].call(this, this, params)
      }
    })
  }

  // 5.实现commit 方法
  // 用于 触发mutations中的方法
  // 第一个参数是事件名 ,第二个是参数
  commit = (eventName, params) => {
    this.mutations[eventName](params)
  }

  // 6.实现 dispatch 方法
  // 用于 触发actions中的异步方法
  // 第一个参数是事件名 ,第二个是参数
  dispatch = (eventName, params) => {
    this.actions[eventName](params)
  }
}

// 因为Vuex 需要 Vue.use() 安装,所以我们必须要有个 install 方法 传入 Vue
// 第二个参数是一个可选对象
function install(Vue) {
  // 保存到全局 _Vue
  _Vue = Vue
  // 全局注册混入 这样在所有的组件都能使用 $store
  _Vue.mixin({
    // beforeCreate vue初始化阶段
    // 在 beforeCreate 这个时候把 $store 挂载到 Vue 上
    beforeCreate() {
      // 判断 Vue 传递的对象是否有 store 需要挂载
      // this.$options  是new Vue() 传递的对象
      if (this.$options.store) {
        // 把 store 挂载到 Vue 原型上
        _Vue.prototype.$store = this.$options.store
      }
    },
  })
}

// mapState
const mapState = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function() {
      return this.$store.state[item]
    }
  })
  return obj
}

// mapMutations
const mapMutations = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function(params) {
      return this.$store.commit(item, params)
    }
  })
  return obj
}

// mapActions
const mapActions = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function(params) {
      return this.$store.dispatch(item, params)
    }
  })
  return obj
}

// mapGetters
const mapGetters = (params) => {
  // 这里我只写个数组的 起别名的就没弄哈
  if (!Array.isArray(params))
    throw new Error('抱歉,当前是丐版的Vuex,只支持数组参数')
  // 第一步就是要初始 obj ,不然[item] 会报错
  let obj = {}
  // 实现逻辑很简单,就是接收传递的的参数
  // 去this.$store寻找
  params.forEach((item) => {
    obj[item] = function() {
      return this.$store.getters[item]
    }
  })
  return obj
}
// 导出
export { mapState, mapMutations, mapActions, mapGetters }

// 导出 install 和 store
export default {
  install,
  Store,
}