简单实现Vuex

githubblog

Vuex

Vuex集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以可预测的方式发生变化。

vuex

安装Vuex

vue add vuex

核心概念

  • state:状态、数据
  • mutations:更改状态的函数
  • action:异步操作
  • store:包含以上概念的容器

状态 - state

state保存应用状态

export default new Vuex.Store({
  state: {
    counter: 0
  }
})
<h1>
  {{$store.state.counter}}
</h1>

状态变更 - mutations

mutations用于修改状态

export default new Vuex.Store({
  mutations:{
    add(state){
      state.counter++
    }
  }
})
<h1 @click="$store.commit('add')">
  {{$store.state.counter}}
</h1>

派生状态 - getters

state派生出来新状态,类似计算属性

export default new Vuex.Store({
  getters:{
    doubleCounter(state){
      return state.counter * 2;
    }
  }
})
<h1>
  {{$store.getters.doubleCounter}}
</h1>

动作 - actions

添加业务逻辑,类似于controller

export default new Vuex.Store({
  actions:{
    add({commit}){
      setTimeout(() => commit('add'), 1000);
    }
  }
})
<h1 @tap="$store.dispatch('add')">
  {{$store.state.counter}}
</h1>

Vuex原理解析

任务分析

  • 实现插件
    • 实现Store类
      • 维持一个响应式状态state
      • 实现commit()
      • 实现dispatch()
      • 实现getters
    • 挂载$store

创建新的插件

Vue2.x项目中的src路径下,复制一份store文件,重命名为ou-store

然后在ou-store路径下新建一个ou-vuex.js文件,并将index.js文件中的Vuex引入改为ou-vuex.js

import Vuex from './ou-vuex'

同时将main.js中的router引入也修改一下。

import router from './ou-vuex'

创建vue的插件

回头看一下store/index.js,首先是使用Vue.use()注册了Vuex,然后再实例化了Vuex.Store这个类,因此Vuex这个对象里含有一个install方法以及一个Store的类。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    ...
})

因此我们来创建一个新的Vuex插件。

let Vue;    // 保存Vue的构造函数,插件中需要用到

class Store {}

function install(_Vue) {
    Vue = _Vue;
}

export default {Store, install};

挂载$store

let Vue;    // 保存Vue的构造函数,插件中需要用到

class Store {}

function install(_Vue) {
    Vue = _Vue;

    Vue.mixin({
        beforeCreate() {
            // 挂载$store
            if(this.$options.store){
                Vue.prototype.$store = this.$options.store;     // vm.$store
            }
        }
    })
}

export default {Store, install};

实现响应式保存state数据

因为state是一个对象,我们可以使用new Vue()state转换为一个响应式数据进行保存起来。

其次,我们不能显式去保存这个state,暴露给外面,因此我们可以使用getset去保存。

class Store {
    /*
    * options:
    *   state
    *   mutations
    *   actions
    *   modules
    *   getters
    * */
    constructor(options = {}) {
        // data响应式处理
        this._vm = new Vue({   
            data: {
                $$state: options.state    // 通过this._vm._data.$$state 或 this._vm.$data.$$state 获取
            }
        });
    }

    // 获取state
    get state() {
        return this._vm._data.$$state;
    }

    // 不可设置state
    set state(v) {
        console.error('please use replaceState to reset state');
    }
}

实现commit方法

当我们使用commit方法时,都是$store.commit(type,payload),第一个参数即mutationstype值,第二个是payload负载,而对应mutation方法的参数为statepayload,因此我们来实现:

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state
            }
        });

        // 保存用户配置的mutations选项
        this._mutations = options.mutations;
    }

    get state() {
        return this._vm._data.$$state;
    }

    set state(v) {
        console.error('please use replaceState to reset state');
    }


    commit(type, payload) {
        // 获取type对应的mutation
        const entry = this._mutations[type]
        if(!entry) {
            console.error(`unknown mutation type : ${type}`);
            return ;
        }

        // 传递state和payload给mutation
        entry(this.state, payload)
    }
}

实现dispatch方法

dispatch方法跟commit方法大同小异,不同之处在于dispatch调用的是action异步函数,而action的参数为contextpayloadpayload我们可以通过dispatch的参数获取到,而context执行上下文其实就是实例中的this

