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调用网络请求,只使用定时函数,返回一个写死的结果,并调用回调函数,完成整个流程。
转载:https://blog.csdn.net/Yt7589/article/details/103457529