uni-app是當前前端開端多端應用的一個強大的工具,可以同時七端發佈。由於之前前端的功能較簡單,所以前端一般不重視程序架構設計,通常所說的MVVM等架構,一般也就止步於VUE就是基於MVVM,使用VUE就是採用MVVM架構,而實際上採用架構的作用,就是要使應用代碼職責分離,增加可維護性。在本篇博文中,我將向大家展示,怎樣通過使用VUEX,將業務邏輯代碼由.vue文件中抽取出來,形成一個職責清晰的應用架構。
整體架構
我們首先來看一下程序的整體架構:
如上圖所示,我們在store中保存vuex的store定義,以及store下的模塊定義。在pages裏面是頁面*.vue文件,components中是我們頁面將要使用的組件,這裏定義了兩個組件:cart和products,分別用來顯示購物車和商品列表,api下保存與後臺接口交互的模塊,這裏定義的是shop。
全局引用vuex
我們首先在main.js引入vuex的總store:
....................................
import store from './store'
....................................
const app = new Vue({
store,
...App
})
...................................
我們在這裏定義的store,可以在應用任何頁面中引用。
主頁面
接着我們定義示例的頁面,這個頁面引用購物車和商品組件顯示相關內容:
<template>
<div id="app">
<h3>購物車示例</h3>
<hr>
<h5>產品列表</h5>
<ProductList/>
<hr>
<ShoppingCart/>
</div>
</template>
<script>
import ProductList from '../../components/ProductList.vue'
import ShoppingCart from '../../components/ShoppingCart.vue'
export default {
components: { ProductList, ShoppingCart }
}
</script>
商品組件
商品組件主要是顯示商品列表,如下所示:
<template>
<ul>
<li
v-for="product in products" :key="product.id">
{{ product.title }} - {{ product.price}}
<br>
<button
:disabled="!product.inventory"
@click="addProductToCart(product)">
加入購物車
</button>
<button @click="test001">測試</button>
<button @click="test002">內部</button>
</li>
</ul>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState({
products: state => state.products.all
})
},
methods: {
...mapActions('cart', ['addProductToCart']),
...mapActions({
test001: 'products/test001'
}),
test002: (e) => {
console.log('頁面內事件')
}
},
created () {
this.$store.dispatch('products/getAllProducts')
}
}
</script>
頁面用v-for顯示商品列表,加入購物車按鈕可以調用store.cart模塊的addProductToCart,將其加入購物車中;測試按鈕顯示另外一種方式調用store模塊的actions方法,內部按鈕顯示僅用於頁面內事件,無需使用vuex。
在腳本部分,首先引入vuex的mapState和mapActions,雖然有其他方法實現同樣任務,但是建議統一使用這種方式。
在計算屬性部分,我們將store中products.all屬性賦給頁面的計算屬性products。
在方法部分,我們將加入購物車按鈕的響應事件,定義爲調用store的cart模塊的addProductToCart的actions;將測試按鈕的單擊響應事件,定義爲調用store的products模塊的test001的actions;將內部按鈕的單擊響應事件,定義爲調用頁面內消息響應函數。在頁面創建完成的生命週期函數中,調用store的products模塊的getAllProducts的actions。
products模塊
接下來我們來看products模塊:
import shop from '../../api/shop'
// initial state
const state = {
all: []
}
// getters
const getters = {}
// actions
const actions = {
getAllProducts ({ commit }) {
shop.getProducts(products => {
commit('setProducts', products)
})
},
test001() {
console.log('store.products.test001 is running...')
}
}
// mutations
const mutations = {
setProducts (state, products) {
state.all = products
},
decrementProductInventory (state, { id }) {
const product = state.all.find(product => product.id === id)
product.inventory--
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
這裏我們看到,其有一個all的屬性,就是主頁中要顯示的商品列表數據。
接下來定義getter方法,這裏沒有定義。
在接下來是定義actions,當我們要在異步請求中更新store.state時,需要調用actions中的方法,由這些方法調用對應的mutation,而mutation只能是同步調用。例如這裏定義的getAllProducts,由於需要調用網絡請求,因此是異步的,所以需要在actions裏調用mutation方法。大家可以看到,調用shop對應的方法時,參數爲一個箭頭函數,當API層處理返回函數時,會執行這個箭頭函數,在這個箭頭函數中調用commit方法,由vuex處理爲對mutation方法的調用。
接下來定義mutation方法,該方法去實際更新state中的數據。
我們在getAllProducts中看到,我們要調用api層的shop來實現從後臺取商品列表數據的功能,因此我們來看api層的實現。
API層
這一層主要用於處理與後臺交互,以getAllProducts爲例:
/**
* Mocking client-server processing
*/
const _products = [
{"id": 1, "title": "魚香肉絲", "price": 25.01, "inventory": 20},
{"id": 2, "title": "宮爆雞丁", "price": 30.99, "inventory": 10},
{"id": 3, "title": "剁椒魚頭", "price": 59.99, "inventory": 5}
]
export default {
getProducts (cb) {
setTimeout(() => cb(_products), 100)
},
buyProducts (products, cb, errorCb) {
setTimeout(() => {
// simulate random checkout failure.
(Math.random() > 0.5 || navigator.userAgent.indexOf('PhantomJS') > -1)
? cb()
: errorCb()
}, 100)
}
}
這裏爲了簡化,沒有使用Axiox調用網絡請求,只使用定時函數,返回一個寫死的結果,並調用回調函數,完成整個流程。