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="vue.js"></script>
    <script type="text/javascript" src="vue-router.js"></script>
    <script type="text/javascript" src="vuex.js"></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

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM