深入淺出的webpack4構建工具--webpack4+vue+route+vuex專案構建(十七)

龍恩0707發表於2018-10-09

閱讀目錄

一:vue傳值方式有哪些?

二:理解使用Vuex

三:webpack4+vue+route+vuex 專案架構

一:vue傳值方式有哪些?

在vue專案開發過程中,經常會使用元件來進行專案開發,那麼在元件中會呼叫另一個元件來作為自己的子元件,那麼我們如何進行給子元件進行傳值呢?或者說,子元件如何給父元件傳值呢?父子元件資料是如何通訊的呢?
因此vue常用的傳值方式有如下幾種:
1. 父傳子
2. 子傳父
3. 非父子傳值

父子元件傳遞資料可以看如下圖所示

總結是:父元件向子元件傳遞資料,可以通過prop屬性向下傳遞資料,子元件向父元件傳遞資料是通過事件給父元件傳送訊息。

下面我們看如下父子元件的demo。在看demo之前,我們看下專案目錄的結構如下:

### 目錄結構如下:
demo1                                       # 工程名
|   |--- dist                               # 打包後生成的目錄檔案             
|   |--- node_modules                       # 所有的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放所有vue頁面檔案
|   | | | |-- parent.vue                    # 父元件
|   | | | |-- child.vue                     # 子元件
|   | | | |-- index.vue
|   | | |-- components                      # 存放vue公用的元件
|   | | |-- js                              # 存放js檔案的
|   | | |-- app.js                          # vue入口配置檔案
|   | | |-- router.js                       # 路由配置檔案
|   |--- views
|   | |-- index.html                        # html檔案
|   |--- webpack.config.js                  # webpack配置檔案 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉碼檔案

1. 父元件向子元件傳值demo如下:

app/index/views/parent.vue 程式碼如下:

<template>
  <div>
    <div>
      <label>父元件:</label>
      <input type='text' v-model = 'name' />
      <br />
      <!-- 引入子元件 -->
      <child :inputName="name"></child>
    </div>
  </div>
</template>

<script type="text/javascript">
  import child from './child';
  export default {
    data() {
      return {
        name: ''
      }
    },
    components: {
      child: child
    }
  }
</script>

app/index/views/child.vue 程式碼如下:

<template>
  <div>
    <label>子元件:</label>
    <span>{{inputName}}</span>
  </div>
</template>

<script type="text/javascript">
  export default {
    // 接收父元件的值
    props: {
      inputName: String,
      required: true
    }
  }
</script>

app/index/views/index.vue 程式碼如下:

<style lang="stylus">
  
</style>

<template>
  <div id="app">
    <header>
      <router-link to="/parent" tag='li'>parent</router-link>
    </header>
    <!-- 對應元件的內容渲染到router-view中 -->
    <router-view></router-view>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        
      }
    }
  }
</script>

app/index/router.js 程式碼如下:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/parent',
    name: 'parent',
    component: resolve => require(['./views/parent'], resolve)
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/parent'
  }
]

var router = new VueRouter({
  base: '/app/index', // 配置單頁應用的基路徑
  routes: routes
});

export default router;

因此頁面執行效果如下圖所示:

2. 子元件向父元件傳值方式demo如下:

app/index/views/parent.vue 程式碼改成如下:

<template>
  <div>
    <div>
      <label>父元件:</label>
      <span style="color:red;">{{name}}</span>
      <br />
      <!-- 引入子元件 定義一個方法來監聽子元件的狀態值 -->
      <child @childChange="childChangeFunc"></child>
    </div>
  </div>
</template>

<script type="text/javascript">
  import child from './child';
  export default {
    data() {
      return {
        name: ''
      }
    },
    methods: {
      childChangeFunc(value) {
        // value 就是子元件傳遞過來的值
        this.name = value;
      }
    },
    components: {
      child: child
    }
  }
</script>

app/index/views/child.vue 程式碼改成如下:

<template>
  <div>
    <label>子元件:</label>
    <span>{{childValue}}</span>
    <!-- 子元件事件傳值 -->
    <input type='button' value='點選觸發' @click="childClick" />
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        childValue: '我是子元件的資料'
      }
    },
    methods: {
      childClick() {
        // 子元件觸發事件
        this.$emit('childChange', this.childValue);
      }
    }
  }
