Vue 初接觸實戰之帳單組件

Vue做爲一個構建數據驅動web界面的庫,是去年最火的MVVM風格庫之一。Vue的用起來有Angular的影子,把不少自定義指令注入html,又吸取了React的優勢和精華。好比與Vue的配套使用的Vuex就是從Redux和Flux中借鑑了很多思路。css

Vue從去年的下半年開始火起來,目前在Awesomes前端資源庫的熱度排行裏面處於Top2的位置。得益於更加簡潔的語法和易用性,Vue目前在社區的受歡迎度不在React之下。不少公司紛紛上手Vue了,我廠的前端團隊也已經在使用Vue進行業務重構了。即將發佈的Vue2.0版本也採用了和React同樣的Virtual Dom技術,並且推出了先後端同構的服務端渲染框架vue-hackernews-2.0,這將會使得Vue在技術社區更加受追捧。目前Vue2.0的RC文檔也已經出來了,能夠預期,它的使用前景將會很是很是不錯。html

這裏要講的是啥?

這篇文章不是普及Vue的內部實現原理,也不是要比較MVVM三大法器Angular、React和Vue的優劣。這裏主要是對本身最近上手學習Vue,進行SPA開發過程當中的思路總結。但願經過展現一個分期帳單組件,能夠對像我同樣的Vue初學者提供一丁點的幫助。由於是Vue新手,若有錯誤之處,也歡迎各位大神批評指正。前端

Vuex

首先要介紹的是Vuex這個神器。關於Vue的基本語法,能夠直接打開官方1.0文檔學習。固然,若是你想直接上手Vue2.0,也能夠直接訪問這裏vue

Vuex 是一個專門爲 Vue.js 應用所設計的集中式狀態管理架構。它借鑑了 Flux 和 Redux 的設計思想,但簡化了概念,而且採用了一種爲能更好發揮 Vue.js 數據響應機制而專門設計的實現。node

也就是說,Vuex將父組件與子組件之間的props傳遞,組件與組件之間的消息傳遞集中起來管理。在一個小型應用中,咱們可能不會用到Vuex,這樣會把本來很簡單的任務複雜化了。可是,企業級的項目都是多條業務線交叉的,若是單純使用Vue自己的組件通訊,業務組件之間複雜的關係網會讓項目後期的調試和Bug跟蹤很是困難,尤爲是當你在構建一個SPA項目的時候。webpack

爲了更好的解決在大型應用中狀態的共用問題,咱們須要對組件的 組件本地狀態(component local state) 和 應用層級狀態(application level state) 進行區分。應用級的狀態不屬於任何特定的組件,但每個組件仍然能夠監視(Observe)其變化從而響應式地更新 DOM。經過彙總應用的狀態管理於一處,咱們就沒必要處處傳遞事件。git

Vuex的數據流模型以下圖所示:github

vuex數據流模型

  1. 用戶在組件中的輸入操做觸發 action 調用;web

  2. Actions 經過分發 mutations 來修改 store 實例的狀態;vue-router

  3. Store 實例的狀態變化反過來又經過 getters 被組件獲知,組件獲悉狀態變動以後就是數據驅動的魔法——實時更新DOM狀態。

須要注意的一點是,mutation自己是一個事件系統,經過定義事件來觸發Store的狀態變動。mutation裏面定義的函數必須是同步函數,涉及到API調用的邏輯要放到Action進行,由於Action是能夠定義異步函數的。

Vue-route

介紹完Vuex,咱們來講說vue-router。vue-router是Vue官方提供的路由插件,經過vue-router配合Vuex,咱們能夠很是高效地開發大型SPA。vue-router最基本的做用是作SPA路由映射。

router.map({
    '/foo': {
        component: Foo
    },
    '/bar': {
        component: Bar
    }
})

上面的配置中,當訪問路徑"/foo"的時候,SPA就會在<router-view>掛載組件Foo,改變訪問路徑爲 "/bar",Bar組件就會切換到主視口。這就是一個最基本的SAP路由配置。

一步一步使用Vue構建S一個PA帳單組件

在簡單瞭解了Vuex和Vue-router的基本概念以後,咱們能夠進入實踐環節。若是尚未徹底理解清楚Vue的語法和Vuex數據流的概念,能夠繼續多看幾回官方文檔,尤爲是對剛接觸MVVM的人來講,可能要多看幾回才能對數據驅動的編程理念有更好的理解。

一、項目目錄

|-vue-demo
    |-node_modules

    |-order
        |-app
            |-components
                |-All.vue

                |-Latest.vue

                |-Nav.vue

                |-Order.vue
            |-vuex
                |-modules
                    |-orderList.js
                |-action.js

                |-mutation.js

                |-mutation-type.js

                |-store.js
            |-App.vue

            |-main.js

            |-router.js
        |-index.html

        |-webpack.config.js
    |-package.json

    |-README.md

