09:vuex组件间通信

 1.1 vuex简介

  官网:https://vuex.vuejs.org/zh/guide/

  参考博客:https://www.cnblogs.com/first-time/p/6815036.html   

  1、什么是Vuex?

      1. 官方说法:Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。
      2. 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
      3. 个人理解:Vuex是用来管理组件之间通信的一个插件

  2、vuex作用

      1. 我们知道组件之间是独立的,组件之间想要实现通信,我目前知道的就只有props选项,但这也仅限于父组件和子组件之间的通信。
      2. 如果兄弟组件之间想要实现通信呢?当做中大型项目时,面对一大堆组件之间的通信,还有一大堆的逻辑代码,会不会很抓狂??
      3. 那为何不把组件之间共享的数据给“拎”出来,在一定的规则下管理这些数据呢? 这就是Vuex的基本思想了。

      总结:使用vuex作用就是实现组件间数据共享

  3、vuex原理

      1. vue团队为了简化组件间的通信,将state抽象成一个单例模式,将其放到全局,让各个组件都能共享使用

      2. vuex数据传递是单向的:action ---> mutation ---> state ---> component ---> action

          vue component指的就是我门定义的组件
          action 交互中产生的动作
          mutations 动作产生的修改数据的行为
          state 共享数据

      3. vuex设计的时候相对修改的行为做单测(测试),开发了devtools来做测试,只能检测同步的操作

      4. 规范定义:只能在mutations中做同步操作,所以增加了action来异步处理数据

      5. 将mutations中的异步操作转移到actions中了,这样就可以测试同步的操作了

  4、vuex使用场景

      1. 如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

      2. 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。

  5、vuex流程图

      1、vue组件(Vue Components)会 发出(Dispatch)一些动作(Actions)
      2、动作会 提交(Commit)一个对数据的改变(Mutations)
      3、提交的改变数据存放在 状态(State) 中的
      4、最后 State将改变的数据再 渲染(Render)到组件(Vue Components),展示被改变后的数据

      

 1.2 vuex使用(vuex使用分为以下两步)

  1、第一步:实例化一个store

      注:vuex.store用来创建store,参数对象中,可以定义各个模块

      1. state定义状态模块(存储数据的),存放组件之间共享的数据

      2. getters定义动态的数据,类似组件中的computed动态数据

      3. mutations:定义改动states的动作行为,类似观察者模式中的订阅事件on

      4. action:定义这些交互动作(异步),类似观察者模式中的订阅事件方法on(只不过是用来处理异步的)

  2、第二步:在vue实例化对象中,注册store

      1. 将第一步的实例化对象注册进来,注册路由后,组件实例化对象有了$route属性对象

      2. 注册store,组件实例化对象有了$store属性对象,这个store对象有下面这些方法

          $.store.commit用来触发mutations订阅的消息

          $.store.dispatch用来触发action订阅的消息的

          $.store.state使用状态中的数据

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<div id="app">
  <h1 @click="$store.commit('reduce', 20);">vue实例化对象:点击减20</h1>
  <h2 @click="$store.dispatch('dealNum', 10, 20, 30)">更新数据:将num两秒后重置为:10</h2>
  <h1>state中的数据 {{$store.state.num}}</h1>
  <h2>双倍num {{$store.getters.doubleNum}}</h2>
  <router-view></router-view>                          <!-- 定义渲染的容器 -->
</div>
<template id="home">
  <div>
    <h1 @click="$store.commit('add', 10, 'hello')">home:点击加10</h1>
    <h2>home组件中 {{$store.state.num}}</h2>
    <router-view></router-view>                     <!-- 第一步 定义子路由渲染的容器 -->
  </div>