</script>

執行如下:

麼有觸發點選之前的效果圖如下:

點選觸發按鈕後,效果圖如下:

3. 非父子元件進行傳值

非父子元件之間傳值,需要定義個公共的實列檔案來作為中間倉庫來傳值的。比如定義一個叫 bus.js 檔案。
所謂中間倉庫就是建立一個事件中心,相當於中轉站,可以使用它來傳遞事件和接收事件的。

在app/index 下新建一個js資料夾,在資料夾內新建一個bus.js檔案,程式碼如下:

import Vue from 'vue';

export default new Vue();

在app/index/views/a.vue, 程式碼如下:

<template>
  <div>
    <label>A元件:</label>
    <span>{{value}}</span>
    <input type="button" value="點選觸發" @click="clickFunc" />
  </div>
</template>

<script type="text/javascript">
  // 引入公共的bus
  import Bus from '../js/bus.js';
  export default {
    data() {
      return {
        value: 1
      }
    },
    methods: {
      clickFunc() {
        Bus.$emit('val', this.value);
      }
    }
  }
</script>

在app/index/views/b.vue, 程式碼如下:

<template>
  <div>
    <label>B元件:</label>
    <input type='button' value='點選觸發' @click="getDataFunc" />
    <span>{{name}}</span>
  </div>
</template>

<script type="text/javascript">
  import Bus from '../js/bus.js';
  export default {
    data() {
      return {
        name: 0
      }
    },
    mounted() {
      const vm = this;
      // 使用$on事件來接收引數
      Bus.$on('val', (data) => {
        console.log(data);
        vm.name = data;
      });
    },
    methods: {
      getDataFunc() {
        this.name++;
      }
    }
  }
</script>

app/index/views/index.vue 程式碼改成如下:

<style lang="stylus">
  
</style>

<template>
  <div id="app">
    <A></A>
    <B></B>
  </div>
</template>

<script type="text/javascript">
  import A from './a';
  import B from './b';
  export default {
    data() {
      return {
        
      }
    },
    components: {
      A: A,
      B: B
    }
  }
</script>

頁面進來的時候,頁面渲染成如下圖所示

當點選 A 元件後的點選觸發後,B元件的值變成了4,如下圖所示:

二:理解使用Vuex

1. 什麼是vuex?

Vuex官網的解釋:Vuex是一個專為vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。

官網的解釋很模糊。其實在vue元件開發中,經常會需要將當前的元件的資料傳遞給其他的元件,父子元件通訊的話,我們可以採用props+emit 這種方式,如上面的demo方式來傳遞資料,但是當通訊雙方不是父子元件甚至根本不存在任何關係的時候,或者說一個狀態需要共享給多個元件的時候,那麼就會非常麻煩,資料維護也相當的不好維護,因此就出現了vuex。它能幫助我們把公用的狀態抽出來放在vuex的容器中,然後根據一定的規則進行管理。vuex採用了集中式儲存管理所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。

2. 怎麼使用vuex?

