Vuex實(shí)現(xiàn)組件通信-Vue學(xué)習(xí)筆記(2).md

2017.4.17-4.21

前言

本周繼續(xù)上周的登錄界面,使用less與vuex進(jìn)行完善。

筆記導(dǎo)航

  • Vue套件使用
  • Vuex狀態(tài)管理學(xué)習(xí)( state & getters & mutations & actions
  • 通過Vuex實(shí)現(xiàn)多個(gè)組件間的通信
  • 使用less編輯頁面樣式

詳細(xì)筆記

1. Vue組件嵌套使用

新建父組件,在script段落內(nèi)引入子組件,并且在父組件中插入已定義的子組件

<!-- 組件嵌套實(shí)例 -->
<!-- 父組件Entry.Vue  -->
<!-- 子組件Login.Vue & Register.Vue -->

<template>
  <div class="entry-box">
    <h1>entry</h1>
    <login></login>
    <register></register>
  </div>
</template>

<script>
// 聲明引入子組件變量
import login from './Login'
import register from './Register'
export default {
  name: 'entry',
  // ******* 在components參數(shù)中賦值,添加引入的子組件  *******
  components: {
    login,
    register
  }
  // ******* 在components參數(shù)中賦值,添加引入的子組件  *******
}
</script>
Vue語法縮寫:

@ === v-on 事件綁定監(jiān)聽

<!-- 完整語法 -->
<a v-on:click="doSomething"></a>
<!-- 縮寫 -->
<a @click="doSomething"></a>   

: === v-bind 元素綁定

<!-- 完整語法 -->
<a v-bind:href="url"></a>
<!-- 縮寫 -->
<a :href="url"></a>

2. 使用Vuex

Vuex的用處:

通過內(nèi)部變量(store實(shí)例中的state)來管理整個(gè)系統(tǒng)的狀態(tài),提供多種系統(tǒng)的接口(getters、mutations、actions),使Vue組件可以通過store實(shí)例的接口獲取store實(shí)例中的state變量。

Vuex的好處:

使用state: 實(shí)現(xiàn)Vue組件中的通信
使用getters與mutations: 不需要在每一個(gè)Vue組件中重寫相同的處理函數(shù)(比如用于獲取state中的參數(shù))
使用actions: 把Vue之間的同步調(diào)用轉(zhuǎn)變成異步調(diào)用,提高響應(yīng)效率

Vuex學(xué)習(xí)資源:
Vuex入門視頻(共5個(gè))
Vuex視頻對應(yīng)的練習(xí)源碼

上面推薦的入門視頻中對于Vuex的一個(gè)模型圖示,看完視頻再看會比較清晰
2.1 關(guān)于Store & State

Step1: 安裝Vuex

npm install --save vuex

Step2: 聲明store實(shí)例,并引用到application中
// 新建store.js文件  
// store.js 配置Vuex實(shí)例
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(vuex)
export const store = new Vuex.Store({
  state: { 
    //填充用于管理狀態(tài)的共享變量
  }
})


//在main.js中引入Store實(shí)例
// main.js
// 使用{}引入store實(shí)例,因?yàn)閟tore是一個(gè)const變量
import { store } from './store'

new Vue({
  router,
  store,
  // 其他屬性 ...
}).$mount('#app')
Step3: 傳遞Store實(shí)例中的state參數(shù)中的共享變量

在使用store之前,需要在App.vue中將公共狀態(tài)傳遞給子組件(通過props獲取)。
相當(dāng)于子組件中傳入的數(shù)據(jù)只是數(shù)據(jù),而非數(shù)據(jù)地址,不可修改數(shù)據(jù)本身,因此自身操作不會影響全局,需要拋出事件給父組件。
而使用js操作變量時(shí),子組件需要通過emit-on機(jī)制向App.vue發(fā)起事件冒泡,由app.vue執(zhí)行操作并改變內(nèi)部的data,進(jìn)而更新子組件中的參數(shù)變化。

// App.vue
<template>
  <div id="app">
    // 通過綁定:users="unregisteredUsers" 向app-registration模塊中傳遞props值
    <app-registration @userRegistered="userRegistered" :users="unregisteredUsers"></app-registration>
    <app-registrations @userUnregistered="userUnregistered" :registrations="registrations"></app-registrations>
  </div>
</template>

<script>
  import Registration from './components/Registration.vue';
  import Registrations from './components/Registrations.vue';

export default {
    data() {        // data中存儲被傳到兩個(gè)組件中的變量
      return {
        registrations: [],
        users: [
            {id: 1, name: 'Max', registered: false},
            {id: 2, name: 'Anna', registered: false},
            {id: 3, name: 'Chris', registered: false},
            {id: 4, name: 'Sven', registered: false}
        ]
      }
    }
    computed: {
        unregisteredUsers() {
            return this.users.filter((user) => {
                return !user.registered;
            });
        }
    },
    // 聲明引用的子組件
    components: {
        appRegistration: Registration,
        appRegistrations: Registrations
    }
}
</script>

在使用store之后,不需要再在子組件中通過props獲取傳入?yún)?shù),可以直接通過this.$store.state.[變量名稱]直接對store實(shí)例中的state參數(shù)進(jìn)行有效編輯。
原本在app.vue中進(jìn)行的操作,可以在子組件中直接執(zhí)行。