這個是比較推薦的項目目錄結構。其中order是咱們要展現的帳單組件的根目錄,組件目錄劃分爲入口index.html、webpack配置文件和app文件夾。在app目錄內:

  • App.vue - 組件的根節點;

  • main.js - 組件入口;

  • router.js - 路由相關配置;

  • component - 組件根節點下的各個子組件;

  • vuex - 顧名思義,數據流架構vuex相關的文件,包括action、mutation和store。

在大型項目中,組件數量多,涉及的數據流是比較複雜的。爲了更好地管理store,咱們又把store定義在不一樣的模塊中,好比modules目錄下的orderList就是帳單列表相關的數據流。事件類型比較多的狀況下,咱們把事件名稱定義在mutation-type中。

二、建立組件入口

組件入口須要作的事情包括建立Vue組件實例、掛載插件、配置路由,main.js以下:

import Vue from 'vue'
import VueRouter from 'vue-router'
import ConfigRouter from './router'
import App from './App.vue'
// import './style/order.css'
Vue.config.devtools = true

Vue.use(VueRouter)

// 建立vue-router實例
var router = new VueRouter()
//配置路由
ConfigRouter(router)

// 滾動到頁面頂部
router.beforeEach(function() {
  window.scrollTo(0, 0)
})

//全路徑匹配,防止出現404
router.redirect({
  '*': '/'
})
//啓動APP
router.start(App, '#root')

三、使用vue-router配置SPA路由

帳單組件的路由包括「最近7天待還」以及「所有待還」,router.js:

export default function(router) {
  router.map({
    '/': {
      component: require("./components/Latest.vue"),
      linkActiveClass: 'active'
    },
    '/latest': {
      component: require("./components/Latest.vue"),
      linkActiveClass: 'active'
    },
    '/all': {
      component: require("./components/All.vue"),
      linkActiveClass: 'active'
    }
  })
}

四、建立 Vuex Store

嘗試列出組件用到的全部數據,在vuex目錄下建立store.js文件:

// vuex/store.js
import Vue from 'vue'
import Vuex from 'vuex'
// 導入各個模塊的初始狀態和 mutations
import orderList from './modules/orderList'

Vue.use(Vuex)

export default new Vuex.Store({
  // 組合各個模塊
  modules: {
    orderList
  }
})

因爲咱們把store拆分到不一樣的模塊,因此建立store實例的時候須要引入orderList模塊,它包括帳單列表orders對象和當前被激活的帳單對象activeOrder。須要定義的mutation只有一個"SHOW_DETAIL",當用戶點擊帳單列表的某一個帳單的時候,SHOW_DETAIL更新store的activeOrder,表示當前被展開的帳單對象。modules/orderList.js以下:

// vuex/modules/index.js
import {
  SHOW_DETAIL,
} from '../mutation-types'

// 該模塊的初始狀態
const state = {
  orders: [{
    id: 'aedf9c25',
    tradeDate: '2016.04.08',
    name: 'Nike跑步鞋青年版',
    totalPrice: 888,
    repayDate: '2016.08.08',
    capital: '880',
    interest: '5.5',
    extra: '2.5',
    type: '消費',
    currentTerm: 1,
    totalTerms: 1
  }, {
    id: 'cves9chs',
    tradeDate: '2016.04.10',
    name: '支付寶提現',
    totalPrice: 773.5,
    repayDate: '2016.08.10',
    capital: '769',
    interest: '4.5',
    extra: '0',
    type: '現金',
    currentTerm: 1,
    totalTerms: 4
  }, {
    id: 'deef1d3g',
    tradeDate: '2016.05.15',
    name: '喜洋洋抱枕',
    totalPrice: 204.8,
    repayDate: '2016.08.15',
    capital: '203.5',
    interest: '1.3',
    extra: '0',
    type: '消費',
    currentTerm: 2,
    totalTerms: 3
  }],
  activeOrder: {}
}

// 相關的 mutations
const mutations = {
  [SHOW_DETAIL](state, id) {
    state.activeOrder = state.orders.find(function(ele) {
      return ele.id == id;
    });
  }
}

export default {
  state,
  mutations
}

mutation-types.js:

export const SHOW_DETAIL = 'SHOW_DETAIL'

五、Action

Actions 是組件內用來分發 mutations 的函數。第一個參數固定爲store。在這裏,當用戶點擊帳單列表的某一個帳單區域的時候,要調用dispatch('SHOW_DETAIL')。這裏的demo只涉及到一個簡單的用戶事件,因此action.js也比較簡單:

// index actions
export const showDetail = makeAction('SHOW_DETAIL')

function makeAction(type) {
  return ({
    dispatch
  }, ...args) => dispatch(type, ...args)
}

六、Vue組件

咱們在第一步,建立入口文件main.js的時候import進來App.vue這個根組件:

import App from './App.vue'
...
router.start(App, '#root')

