前言
本文适合使用过 Vuex 的人阅读,来了解下怎么自己实现一个 Vuex。
基本骨架
这是本项目的src/store/index.js文件,看看一般 vuex 的使用
import Vue from 'vue' import Vuex from './myvuex' // 引入自己写的 vuex import * as getters from './getters' import * as actions from './actions' import state from './state' import mutations from './mutations' Vue.use(Vuex) // Vue.use(plugin)方法使用vuex插件 // vuex 导出一个类叫Store,并传入对象作为参数 export default new Vuex.Store({ state, mutations, actions, getters, })
Vue.use
的用法:
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
- 该方法需要在调用 new Vue() 之前被调用。
- 当 install 方法被同一个插件多次调用,插件将只会被安装一次。
即是我们需要在./myvuex.js
中导出 install
方法,同时导出一个类Store
,于是第一步可以写出代码:
let Vue = null class Store { constructor(options) {} } function install(_Vue) { Vue = _Vue // 上面Store类需要能获取到Vue } export default { Store, install, }
install 方法
当我们使用 vuex 的时候,每一个组件上面都有一个this.$store属性,里面包含了 state,mutations, actions, getters 等,所以我们也需要在每个组件上都挂载一个$store 属性,要让每一个组件都能获取到,这里我们使用Vue.mixin(mixin),用法介绍如下:
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的 Vue 实例。
function install(_Vue) { Vue = _Vue // install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue) // 实现每一个组件,都能通过this调用$store Vue.mixin({ beforeCreate() { // 通过this.$options可以获取new Vue({参数}) 传递的参数 if (this.$options && this.$options.store) { // 证明这个this是根实例,也就是new Vue产生的那个实例 this.$store = this.$options.store } else if (this.$parent && this.$parent.$store) { // 子组件获取父组件的$store属性 this.$store = this.$parent.$store } }, }) }
state
由于 Vuex 是基于 Vue 的响应式原理基础,所以我们要让数据改变可刷新视图,则需要创建一个 vue 实例
class Store { // options 即是 Vuex.Store({})传入的参数 constructor(options) { // vuex 的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图 let vm = new Vue({ data: { state: options.state, }, }) // state this.state = vm.state } }
commit
我们使用 vuex 改变数据时,是触发 commit 方法,即是这样使用的:
this.$store.commit('eventName', '参数' );
所以我们要实现一个commit方法,把 Store 构造函数传入的 mutations 做下处理
class Store { constructor(options) { // 实现 state ... // mutations this.mutations = {} // 存储传进来的mutations let mutations = options.mutations || {} // 循环取出事件名进行处理(mutations[事件名]: 执行方法) Object.keys(mutations).forEach(key => { this.mutations[key] = params => { mutations[key].call(this, this.state, params) // 修正this指向 } }) } commit = (key, params) => { // key为要触发的事件名 this.mutations[key](params) } }
dispatch
跟上面的 commit 流程同理
class Store { constructor(options = {}) { // ... // actions this.actions = {} let actions = options.actions || {} Object.keys(actions).forEach(key => { this.actions[key] = params => { actions[key].call(this, this, params) } }) } dispatch = (type, payload) => { this.actions[type](payload) } }
getters
getters 实际就是返回 state 的值,在使用的时候是放在 computed 属性,每一个 getter 都是函数形式;
getters 是需要双向绑定的。但不需要双向绑定所有的 getters,只需要绑定项目中事件使用的 getters。
这里使用Object.defineProperty()方法,它会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
class Store { constructor(options = {}) { // ... // getters this.getters = {} let getters = options.getters || {} Object.keys(getters).forEach(key => { Object.defineProperty(this.getters, key, { get: () => { return getters[key].call(this, this.state) }, }) }) } }
到此为止,已经可以使用我们自己写的 vuex 做一些基本操作了,但只能通过this.$store.xx的形式调用,故需要再实现方法。
map 辅助函数
先来说说 mapState
没有 map 辅助函数之前这样使用:
computed: { count () { return this.$store.state.count } }
当映射的计算属性的名称与 state 的子节点名称相同时,给 mapState 传一个字符串数组。
computed: { // 使用对象展开运算符将此对象混入到外部对象中 ...mapState(['count']) }
我们这里简单就只实现数组的情况
export const mapState = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.state[item] } }) return obj }
之后几个 map 辅助函数都是类似
- mapGetters
export const mapGetters = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.getters[item] } }) return obj }
- mapMutations
export const mapMutations = args => { let obj = {} args.forEach(item => { obj[item] = function(params) { return this.$store.commit(item, params) } }) return obj }
- mapActions
export const mapActions = args => { let obj = {} args.forEach(item => { obj[item] = function(payload) { return this.$store.dispatch(item, payload) } }) return obj }
完整代码
let Vue = null class Store { constructor(options) { // vuex 的核心就是借用了vue的实例,因为vue的实例数据变化,会刷新视图 let vm = new Vue({ data: { state: options.state, }, }) // state this.state = vm.state // mutations this.mutations = {} // 存储传进来的mutations let mutations = options.mutations || {} Object.keys(mutations).forEach(key => { this.mutations[key] = params => { mutations[key].call(this, this.state, params) } }) // actions this.actions = {} let actions = options.actions || {} Object.keys(actions).forEach(key => { this.actions[key] = params => { actions[key].call(this, this, params) } }) // getters this.getters = {} let getters = options.getters || {} Object.keys(getters).forEach(key => { Object.defineProperty(this.getters, key, { get: () => { return getters[key].call(this, this.state) }, }) }) } commit = (key, params) => { this.mutations[key](params) } dispatch = (type, payload) => { this.actions[type](payload) } } export const mapState = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.state[item] } }) return obj } export const mapGetters = args => { let obj = {} args.forEach(item => { obj[item] = function() { return this.$store.getters[item] } }) return obj } export const mapMutations = args => { let obj = {} args.forEach(item => { obj[item] = function(params) { return this.$store.commit(item, params) } }) return obj } export const mapActions = args => { let obj = {} args.forEach(item => { obj[item] = function(payload) { return this.$store.dispatch(item, payload) } }) return obj } function install(_Vue) { Vue = _Vue // install方法调用时,会将Vue作为参数传入(上面Store类需要用到Vue) // 实现每一个组件,都能通过this调用$store Vue.mixin({ beforeCreate() { // 通过this.$options可以获取new Vue({参数}) 传递的参数 if (this.$options && this.$options.store) { // 证明这个this是根实例,也就是new Vue产生的那个实例 this.$store = this.$options.store } else if (this.$parent && this.$parent.$store) { // 子组件获取父组件的$store属性 this.$store = this.$parent.$store } }, }) } export default { Store, install, }
以上就是如何手写一个简易的 Vuex的详细内容,更多关于手写 vuex的资料请关注其它相关文章!
手写,vuex,vuex
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]