如下demo使用:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
  <script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
  <div id='app'></div>
  <script type="text/javascript">
    Vue.use(Vuex); // 使用vuex
    var myStore = new Vuex.Store({
      // state是儲存狀態 定義應用狀態全域性的資料結構
      state: {
        name: 'kongzhi',
        todoLists: []
      },
      /*
        mutations是提交狀態修改,也就是set、get中的set,這是vuex中唯一修改state的方式,但是不支援非同步操作。
        每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)
        第一個引數預設是state,外部呼叫方式為:store.commit('SET_AGE', 30).
      */
      mutations: {
        // 新增list
        ADDLIST(state, item) {
          state.todoLists.push(item);
        },
        // 刪除list中的項
        DELLIST(state, index) {
          state.todoLists.splice(index, 1);
        },
        // 設定 錯誤提示資訊
        SETERROR(state, msg) {
          state.message = msg;
        }
      },
      /*
        getters是從state中派生出狀態的。也就是set、get中的get,它有兩個可選的引數,state和getters,
        分別可以獲取state中的變數和其他的getters。外部呼叫的方式:store.getters.todoCount()
      */
      getters: {
        todoCount(state) {
          return state.todoLists.length;
        }
      },
      /*
       和上面的mutations類似,但是actions支援非同步操作的,外部呼叫方式為:store.dispatch('nameAction')
       常見的使用是:從伺服器端獲取資料,在資料獲取完成後會呼叫 store.commit()來更改Store中的狀態。
       Action函式接收一個與store實列具有相同方法和屬性的context物件,因此我們可以使用 context.commit 提交一個
       mutation,或者通過 context.state 和 context.getters來獲取state和getters
      */
      actions: {
        addList(context, item) {
          if (item) {
            context.commit('ADDLIST', item);
            context.commit('SETERROR', '');
          } else {
            context.commit('SETERROR', '新增失敗');
          }
        },
        delList(context, index) {
          context.commit('DELLIST', index);
          context.commit('SETERROR', '刪除成功');
        }
      },
      /*
       modules 物件允許將單一的Store拆分為多個Store的同時儲存在單一的狀態樹中。
      */
      modules: {

      }
    });
    new Vue({
      el: '#app',
      data: {
        name: 'init name'
      },
      store: myStore,
      mounted: function() {
        console.log(this);
      }
    })
  </script>
</body>
</html>

如上程式碼,new Vuex.store({}) 含義是建立一個Vuex實列。store是vuex的一個核心方法,字面含義為 '倉庫'的意思。實列化完成後,需要注入到vue實列中,它有五個核心的選項,state、mutations、getters、actions和modules。如下圖所示:

檢視demo1.html

3. vuex中如何獲取state的資料呢?
下面我們來註冊一個元件,那麼在元件內部中的computed來獲取state的資料(computed是實時響應的)。如下程式碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
  <script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
  <div id='app'>
    <xxx></xxx>
  </div>
  <script type="text/javascript">
    Vue.use(Vuex); // 使用vuex
    var myStore = new Vuex.Store({
      // state是儲存狀態 定義應用狀態全域性的資料結構
      state: {
        name: 'kongzhi',
        todoLists: []
      },
      /*
        mutations是提交狀態修改,也就是set、get中的set,這是vuex中唯一修改state的方式,但是不支援非同步操作。
        每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)
        第一個引數預設是state,外部呼叫方式為:store.commit('SET_AGE', 30).
      */
      mutations: {
        // 新增list
        ADDLIST(state, item) {
          state.todoLists.push(item);
        },
        // 刪除list中的項
        DELLIST(state, index) {
          state.todoLists.splice(index, 1);
        },
        // 設定 錯誤提示資訊
        SETERROR(state, msg) {
          state.message = msg;
        }
      },
      /*
        getters是從state中派生出狀態的。也就是set、get中的get,它有兩個可選的引數,state和getters,
        分別可以獲取state中的變數和其他的getters。外部呼叫的方式:store.getters.todoCount()
      */
      getters: {
        todoCount(state) {
          return state.todoLists.length;
        }
      },
      /*
       和上面的mutations類似,但是actions支援非同步操作的,外部呼叫方式為:store.dispatch('nameAction')
       常見的使用是:從伺服器端獲取資料,在資料獲取完成後會呼叫 store.commit()來更改Store中的狀態。
       Action函式接收一個與store實列具有相同方法和屬性的context物件,因此我們可以使用 context.commit 提交一個
       mutation,或者通過 context.state 和 context.getters來獲取state和getters
      */
      actions: {
        addList(context, item) {
          if (item) {
            context.commit('ADDLIST', item);
            context.commit('SETERROR', '');
          } else {
            context.commit('SETERROR', '新增失敗');
          }
        },
        delList(context, index) {
          context.commit('DELLIST', index);
          context.commit('SETERROR', '刪除成功');
        }
      },
      /*
       modules 物件允許將單一的Store拆分為多個Store的同時儲存在單一的狀態樹中。
      */
      modules: {

      }
    });
    // 註冊vue元件 xxx
    Vue.component('xxx', {
      template: "<div>{{name}}</div>",
      computed: {
        name: function() {
          console.log(this.$store.state);
          return this.$store.state.name;
        }
      },
      mounted: function() {
        console.log(this);
      }
    });
    new Vue({
      el: '#app',
      data: {
        name: 'init name'
      },
      store: myStore,
      mounted: function() {
        console.log(this);
      }
    })
  </script>
