从零开始构建一个Vue项目

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

2020-02-23  本文已影响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,什么时候直接用简单的通信方式哩?

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

vuex的构成
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的是怎么运行的?

  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的组件中使用

<!-- 测试项目 -->
<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方法以及各种语法糖,更深层次是如何实现的?

上一篇下一篇

猜你喜欢

热点阅读