// Registration.vue
<template>
    <div id="registration">
        <h3>Register here</h3>
        <hr>
        <div class="row" v-for="user in users">
            <h4>{{ user.name }}</h4>
            <button @click="registerUser(user)">Register</button>
        </div>
    </div>
</template>

<script>
    export default {
      computed: {
        // 通過user() 獲取store中的公共共享參數(shù)
        users() {
         return this.$store.state.users.filter(user => {     
            return !user.registered;    // filter:條件過濾,只返回user.registered===false的元素
          })
        }
      },
        methods: {
            registerUser(user) {
              user.registered = true;
              const date = new Date;
              this.$store.state.registrations.push({userId: user.id, name: user.name, date: date.getMonth() + '/' + date.getDay()})
            }
        }
    }
</script>
2.2 關(guān)于getters與mutations

概念:這一對Vuex實(shí)例屬性,在一般情況下被理解作getter與setteraccessor和mutator

getters [new store property]

getters用于簡化子組件從store.js中獲取公共變量的代碼

使用getters優(yōu)化前:
// store.js 
export const store = new Vuex.Store({
  state: {
    registrations: [],
    users: [
        {id: 1, name: 'Max', registered: false},
        {id: 2, name: 'Anna', registered: false},
        {id: 3, name: 'Chris', registered: false},
        {id: 4, name: 'Sven', registered: false}
    ]
  }
})

// 子組件 Registration.vue
export default {
      computed: {
        users() {
          return this.$store.state.users.filter(user => {
            return !user.registered;
          })
        }
      }
}

使用getters優(yōu)化后:
// store.js
export const store = new Vuex.Store({
  state: {
    registrations: [],
    users: [
        {id: 1, name: 'Max', registered: false},
        {id: 2, name: 'Anna', registered: false},
        {id: 3, name: 'Chris', registered: false},
        {id: 4, name: 'Sven', registered: false}
    ]
  },
  getters: {
    unregisteredUsers(state) {
       return state.users.filter(user => {
         return !user.registered;
       })
    },
    registerUser(state) {
      return state.registrations;
    }
    totalRegistration(state) {
      return state.registrations.length;
    }
  }
})

// 子組件 Registration.vue
export default {
      computed: {
        users() {
          // 以屬性的形式調(diào)用getters中的方法,不需要傳入?yún)?shù)
          return this.$store.getters.unregisteredUsers;
          }
      }
}

使用mapGetters的二度簡化

安裝mapGetters的編譯工具
npm install --save-dev babel-preset-stage-2
配置mapGetters的編譯參數(shù)

// .babelrc 文件
{
  "presets": [
    ["es2015", { "modules": false }],
    ["stage-2"]    //新增參數(shù)
  ]
}
// 子組件 Registrations.vue
<script>
// 引入mapGetters
import { mapGetters } from 'vuex'
    export default {
      computed: mapGetters({
        // getters映射的語法規(guī)則:
        // 'customized name' : 'name of getters'
        registrations: 'registeredUser',
        total: 'totalRegistrations'
      }),
        methods: {
            unregister(registration) {
              const user = this.$store.state.users.find(user => {
                  return user.id == registration.userId;
              });
              user.registered = false;
              this.$store.state.registrations.splice(this.$store.state.registrations.indexOf(registration), 1);
            }
        }
    }
</script>

mutations [new store property]

getters用于管理可復(fù)用的'獲取state參數(shù)的操作';相應(yīng)地,mutations用于管理可復(fù)用的'修改state參數(shù)的js操作'。

// mutations 的定義與聲明
// store.js
export const store = new Vuex.Store({
  state: { ··· },
  getters: { ··· },
  mutations: {
    register(state, userId) {
      const user = state.users.find(user => {
          return user.id == userId;
      });
      user.registered = true;
      const date = new Date;
      const registration = {
        userId: userId,
        name: user.name,
        date: date.getMonth() + '/' + date.getDay()
      }
      state.registrations.push(registration);
    },
    unregister(state, userId) {
      const user = state.users.find(user => {
          return user.id == userId;
      });
      user.registered = false;
      // 使用findIndex定位目標(biāo)刪除元素
      const registrationIndex = state.registrations.findIndex(registration => {
        return registration.userId == userId;
      })
      state.registrations.splice(registrationIndex, 1);

      // 使用find函數(shù)定位目標(biāo)刪除元素
      // const registration = state.registrations.find(registration => {
      //   return registration.userId == userId;
      // })
      // state.registrations.splice(state.registrations.indexOf(registration), 1);
    }
  }
})