</body>
</html>

執行效果如下圖所示:

檢視demo2.html

4. 如何對state的資料進行篩選和過濾。
有時候,我們需要對state的資料進行刷選和過濾操作,比如後臺請求回來的資料,我們需要進行資料過濾操作,getters就可以了。具體程式碼可以看如下demo:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
  <script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
  <div id='app'>
    <xxx></xxx>
  </div>
  <script type="text/javascript">
    Vue.use(Vuex); // 使用vuex
    var myStore = new Vuex.Store({
      // state是儲存狀態 定義應用狀態全域性的資料結構
      state: {
        name: 'kongzhi',
        todoLists: []
      },
      /*
        mutations是提交狀態修改,也就是set、get中的set,這是vuex中唯一修改state的方式,但是不支援非同步操作。
        每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)
        第一個引數預設是state,外部呼叫方式為:store.commit('SET_AGE', 30).
      */
      mutations: {
        // 新增list
        ADDLIST(state, item) {
          state.todoLists.push(item);
        },
        // 刪除list中的項
        DELLIST(state, index) {
          state.todoLists.splice(index, 1);
        },
        // 設定 錯誤提示資訊
        SETERROR(state, msg) {
          state.message = msg;
        }
      },
      /*
        getters是從state中派生出狀態的。也就是set、get中的get,它有兩個可選的引數,state和getters,
        分別可以獲取state中的變數和其他的getters。外部呼叫的方式:store.getters.todoCount()
      */
      getters: {
        todoCount(state) {
          return state.todoLists.length;
        }
      },
      /*
       和上面的mutations類似,但是actions支援非同步操作的,外部呼叫方式為:store.dispatch('nameAction')
       常見的使用是:從伺服器端獲取資料,在資料獲取完成後會呼叫 store.commit()來更改Store中的狀態。
       Action函式接收一個與store實列具有相同方法和屬性的context物件,因此我們可以使用 context.commit 提交一個
       mutation,或者通過 context.state 和 context.getters來獲取state和getters
      */
      actions: {
        addList(context, item) {
          if (item) {
            context.commit('ADDLIST', item);
            context.commit('SETERROR', '');
          } else {
            context.commit('SETERROR', '新增失敗');
          }
        },
        delList(context, index) {
          context.commit('DELLIST', index);
          context.commit('SETERROR', '刪除成功');
        }
      },
      /*
       modules 物件允許將單一的Store拆分為多個Store的同時儲存在單一的狀態樹中。
      */
      modules: {

      }
    });
    // 註冊vue元件 xxx
    Vue.component('xxx', {
      template: "<div>{{name}}, 總數:{{todoCount}}</div>",
      computed: {
        name: function() {
          console.log(this.$store.state);
          return this.$store.state.name;
        },
        todoCount: function() {
          return this.$store.getters.todoCount;
        }
      },
      mounted: function() {
        console.log(this);
      },

    });
    new Vue({
      el: '#app',
      data: {
        name: 'init name'
      },
      store: myStore,
      mounted: function() {
        console.log(this);
      }
    })
  </script>
</body>
</html>

執行效果 如下圖所示

檢視demo3.html