</template>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script type="text/javascript" src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>
<script type="text/javascript" src="https://unpkg.com/vuex@2.0.0"></script>
<script type="text/javascript">
  // 定义组件
  var Home = {
    template: '#home'
  };

  // 第一步 定义路由规则
  var routes = [
    {
      path: '/home',
      name: 'home',
      component: Home
    }
  ];

  // 定义store第一步 定义store实例化对象
  var store = new Vuex.Store({
    state: {                                       // 定义状态
      num: 0
    },
    getters: {                                       // 定义动态绑定的数据
      doubleNum: function(state) {
        return state.num * 2;
      }
    },
    mutations: {                                   // 修改的消息
      add: function(state, num) {                    // 增加num值
        state.num += num;
      },
      reduce: function(state, num) {                // 减少num值
        state.num -= num;
      },
      resetNum: function(state, num) {
        state.num = num;
      }
    },
    actions: {                                        // 定义actions
      dealNum: function(context, num) {
        setTimeout(function() {                    // 我们可以异步提交
          context.commit('resetNum', num)
        }, 2000)
      }
    }
  });

  // 第二步 实例化路由对象
  var router = new VueRouter({
    routes: routes                    // 定义路由规则
  });

  // 第三步 注册路由 和 store对象
  var app = new Vue({
    el: '#app',                        // 注册路由
    router: router,
    store: store                    // 使用vuex第二步 注册store
  })
</script>
</body>
</html>
vuex基本使用

     

1.3 vuex基本用法

  1、初始化环境

      vue init webpack-simple vuex-demo

      cd vuex-demo

      npm install

      cnpm install vuex -S     # 安装vuex

      npm run dev

  2、在main.js中导入并配置store.选项(创建 sre/store.js文件,可以是一个空文件

      1. 在main.js中导入 store对象:  import store from './store'

      2. 配置store选项后,vue就会自动将store对象注入到所有子组件中,在子组件中通过this.$store 访问store对象

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

import store from './store'  // 导入store对象

new Vue({
  store,  // 配置store选项后,指定为store对象,vue就会自动将store对象注入到所有子组件中
          // 在子组件中通过this.$store 访问store对象
  el: '#app',
  render: h => h(App)
});
main.js

  3、编辑store.js文件

      注1:Vuex的核心是Store(仓库),相当于是一个容器,一个store实例中包含以下属性的方法:

      注2:不能直接修改数据,必须显式提交变化,目的是为了追踪到状态的变化

      1) state 定义属性(状态、数据)

      2) getters 用来获取属性

      3) actions 定义方法(动作)

      4) commit 提交变化,修改数据的唯一方式就是显式的提交mutations

      5) mutations 定义变化

/**
 * vuex配置:store.js
 **/
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

//1、定义属性(数据)
var state = {
  count:6
};

//2、定义gettters获取属性:在App.vue中使用 辅助函数 访问 vuex 组件中数据调用此函数
var getters={
  count(state){
    return state.count;
  }
};

//3、定义actions提交变化:其他组件中调用的方法()
const actions = {
  increment({commit,state}){       // context包含属性(函数):commit,dispatch,state
    if(state.count<10){           // 当count数值小于10才会提交改变(大于10就不增加了)
      commit('increment');
    }
    // 1、commit提交改变(不能直接修改数据)
    // 2、commit中的参数 'increment' 是自定义的,可以认为是类型名
    // 3、commit提交的改变会给 mutations
  }
};

//4、定义mutations定义变化,处理状态(数据的改变)
const mutations={
  increment(state){
    state.count++;
  }
};

// 创建一个store对象(对象里定义需要导出的变量)
const store=new Vuex.Store({
  state,
  getters,
  actions,
  mutations,
});

// 导出store对象
export default store;
sre/store.js

  4、 编辑App.vue

    1. 在子组件中访问store对象的两种方式

        方式1:通过this.$store访问

        方式2:通过辅助函数:mapState、mapGetters、mapActions 访问,vuex提供了两个方法

            mapState        获取state

            mapGetters     获取getters(获取属性:数据)

            mapActions     获取actions(获取方法:动作)

<template>
  <div id="app">
    <button @click="increment">增加</button>
    <button>减小</button>
    <p>当前数字为:{{count}}</p>
  </div>