// mutations在組件中的調(diào)用
// Registration.vue
<script>
    export default {
      computed: {
        users() {
          return this.$store.getters.unregisteredUsers;
          }
      },
      methods: {
            registerUser(user) {
              // 與getters不同,在子組件中使用commit調(diào)用mutations中的函數(shù)
              this.$store.commit('register', user.id);
        }
      }
    }
</script>

2.3 關(guān)于actions

在nodejs中,我們認(rèn)識到最深刻的一點(diǎn)就是異步調(diào)用。但是在Vuex中,mutations屬性中包含了大量函數(shù)接口,并且具有同步執(zhí)行的特點(diǎn)。因此在必須在執(zhí)行完mutations中某個(gè)被調(diào)用的函數(shù)之后,才能繼續(xù)調(diào)用下一個(gè),效率大大降低。
這個(gè)問題,就是actions屬性所要解決的問題 ==> 異步調(diào)用(Async)

// store.js 
// 聲明actions, 可以在其中的函數(shù)中加入異步代碼
export const store = new Vuex.Store({
  state: { ··· },
  getters: { ··· },
  mutations: {
    register(state, userId) {
      ······
    },
    unregister(state, userId) {
      ······
    }
  },
  actions: {
    // actions中的函數(shù)名可自定義,此處為了方便練習(xí)與mutations中函數(shù)同名
    // 寫法一:
    register(context, userId) {
      context.commit('register', userId);
    },
    // 寫法二:
    unregister( { commit } , userId) {
      commit('unregister', userId);
    }
  }
})


// 在子組件中使用dispatch,調(diào)用可異步執(zhí)行的actions 
// Registrations.vue
<script>
import { mapGetters } from 'vuex'
    export default {
      computed: mapGetters({
        registrations: 'registeredUser',
        total: 'totalRegistrations'
      }),
      methods: {
            unregister(registration) {
              // 調(diào)用不可異步的mutations的寫法,傳入的第一個(gè)參數(shù)是actions中的函數(shù)名
              // this.$store.commit('unregister', registration.userId);

              // 調(diào)用可異步的actions的寫法
              this.$store.dispatch('unregister', registration.userId);
            }
        }
    }
</script>

2.3 關(guān)于store.js的合理分裝

store.js中包含了getters、mutations以及actions,隨著application的功能擴(kuò)展,函數(shù)將會越來越多,因此把這三個(gè)屬性分裝出去,是十分有必要的。
在src目錄下新增store文件夾,更新后當(dāng)前目錄如下:

.
├── build/                      
├── config/
├── node_modules
├── src/
│   ├── main.js                 
│   ├── App.vue       
│   ├── store/  
│   │   ├── store.js
│   │   ├── getters.js  
│   │   ├── mutations.js
│   │   └── actions.js
│   ├── components/           
│   │   └── ...
│   └── assets/                 
│       └── ...
├── .babelrc                   
├── .postcssrc.js               
├── .eslintrc.js               
├── .editorconfig           
├── index.html                 
└── package.json            

分裝方法如下:

// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'


Vue.use(Vuex)

export const store = new Vuex.Store({
  state: {
    registrations: [],
    users: [
        {id: 1, name: 'Max', registered: false},
        {id: 2, name: 'Anna', registered: false},
        {id: 3, name: 'Chris', registered: false},
        {id: 4, name: 'Sven', registered: false}
    ]
  },
  getters,
  mutations,
  actions
})


// getters.js
export default {
  unregisteredUsers(state) {
     return state.users.filter(user => {
       return !user.registered;
     })
  },
  registeredUser(state) {
    return state.registrations;
  },
  totalRegistrations(state) {
    return state.registrations.length;
  }
}

// mutations.js 與 actions.js同getters的做法

3.使用less修改Vue界面

首先:安裝less依賴:
npm install less less-loader --save
然后:修改webpack.base.conf.js文件,配置loader加載依賴,讓其支持外部的less。

在modules.rules屬性中添加一個(gè)新的對象

{
  test: /\.less$/,
  loader: "style-loader!css-loader!less-loader",
}
最后:在Vue組件中使用的時(shí)候在style標(biāo)簽里加上lang="less"
less語法

美出天際的登錄界面模板

4. Vue組件布局與路由

1. App.vue是整個(gè)application的主容器,根據(jù)不同的路由放入不同的組件。

即使當(dāng)前Vue中沒有html標(biāo)簽,也同樣可以設(shè)置html、body標(biāo)簽的屬性(width:100%)。

URL中的#號作用——定位頁面元素

2. css樣式筆記

提高樣式優(yōu)先級
在樣式的后面添加"!important"

width: 40% !important;

input標(biāo)簽與button標(biāo)簽在同一行內(nèi)頂部對齊: vertical-align: baseline;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容