5. mutations操作來改變state資料
如上是如何獲取state的資料了,那麼現在我們使用mutations來改變state的資料了,在Vuex中,改變狀態state的唯一方式是通過提交commit的一個mutations,mutations下的對應函式接收第一個引數state,第二個引數為payload(載荷),payload一般是一個物件,用來記錄開發時使用該函式的一些資訊。mutations是處理同步的請求。不能處理非同步請求的。看如下demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
  <script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
  <div id='app'>
    <xxx></xxx>
  </div>
  <script type="text/javascript">
    Vue.use(Vuex); // 使用vuex
    var myStore = new Vuex.Store({
      // state是儲存狀態 定義應用狀態全域性的資料結構
      state: {
        name: 'kongzhi',
        todoLists: []
      },
      /*
        mutations是提交狀態修改,也就是set、get中的set,這是vuex中唯一修改state的方式,但是不支援非同步操作。
        每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)
        第一個引數預設是state,外部呼叫方式為:store.commit('SET_AGE', 30).
      */
      mutations: {
        // 新增list
        ADDLIST(state, item) {
          console.log(state.todoLists);
          state.todoLists.push(item);
        },
        // 刪除list中的項
        DELLIST(state, index) {
          state.todoLists.splice(index, 1);
        },
        // 設定 錯誤提示資訊
        SETERROR(state, msg) {
          state.message = msg;
        }
      },
      /*
        getters是從state中派生出狀態的。也就是set、get中的get,它有兩個可選的引數,state和getters,
        分別可以獲取state中的變數和其他的getters。外部呼叫的方式:store.getters.todoCount()
      */
      getters: {
        todoCount(state) {
          return state.todoLists.length;
        }
      },
      /*
       和上面的mutations類似,但是actions支援非同步操作的,外部呼叫方式為:store.dispatch('nameAction')
       常見的使用是:從伺服器端獲取資料,在資料獲取完成後會呼叫 store.commit()來更改Store中的狀態。
       Action函式接收一個與store實列具有相同方法和屬性的context物件,因此我們可以使用 context.commit 提交一個
       mutation,或者通過 context.state 和 context.getters來獲取state和getters
      */
      actions: {
        addList(context, item) {
          if (item) {
            context.commit('ADDLIST', item);
            context.commit('SETERROR', '');
          } else {
            context.commit('SETERROR', '新增失敗');
          }
        },
        delList(context, index) {
          context.commit('DELLIST', index);
          context.commit('SETERROR', '刪除成功');
        }
      },
      /*
       modules 物件允許將單一的Store拆分為多個Store的同時儲存在單一的狀態樹中。
      */
      modules: {

      }
    });
    // 註冊vue元件 xxx
    Vue.component('xxx', {
      template: "<div>{{name}}, 總數:{{todoCount}} <span @click='addList'>點選改變總數</span></div>",
      computed: {
        name: function() {
          return this.$store.state.name;
        },
        todoCount: function() {
          return this.$store.getters.todoCount;
        }
      },
      mounted: function() {
        console.log(this);
      },
      data: function() {
        return {
          count: 0
        }
      },
      methods: {
        addList: function() {
          var count = this.count++;
          this.$store.commit('ADDLIST', count);
        }
      }
    });
    new Vue({
      el: '#app',
      data: {
        name: 'init name'
      },
      store: myStore,
      mounted: function() {
        console.log(this);
      }
    })
  </script>
</body>
</html>

如上程式碼,在元件xxx中的template新增程式碼 <span @click='addList'>點選改變總數</span>,每次點選的時候,會呼叫自身內部的addList的函式,然後count自增1,然後會繼續呼叫 this.$store.commit('ADDLIST', count); 來提交 commit的一個mutations,因此state.todoLists.push(item);會增加一項資料後,在元件xxx內部通過 todoCount 可以實時獲取到資料的改變狀態。

檢視demo4.html

6. actions操作mutations非同步來改變state資料
actions可以非同步操作,actions提交mutations,通過mutations來提交資料的變更。它是非同步修改state的狀態的。
外部呼叫方式是 this.$store.dispatch('nameAsyn');