</template>

<script>
  import {mapGetters,mapActions} from 'vuex'

export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },

  // 方式二:使用 辅助函数 访问 vuex 组件中数据
  computed:mapGetters([            // 这里定义一个数组,数组中指定要从vuex中获取的属性
    'count',                       // 这里的count就是 store.js中getters定义的属性
  ]),
  methods:mapActions([             // 这里定义一个数组,数组中指定要从vuex中获取的方法
    'increment'                   // 这里的increment就是 store.js中actions定义的函数
  ])

  // // 方式一:通过this.$store访问vuex组件中的数据
  // computed:{
  //   count(){
  //     return this.$store.state.count;
  //   }
  // }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
App.vue

  5、效果图

      

  6、异步操作

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

import store from './store'  // 导入store对象

new Vue({
  store,  // 配置store选项后,指定为store对象,vue就会自动将store对象注入到所有子组件中
          // 在子组件中通过this.$store 访问store对象
  el: '#app',
  render: h => h(App)
});
main.js
/**
 * vuex配置:store.js
 **/
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

//1、定义属性(数据)
var state = {
  count:6
};

//2、定义gettters获取属性:在App.vue中使用 辅助函数 访问 vuex 组件中数据调用此函数
var getters={
  count(state){
    return state.count;
  }
};

//3、定义actions提交变化:其他组件中调用的方法()
const actions = {
  increment({commit,state}){       // context包含属性(函数):commit,dispatch,state
    if(state.count<10){           // 当count数值小于10才会提交改变(大于10就不增加了)
      commit('increment');
    }
    // 1、commit提交改变(不能直接修改数据)
    // 2、commit中的参数 'increment' 是自定义的,可以认为是类型名
    // 3、commit提交的改变会给 mutations
  },

  /** 定义异步操作 **/
  incrementAsyn({commit,state}){
    var p=new Promise((resolve,reject) => {    // 异步操作
      setTimeout(() => {
        resolve()
      },3000)
    });
    p.then(() => {                             // 上面执行完成后才执行 p.then()
      commit('increment');
    }).catch(() => {                           // 异常处理
      console.log('异步操作失败')
    })
  }
};

//4、定义mutations定义变化,处理状态(数据的改变)
const mutations={
  increment(state){
    state.count++;
  }
};

// 创建一个store对象(对象里定义需要导出的变量)
const store=new Vuex.Store({
  state,
  getters,
  actions,
  mutations,
});

// 导出store对象
export default store;
store.js
<template>
  <div id="app">
    <button @click="increment">增加</button>
    <button @click="incrementAsyn">异步增加</button>
    <p>当前数字为:{{count}}</p>
  </div>
</template>

<script>
  import {mapGetters,mapActions} from 'vuex'

export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },

  // 方式二:使用 辅助函数 访问 vuex 组件中数据
  computed:mapGetters([            // 这里定义一个数组,数组中指定要从vuex中获取的属性
    'count',                       // 这里的count就是 store.js中getters定义的属性
  ]),
  methods:mapActions([             // 这里定义一个数组,数组中指定要从vuex中获取的方法
    'increment',                   // 这里的increment就是 store.js中actions定义的函数
    'incrementAsyn'                // 异步提交
  ])
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
App.vue

    

1.4 分模块组织Vuex

  1、初始化环境

      vue init webpack-simple vuex-demo2

      cd vuex-demo2

      npm install

      cnpm install vuex -S      # 安装vuex

      npm run dev

  2、Vuex模块化结构

|-src
    |-main.js                       // 项目入口文件
    |-App.vue
    
    |-store
        |-index.js                  // 我们组装模块并导出 store 的地方
        |-getters.js                // 公共的 getters (用来获取公共属性)
        |-actions.js                // 根级别的 action (提交公共改变)
        |-mutations.js              // 根级别的 mutation (处理状态,数据的改变)
        |-types.js                  // 定义类型常量(commit中提交的常量)

        |-modules                   //分为多个模块,每个模块都可以拥有自己的state、getters、actions、mutations
            |-user.js               // 用户模块(这里仅以user模块作为事例)
