美文网首页从零开始构建一个Vue项目
Vue四、vuex是个啥?怎么用?

Vue四、vuex是个啥?怎么用?

作者: 好一只帅卤蛋 | 来源:发表于2020-02-23 22:31 被阅读0次

    vuex官方文档

    一、vuex是什么

    官方解释是:Vuex是通过全局注入store对象,来实现组件间的状态共享,是一个专为 Vue.js 应用程序开发的状态管理模式

    我的理解是,例如你的项目里某一个数据在前端多个组件中都有应用,如果一个改的话,那岂不是每个组件都需要改一次,特别是类似的数据多起来的话,操作起来想想就繁杂,于是,可以通过Vuex来实现组件间的状态共享,改一个,其他组件中的值的状态自动改变。

    * 那么可能有人问:直接用全局对象不久可以了?

    Vuex 和单纯的全局对象有以下两点不同:

    1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
    2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

    * 那么可能有人问:Vuex与localStorage不一样么?

    vuex 是 vue 的状态管理器,存储的数据是响应式的,但是并不会保存起来,刷新之后就回到了初始状态,但是localStorage是保存在浏览器中的,刷新之后还可以取出来继续使用。而且,vuex里,我们保存的一般都是数组,而localStorage保存到话只支持字符串

    * 那么可能有人问:既然是存数据的,那什么时候用vuex,什么时候直接用简单的通信方式哩?

    • 如果项目足够简单(只是多个组件间传递数据),最好不要使用 Vuex。一个简单的 store 模式就足够所需了,只使用组件间常用的通信方法即可,使用 Vuex 可能是繁琐冗余的。

    Vue组件简单常用的通信方式有以下几种:
    1、父向子传值通过props的方式;
    2、子向父传值通过events ($emit),实际上就是子组件把自己的数据发送到父组件;
    3、父调用子方法通过ref;provide / inject。
    4、兄弟之间通信通过bus
    5、跨级嵌套通信可以使用bus;provide / inject等。
    6、 vue组件间通信六种方式(完整版)

    • 如果需要构建一个中大型单页应用(多级组件嵌套),一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,Vuex 将会成为自然而然的选择。
    vuex的构成
    • 由上面官方给的图可以看出vuex由以下几部分构成
      1. State
      state是存储的单一状态,是存储的基本数据。对象必须是纯粹的对象 (含有零个或多个的 key/value 对)。它作为一个“唯一数据源 (SSOT)”而存在。
      2. Mutation
      更改 Vuex 的 store 中的状态的唯一方法是提交 mutation更改数据【必须是同步函数】。(使用store.commit方法更改state存储的状态)
      3. Action
      Action 类似于 mutation,不同在于:
      Action 提交的是 mutation,而不是直接变更状态。
      Action 可以包含任意【异步操作】。
      Action 通过 store.dispatch 方法触发,Mutation使用store.commit触发。
      4. Getter
      getters是store的计算属性,可以通过store 中的 state 中派生出一些状态(比如说过滤)。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。
      5. Module
      当应用变得复杂时,store对象可能变得相当臃肿复杂。为了解决这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
    const moduleA = {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: { ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    

    * 所以vuex的是怎么运行的?

    • vuex的运行流程
    1. 在组件内部,通过dispatch来分发action。
    2. 再通过action来第调用mutation
    3. 进而触发mutation内部的commit来修改state
    4. 最后state改变,导致页面重新render。

    二、vuex怎么用

    1.安装

    NPM
    npm install vuex --save
    Yarn
    yarn add vuex

    2.中型数据不太复杂的项目中(直接使用)

    其实使用cli工具初始化的项目中如果选择安装vuex的话就已经有了。如下:
    src文件夹中新建一个store文件夹,下面新建一个名为index.js的文件。如下

    src文件夹下

    index.js中注册vuex,并且在state中初始化
    一个count变量介绍mutations
    一个tasks数组 + 一个taskFinish方法介绍getters
    一个increment方法介绍actions

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: { 
          count:0,
          tasks: [
             { id: 1, finish: true },
             { id: 2, finish: false }
          ]
     },
      mutations: {
          // 更改 Vuex 的 store 中的状态的唯一方法
         countAdd (state) {
                // 自定义将传过来的参数操作操作
                state.count++
               }
      },
      getters: {
            // getters是store的计算属性, 有时候需要从  store 中的 state 中派生出一些状态(比如说过滤)
           taskFinish: state => {
                  return state.tasks.filter(a=> a.finish)
        }
       },
      actions: { 
             // actions 提交的是 mutation,而不是直接变更状态.
            increment(context) {
                context.commit('countAdd')
              }
      },
      modules: {}
    });
    
    

    main.js中导入store实例,我们就可以通过this.$store.state访问这些状态,一般把它的值注入到computed

    import Vue from "vue";
    import App from "./App.vue";
    import router from "./router";
    import store from "./store";
    
    // 阻止启动生产消息,常用作指令 ⬇
    Vue.config.productionTip = false;
    
    new Vue({
      router,
      store,   // 注册,全局使用vuex,(this.$store)
      render: h => h(App)
    }).$mount("#app");
    
    

    在后缀名为.vue的组件中使用

    • this.$store.state.count实现数据调用
    • this.$store.commit('countAdd')实现通过mutations实现数据修改(同步方式)
    • this.$store.getters.taskFinish实现通过getters实现 state 中的数据派生出一些状态(过滤数据)
    <!-- 测试项目 -->
    <template>
      <div>      
            <h3>{{this.$store.state.count}}</h3>
            <h4>{{this.$store.getters.taskFinish}}</h4>
            <input type="button" value="count自增"  @click="countAdd"></div>
            <button @click="increment">按钮</button>
    </template>
    
    <script>
    export default {
      data() {
        return {};
      },
    
      components: {},
    
      computed: {},
    
      mounted() {},
    
      methods: {
           countAdd() {
                // commit不仅可以传state的参数,而且可以传额外的参数,只需在mutations里定义的函数后面的参数里与这里的一致就可以
                this.$store.commit('countAdd')
            },
           increment(){
                // 效果跟countAdd()是一样的
                this.$store.dispatch('increment')
            }
      }
    };
    </script>
    <style>
    </style>
    
    

    * 看完例子之后可能有人说了,mutations和action效果不是一样的么,直接使用mutations不就行了,何必再用action做一次类似于请求转发的操作呢?

    :实际上并非如此。还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作

    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() => {
          commit('increment')
        }, 1000)
      }
    }
    

    Actions 支持同样的载荷方式对象方式进行分发:

    // 以载荷形式分发
    store.dispatch('incrementAsync', {
      amount: 10
    })
    
    // 以对象形式分发
    store.dispatch({
      type: 'incrementAsync',
      amount: 10
    })
    

    当然这些方法只是简单的介绍理解一下vuex里面的东西,深入理解还需要自己实际写一下去探索。上面输出的格式可以简化,例如
    this.$store.state.count放入computed计算属性当中去

    export default {
     name: 'App',
      computed:{
           count(){
               return this.$store.state.count;
           }
       }
    }
    

    然后直接通过<h3>{{count}}</h3>调用即可

    3.大型数据密集型项目中使用(需要将其划分为模块使用)

    方法一:src文件夹中新建一个store文件夹,下面分别新建名为index.js / actions.js / getter.js / mutations.js的文件。

    store文件夹下
    方法一例子来自于vuex最详细完整的使用用法,让大家知道每个文件里的内容的格式

    index.js

    
    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as getters from './getters' // 导入相应的模块,*相当于引入了这个组件下所有导出的事例,使用*和from关键字来实现的模块的继承
    import * as actions from './actions'
    import * as mutations from './mutations'
     
    Vue.use(Vuex)
    // 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
    const state = {
        resturantName: '飞歌餐馆' // 默认值
        // id: xxx  如果还有全局状态也可以在这里添加
        // name:xxx
    }
     
    // 注册上面引入的各大模块
    const store = new Vuex.Store({
        state,    // 共同维护的一个状态,state里面可以是很多个全局状态
        getters,  // 获取数据并渲染
        actions,  // 数据的异步操作
        mutations  // 处理数据的唯一途径,state的改变或赋值只能在这里
    })
     
    export default store  // 导出store并在 main.js中引用注册。
    
    

    actions.js

    // 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
    export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
        return commit ('modifyAName', name)
    }
    export function modifyBName({commit}, name) {
        return commit ('modifyBName', name)
    }
     
    // ES6精简写法
    // export const modifyAName = ({commit},name) => commit('modifyAName', name)
    
    

    mutations.js

    // 提交 mutations是更改Vuex状态的唯一合法方法
    export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
        state.resturantName = name   // 把方法传递过来的参数,赋值给state中的resturantName
    }
    export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
        state.resturantName = name
    }
    
    

    getters.js

    // 获取最终的状态信息
    export const resturantName = state => state.resturantName
    

    在后缀为.vue的文件中

    像上面一样类似操作

    方法二:新建一个名为modules的文件夹,modules文件夹下面分别新建自己对应需要的模块。

    store文件夹下
    每一个modules文件夹下的文件里
    const moduleA = {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    moudel.exports = moduleA 
    

    index.js中注册使用的话

    
    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as getters from './modules/cart' // 导入相应的模块,使用*和from关键字来实现的模块的继承
    import * as  ...
     
    Vue.use(Vuex)
    
    // 注册上面引入的各大模块
    const store = new Vuex.Store({
       modules: {
        a: moduleA,
        ...
      } 
    })
     
    export default store  // 导出store并在 main.js中引用注册。
    

    使用:

    store.state.a.xx // -> moduleA 的状态
    

    4.一些思考

    (1)、vuex中的设计理念?为什么能实现各个组件间数据的监听?

    (2)、vuex是如何实现在各个组件中,store里面的数据以响应的方式来实现实时跟新的?

    (3)、vuex中的那些commit ,dispatch方法以及各种语法糖,更深层次是如何实现的?

    相关文章

      网友评论

        本文标题:Vue四、vuex是个啥?怎么用?

        本文链接:https://www.haomeiwen.com/subject/zoseqhtx.html