下面我們看如下demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
  <script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
  <div id='app'>
    <xxx></xxx>
  </div>
  <script type="text/javascript">
    Vue.use(Vuex); // 使用vuex
    var myStore = new Vuex.Store({
      // state是儲存狀態 定義應用狀態全域性的資料結構
      state: {
        name: 'kongzhi',
        todoLists: [],
        // 新增count欄位
        count: 1
      },
      /*
        mutations是提交狀態修改,也就是set、get中的set,這是vuex中唯一修改state的方式,但是不支援非同步操作。
        每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)
        第一個引數預設是state,外部呼叫方式為:store.commit('SET_AGE', 30).
      */
      mutations: {
        // 新增list
        ADDLIST(state, item) {
          console.log(state.todoLists);
          state.todoLists.push(item);
        },
        // 刪除list中的項
        DELLIST(state, index) {
          state.todoLists.splice(index, 1);
        },
        // 設定 錯誤提示資訊
        SETERROR(state, msg) {
          state.message = msg;
        },
        // 非同步操作count
        COUNTASYNC(state, param) {
          console.log(state.count += param);
        }
      },
      /*
        getters是從state中派生出狀態的。也就是set、get中的get,它有兩個可選的引數,state和getters,
        分別可以獲取state中的變數和其他的getters。外部呼叫的方式:store.getters.todoCount()
      */
      getters: {
        todoCount(state) {
          return state.todoLists.length;
        }
      },
      /*
       和上面的mutations類似,但是actions支援非同步操作的,外部呼叫方式為:store.dispatch('nameAction')
       常見的使用是:從伺服器端獲取資料,在資料獲取完成後會呼叫 store.commit()來更改Store中的狀態。
       Action函式接收一個與store實列具有相同方法和屬性的context物件,因此我們可以使用 context.commit 提交一個
       mutation,或者通過 context.state 和 context.getters來獲取state和getters
      */
      actions: {
        addList(context, item) {
          if (item) {
            context.commit('ADDLIST', item);
            context.commit('SETERROR', '');
          } else {
            context.commit('SETERROR', '新增失敗');
          }
        },
        delList(context, index) {
          context.commit('DELLIST', index);
          context.commit('SETERROR', '刪除成功');
        },
        // 設定延時來演示一下非同步提交
        addFunc(context, value) {
          console.log(context);
          setTimeout(function(){
            // 非同步提交 actions操作Mutations
            context.commit('COUNTASYNC', value);
          }, 100);
        }
      },
      /*
       modules 物件允許將單一的Store拆分為多個Store的同時儲存在單一的狀態樹中。
      */
      modules: {

      }
    });
    // 註冊vue元件 xxx
    Vue.component('xxx', {
      template: 
        `<div>
          {{name}}, 總數:{{todoCount}} 
          <span @click='addList'>點選改變總數</span>
          <button @click='addFuncAnsyc'>點選我actions改變資料</button>
        </div>`,
      computed: {
        name: function() {
          return this.$store.state.name;
        },
        todoCount: function() {
          return this.$store.getters.todoCount;
        }
      },
      mounted: function() {
        console.log(this);
      },
      data: function() {
        return {
          count: 0
        }
      },
      methods: {
        addList: function() {
          var count = this.count++;
          this.$store.commit('ADDLIST', count);
        },
        addFuncAnsyc: function() {
          this.$store.dispatch('addFunc', 1);
        }
      }
    });
    new Vue({
      el: '#app',
      data: {
        name: 'init name'
      },
      store: myStore,
      mounted: function() {
        console.log(this);
      }
    })
  </script>
</body>
</html>

如上程式碼,template中新增程式碼 <button @click='addFuncAnsyc'>點選我actions改變資料</button>,點選後,觸發內部函式 addFuncAnsyc後,會呼叫 this.$store.dispatch('addFunc', 1);呼叫來操作actions中的方法,第一個引數對應actions中的方法名,第二個是引數值,在actions中的函式 addFunc 列印出 context, 如下圖所示:


addFunc函式會呼叫 context.commit('COUNTASYNC', value); 程式碼,會找到 mutations中對應的COUNTASYNC,然後會對state資料進行更改。

理解context:  context是和 this.$store 具有相同的方法和屬性的物件。我們可以通過 context.state 和 context.getters來獲取state和getters。

理解dispatch: 它含有非同步操作,含義可以理解為 '派發',比如向後臺提交資料,可以為 this.$store.dispatch('actions方法名', 值);

檢視demo5.html

三:webpack4+vue+route+vuex 專案架構

 如上程式碼是基本的demo,但是在專案中,我們是如何配置的呢?下面再來看看使用store來重構專案,整個目錄架構如下:

### 目錄結構如下:
demo1                                       # 工程名
|   |--- dist                               # 打包後生成的目錄檔案             
|   |--- node_modules                       # 所有的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放所有vue頁面檔案
|   | | | |-- parent.vue                    # 父元件
|   | | | |-- child.vue                     # 子元件
|   | | | |-- index.vue
|   | | |-- components                      # 存放vue公用的元件
|   | | |-- js                              # 存放js檔案的
|   | | |-- store                           # store倉庫
|   | | | |--- actions.js
|   | | | |--- mutations.js
|   | | | |--- state.js
|   | | | |--- mutations-types.js
|   | | | |--- index.js
|   | | |-- app.js                          # vue入口配置檔案
|   | | |-- router.js                       # 路由配置檔案
|   |--- views
|   | |-- index.html                        # html檔案
|   |--- webpack.config.js                  # webpack配置檔案 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉碼檔案

在app/index/views 下新建 testvue.vue, 程式碼如下:

<template>
  <div>
    {{name}}, 總數:{{todoCount}} 
    <span @click='addList'>點選改變總數</span>
    <button @click='addFuncAnsyc'>點選我actions改變資料</button>
  </div>
</template>

<script type="text/javascript">
  export default {
    data() {
      return {
        name: '我是空智',
        count: 1,
        todoLists: []
      }
    },
    created() {
      
    },
    computed: {
      todoCount: function() {
        return this.$store.state.add;
      }
    },
    methods: {
      addList() {
        var count = this.count++;
        this.$store.commit('ADD', count);
      },
      addFuncAnsyc() {
        const obj = {
          
        };
        Promise.all([this.$store.dispatch('commonActionGet', ['getPower:COUNTASYNC', obj])]).then((res) => {

        });
      }
    }
  }
</script>

模板程式碼中 <span @click='addList'>點選改變總數</span> 點選一下 觸發 addList 函式,該函式會呼叫 this.$store.commit('ADD', count); 該方法,commit方法是同步的,它會尋找mutations.js對應的 'ADD'函式.
因此,app/index/store/mutations-types.js 程式碼可以改成如下:

// 新增list
export const ADD = 'ADD'; 

// 設定錯誤提示
export const SETERROR = 'SETERROR';

// 非同步操作count
export const COUNTASYNC = 'COUNTASYNC';

app/index/store/mutations.js 程式碼改造成如下:

import * as types from './mutations-types';

export default {
  [types.ADD] (state, payload) {
    state.add = payload;
  },
  [types.SETERROR] (state, payload) {
    state.errors = payload;
  },

  [types.COUNTASYNC] (state, payload) {
    state.counts = payload;
  }
}

app/index/store/state.js 改造程式碼如下:

export default {
  add: 0,
  errors: '',
  counts: 0
};

app/index/store/actions.js 改造程式碼如下, 封裝了常見的ajax中的get和POST方法:

import * as types from './mutations-types';
import Vue from 'vue';
const ajaxHandle = () => {
  const buildUrl = (url, params, childParams) => {
    let str = '?'
    for (const key in params) {
      url += str + key + '=' + params[key];
      str = '&';
    }
    if (childParams) {
      return url + '/' + childParams;
    }
    return url;
  };
  const ajaxGet = (url, fn) => {
    let results = null;
    Vue.http.get(url, { emulateJSON: true, credentials: true }).then((response) => {
      if (response.ok) {
        results = response.body;
        fn && fn(1, results);
      } else {
        fn && fn(0, results);
      }
    }, (error) => {
      if (error) {
        fn && fn(0, results);
      }
    });
  };
  const ajaxGetJSON = (url, fn) => {
    let results = null;
    Vue.http.get(url, { credentials: true }).then((response) => {
      if (response.ok) {
        results = response.body;
        fn && fn(1, results);
      } else {
        fn && fn(0, results);
      }
    }, (error) => {
      if (error) {
        fn && fn(0, results);
      }
    });
  };
  const ajaxPost = (url, params, options, fn) => {
    let results = null;
    if (typeof options === 'function' && arguments.length <= 3) {
      fn = options;
      options = {};
    }
    Vue.http.interceptors.push((request, next) => {
      request.credentials = true;
      next();
    });
    Vue.http.post(url, params, { emulateJSON: true }).then((response) => {
      if (response.ok) {
        results = response.body;
        fn && fn(1, results);
      } else {
        fn && fn(0, results);
      }
    }, (error) => {
      if (error) {
        fn && fn(0, results);
      }
    })
  };
  const ajaxPostJSON = (url, params, options, fn) => {
    let results = null;
    if (typeof options === 'function' && arguments.length <= 3) {
      fn = options;
      options = {};
    }
    Vue.http.interceptors.push((request, next) => {
      request.credentials = true;
      next();
    });
    Vue.http.post(url, params).then((response) => {
      if (response.ok) {
        results = response.body;
        fn && fn(1, results);
      } else {
        fn && fn(0, results);
      }
    }, (error) => {
      if (error) {
        fn && fn(0, results);
      }
    })
  };
  return {
    buildUrl: buildUrl,
    ajaxGet: ajaxGet,
    ajaxGetJSON: ajaxGetJSON,
    ajaxPost: ajaxPost,
    ajaxPostJSON: ajaxPostJSON
  }
};