Vuex模块化结构

  3、代码事例

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

import store from './store/index.js'

new Vue({
  store,
  el: '#app',
  render: h => h(App)
});
main.js
<template>
  <div id="app">
    
    <button @click="increment">增加</button>
    <button @click="decrement">减小</button>
    <button @click="incrementAsync">增加</button>
    <p>当前数字为:{{count}}</p>
    <p>{{isEvenOrOdd}}</p>

  </div>
</template>

<script>
import {mapState,mapGetters,mapActions} from 'vuex'

export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  computed:mapGetters([
      'count',
      'isEvenOrOdd'
  ]),
  methods:mapActions([
      'increment',
      'decrement',
      'incrementAsync'
  ])
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
App.vue
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

import getters from './getters.js'
import actions from './actions.js'
import user from './modules/user.js'

export default new Vuex.Store({
    getters,
    actions,
    modules:{
        user
    }
});
store/index.js
const getters={
    isEvenOrOdd(state){
        return state.user.count%2==0?'偶数':'奇数';  // user模块中的count
    }
};

export default getters;
store/getters.js
import types from './types.js'

const actions={
    incrementAsync({commit,state}){
        //异步操作
        var p=new Promise((resolve,reject) => {
            setTimeout(() => {
                resolve();
            },3000);
        });

        p.then(() => {
            commit(types.INCREMENT);
        }).catch(() => {
            console.log('异步操作');
        });
    }
};

export default actions;
store/actions.js
/**
 * 定义类型常量
 */

const INCREMENT='INCREMENT';
const DECREMENT='DECREMENT';

export default {
    INCREMENT,
    DECREMENT
}
store/types.js
/**
 * 用户模块
 */

import types from '../types.js'

 const state={
     count:6
 };

var getters={
    count(state){
        return state.count;
    }
};

const actions = {
    increment({commit,state}){
        commit(types.INCREMENT); //提交一个名为increment的变化,名称可自定义,可以认为是类型名
    },
    decrement({commit,state}){
        if(state.count>10){
            commit(types.DECREMENT);
        }
    }
};

const mutations={
    [types.INCREMENT](state){   // ES6中中括号里表示 变量
        state.count++;
    },
    [types.DECREMENT](state){
        state.count--;
    }
};

export default {
    state,
    getters,
    actions,
    mutations
}
store/modules/user.js

  4、项目结构 

         

  5、简化版

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

import store from './store/index'

new Vue({
  store,
  el: '#app',
  render: h => h(App)
})
main.js
<template>
  <div id="app">
    <h1>app</h1>
    <p>数据:{{count}}</p>
    <p @click="increment">增加</p>
  </div>
</template>

<script>
  import {mapGetters, mapActions} from 'vuex'

export default {
  name: 'app',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  computed:mapGetters([
    'count',
  ]),
  methods:mapActions([
    'increment'
  ])
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
App.vue
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

// import getters from './getters.js'
// import actions from './actions.js'
import user from './modules/user.js'

export default new Vuex.Store({
  // getters,
  // actions,
  modules:{
    user
  }
});
src\store\index.js
//1、定义属性(数据)
var state = {
  count:6
};

//2、定义gettters获取属性:在App.vue中使用 辅助函数 访问 vuex 组件中数据调用此函数
var getters = {
  count(state){
    return state.count
  }
};

//3、定义actions提交变化:其他组件中调用的方法()
var actions = {
  increment({commit,state}){
    commit('increment')
  }
};

//4、定义mutations定义变化,处理状态(数据的改变)
var mutations = {
  increment(state){
    state.count++
  }
};

//5、导出store对象
export default {
  state,
  getters,
  actions,
  mutations
}
src\store\modules\user.js

    

 

posted @ 2018-09-25 09:23  不做大哥好多年  阅读(1503)  评论(0编辑  收藏  举报