action是用来处理异步函数的,因此我们需要对dispatch方法进行this绑定;同时,action方法中有可能会调用到commit方法,因此我们也需要对commit方法进行this绑定。

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state 
            }
        });

        // 保存用户配置的mutations选项和actions选项
        this._mutations = options.mutations;
        this._actions = options.actions;

        // 将commit和dispatch绑定this,
        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
    }

    get state() {
        return this._vm._data.$$state;
    }

    set state(v) {
        console.error('please use replaceState to reset state');
    }


    commit(type, payload) {
        const entry = this._mutations[type]
        if(!entry) {
            console.error(`unknown mutation type : ${type}`);
            return ;
        }

        entry(this.state, payload)
    }

    dispatch(type, payload) {
        // 获取用户编写的type对应的action
        const entry = this._actions[type];
        if(!entry) {
            console.error(`unknown action type : ${type}`)
        }
        // 异步结果处理常常需要返回Promise
        return entry(this, payload)
    }
}

实现getters派生状态

当我们定义getters状态时,实际上是定义了一个function

getters: {
   doubleCounter(state) {
     return state.counter * 2;
   }
},

而使用getters中某一个派生状态时,实际上是得到一个值,也就是这个function的返回值。

<h4>double count: {{$store.getters.doubleCounter}}</h4>

这其实就有点像对象中的get属性,因此我们可以使用Object.defineProperty()来实现getters

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state   
            }
        });

        this._mutations = options.mutations;
        this._actions = options.actions;

        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);

        // 初始化getters,默认为一个空对象
        this.getters = {};

        // 遍历options.getters
        for (const key in options.getters) {
            const self = this;
            Object.defineProperty(
                this.getters,
                key,   // key名
                {
                    get() {
                      	// 调用对应的函数,第一个参数为state,将结果返回
                        return options.getters[key](self._vm._data.$$state)   
                    }
                }
            )
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
好的,下面是一个简单的使用Vuex实现TodoList的示例代码: 首先,需要安装Vuex: ``` npm install vuex --save ``` 然后,创建一个store.js文件,用于定义Vuex的状态、mutations、actions等: ```javascript import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '学习Vue', completed: false }, { id: 2, text: '学习Vuex', completed: false }, { id: 3, text: '学习Vue Router', completed: false } ] }, mutations: { addTodo (state, payload) { state.todos.push({ id: Date.now(), text: payload.text, completed: false }); }, toggleTodo (state, payload) { const todo = state.todos.find(todo => todo.id === payload.id); todo.completed = !todo.completed; }, deleteTodo (state, payload) { state.todos = state.todos.filter(todo => todo.id !== payload.id); } }, actions: { addTodo ({ commit }, payload) { commit('addTodo', payload); }, toggleTodo ({ commit }, payload) { commit('toggleTodo', payload); }, deleteTodo ({ commit }, payload) { commit('deleteTodo', payload); } } }); export default store; ``` 在上面的代码中,我们定义了一个todos数组作为状态,然后定义了三个mutations来修改这个状态。addTodo用于添加一个todo项,toggleTodo用于切换一个todo项的完成状态,deleteTodo用于删除一个todo项。最后,我们定义了三个actions,这些actions用于触发mutations来修改状态。 接下来,我们可以在组件中使用Vuex: ```vue <template> <div> <h2>TodoList</h2> <form @submit.prevent="addTodo"> <input type="text" v-model="newTodoText"> <button type="submit">Add Todo</button> </form> <ul> <li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.completed }"> <input type="checkbox" v-model="todo.completed" @change="toggleTodo(todo)"> <span>{{ todo.text }}</span> <button @click="deleteTodo(todo)">Delete</button> </li> </ul> </div> </template> <script> import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState(['todos']), newTodoText: { get () { return ''; }, set (value) { this.$data.newTodoText = value; } } }, methods: { ...mapActions(['addTodo', 'toggleTodo', 'deleteTodo']) } }; </script> ``` 在上面的代码中,我们使用了Vuex提供的mapState和mapActions辅助函数,来将todos状态和addTodo、toggleTodo、deleteTodo等actions映射到组件的computed和methods中。 最后,在组件的created钩子中,我们可以使用Vuex的actions来初始化状态: ```javascript import store from './store'; export default { created () { this.$store.dispatch('addTodo', { text: '学习Vuex' }); this.$store.dispatch('addTodo', { text: '学习Vue Router' }); }, store }; ``` 以上就是使用Vuex实现TodoList的示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值