const ah = ajaxHandle();
const prefix = '//xxx.abc.com';

const apiObj = {
  API_GET_POWER: prefix + '/xxxx/yyy', // 獲取使用者資訊
};

const apiFuncObj = {
  getPower: 'API_GET_POWER', // 獲取使用者資訊
};

export default {
  commonActionPost ({ commit }, payload) {
    let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
    const mutationsName = payload[0].split(':')[1];

    const params = payload[1] ? payload[1] : {};
    return new Promise((reslove, reject) => {
      ah.ajaxPost(url, params, (state, results) => {
        if (state) {
          reslove(results);
        } else {
          reject();
        }
        if (mutationsName) {
          commit(mutationsName, results);
        }
      });
    });
  },
  commonActionPostJSON({ commit }, payload) {
    let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
    const mutationsName = payload[0].split(':')[1];

    const params = payload[1] ? payload[1] : {};
    return new Promise((reslove, reject) => {
      ah.ajaxPostJSON(url, params, (state, results) => {
        if (state) {
          reslove(results);
        } else {
          reject();
        }
        if (mutationsName) {
          commit(mutationsName, results);
        }
      });
    });
  },
  commonActionGet ({ commit }, payload) {
    let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
    const mutationsName = payload[0].split(':')[1];

    const params = payload[1] ? payload[1] : {};
    const childParams = payload[2] ? payload[2] : '';
    url = ah.buildUrl(url, params, childParams);
    return new Promise((reslove, reject) => {
      ah.ajaxGet(url, (state, results) => {
        if (state) {
          reslove(results);
        } else {
          reject();
        }
        if (mutationsName) {
          commit(mutationsName, results);
        }
      });
    });
  },
  commonActionGetJSON ({ commit }, payload) {
    let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
    const mutationsName = payload[0].split(':')[1];

    const params = payload[1] ? payload[1] : {};
    url = ah.buildUrl(url, params);
    return new Promise((reslove, reject) => {
      ah.ajaxGetJSON(url, (state, results) => {
        if (state) {
          reslove(results);
        } else {
          reject();
        }
        if (mutationsName) {
          commit(mutationsName, results);
        }
      });
    });
  }
}

app/index/store/index.js 改造程式碼如下

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

import state from './state';
import mutations from './mutations';
import actions from './actions';

Vue.use(Vuex);

Vue.config.devtools = true;

export default new Vuex.Store({
  state,
  mutations,
  actions
});

然後在我們入口檔案 app/index/app.js 引入store即可,如下程式碼:

import Vue from 'vue';

// 新增store
import store from './store/index';

// 暫時先用 vue-resource 演示
import VueResource from 'vue-resource';

import Index from './views/index';

// 引入路由
import router from './router';

// Resource
Vue.use(VueResource);

new Vue({
  el: '#app',
  router: router,
  // vue實列新增store
  store: store,
  render: h => h(Index)
});

檢視github原始碼

相關文章