定義App.vue也很簡單,建立一個Vue實例,把vuex的store注入到根實例便可,這樣組件內的全部子組件都能訪問到store:

// App.vue
<script>
  import Nav from './components/Nav.vue'
  import store from './vuex/store'

  export default {
    name: 'App',
    // 注入store到根組件
    store,
    data() {
      return {}
    },

    components: {
      //子組件order-nav
      'order-nav': Nav
    }
  }
</script>
<template>
  <div id="app">
    <order-nav></order-nav>
    <router-view></router-view>
  </div>
</template>

這裏的order-nav是一個導航欄組件,包括「最近7天待還」和「所有待還」兩個,因爲"所有待還」的展現和「最近7天待還」基本同樣,demo裏面就沒有再作實現,只提供一個佔位方便展現router切換。Latest.vue就是最近7天待還子組件,它獲取近7天待還帳單列表,並在下一級子組件(order)中渲染組件列表。組件獲取store的數據是經過getter來實現的。

// Latest.vue
<script>
  import { showDetail} from '../vuex/actions';
  import Order from './Order.vue';

  export default {
    name: 'Latest',

    data() {
      return {
      }
    },
    components:{
      'order':Order
    },
    vuex: {
     //解構函數,{orderList}對象指的是store的orderList模塊的state.orderList
      getters: {
        orders: ({orderList}) => orderList.orders
      },
      //注入actoin
      actions: {
        showDetail
      },
      computed:{
      }
    }
  }
</script>
<template>
  <div class="container-fluid">
    <ul class="list-unstyled row">
      <order></order>
    </ul>
  </div>
</template>

Order.vue是這個帳單組件中負責內容顯示和用戶事件分發的組件模塊。Order組件中包括比較詳細的Vue指令語法,好比v-for,v-show,track-by等。爲了加強用戶體驗,Order組件在用戶點擊帳單展現詳情的時候,經過定義trasitoin屬性達到簡單的動畫切換效果。

// Order.vue
<script>
  import {fold,showDetail} from '../vuex/actions'
  export default {
    name: 'Order',
    vuex: {
      getters: {
        orders: ({orderList}) => orderList.orders,
        activeOrder:({orderList})=>orderList.activeOrder
      },
      actions:{
        showDetail
      }
    }
  }
</script>
<template>
  <li  class="bill" v-for="order in orders" @click="showDetail(order.id)" track-by="id">
   <div>
      <h4>{{order.name}}</h4>
      <p>待還
      <span class="text-danger">{{order.totalPrice}}</span>元&nbsp;&nbsp;
      <span><small>[{{order.currentTerm}}/{{order.totalTerms}}]</small></span>
      </p>
    </div>
    <div class="bill-detail" v-show="order.id==activeOrder.id" transition="fade">
        <p>
        <div class="order-item">最後還款日期:{{order.repayDate}}</div>
        <div class="order-item">交易類型期:{{order.type}}</div>
        <div class="order-item">應還本金:{{order.capital}}元</div>
        <div class="order-item">應還利息:{{order.interest}}元</div>
        <div class="order-item">手續費:{{order.extra}}元</div>
        <div class="order-item">交易日期:{{order.tradeDate}}</div>
        </p> 
    </div>
  </li>
</template>

<style media="screen">
.bill {
  border-top:2px solid #e7e7e7;
  border-bottom: 2px solid #e7e7e7;
  margin: 5px; 
  padding: 10px;
  cursor: pointer;
}
.bill-detail {
  padding: 0 10px;
}
.order-item {
  display: inline-block;
  width: 45%;
}
/* 過渡效果 */
.fade-transition {
  transition: all .8s ease;
}
.fade-enter,
.fade-leave {
  height: 0;
  opacity: 0;
}
</style>

最後附上的是組件導航子組件,主要是負責router路由切換的Nav.vue:

// Nav.vue
<script>
  export default {
    name: 'nav',
    vuex: {
      getters: {
        current: ({orderList}) => orderList.activeOrder
      }
    }
  }
</script>
<template>
  <nav class="navbar navbar-default">
    <div class="container-fluid">
      <div class="navbar-header">
        <a class="navbar-brand" href="#" v-link="{ path: '/' }">Vue-訂單demo</a>
      </div>
      <div class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
          <li v-link="{ path: '/latest',activeClass:'active'}"><a href="#!">近7天待還 <span class="sr-only">(current)</span></a></li>
          <li v-link="{ path: '/all',activeClass:'active'}"><a href="#!"> 所有待還 </a></li>
        </ul>
      </div>
    </div>
  </nav>
</template>

至此,咱們已經一步步實現了一個基於Vue+Vuex+vue-router搭建的SPA組件demo,若是你們還沒學會,能夠直接去把完整的demo看一遍,喜歡的話也麻煩給個star。點擊這裏進入帳單組件的github地址。

相關文章
相關標籤/搜索