平时用vuejs写业务页面时,发现经常会用到相同的逻辑去搭建页面,初始化数据、校验参数、子页面子路由的渲染等。所以在这里总结一下这种页面的初始化套路,方便后续创建页面或者优化逻辑。
设计原则
利用发布-订阅的模式去初始化子页面,父页面处理全局的相关操作。
- 父页面:校验参数、数据的合法性,并发网络请求,引导至全局的错误页面,最终触发initReady=true。
- 子页面:订阅initReady事件,成功后进行子页面初始化。
逻辑图
代码设计
1. vuex状态数据
// store.js
import global from './modules/global';
import cards from './modules/cards';
export default {
namespaced: true,
modules: {
global,
cards,
},
};
// ./modules/global.js
/*
* 全局数据
*/
export default {
// 命名空间隔离
namespaced: true,
state: {
// 全局事件-初始化完成,子页面订阅此事件的变更
initReady: false,
// 全局错误码
error: null,
// 全局常量,不可更改
constants: Object.freeze({
test: 1,
}),
},
mutations: {
SET_INIT_READY(state, value) {
state.initReady = value;
},
SET_ERROR(state, value) {
state.error = value;
},
},
actions: {
setInitReady({ commit }, value) {
commit('SET_INIT_READY', !!value);
},
setError({ commit }, value) {
commit('SET_ERROR', value || null);
},
},
};
// ./modules/cards.js
/*
* 卡列表相关数据
*/
export default {
// 命名空间隔离
namespaced: true,
state: {
// 当前卡
currentCard: {},
// 卡列表
cardsList: [],
},
mutations: {
SET_CURRENT_CARD(state, value) {
state.currentCard = value;
},
SET_CARDS_LIST(state, value) {
state.cardsList = value;
},
},
actions: {
setCurrentCard({ commit }, value) {
commit('SET_CURRENT_CARD', value || {});
},
setCardsList({ commit }, value) {
commit('SET_CARDS_LIST', value || []);
},
},
};
2. vue router路由定义
// routes.js,subApp的路由定义
const index = () => import(/* webpackChunkName: "HcTplSubApp" */ './views/index.vue');
const home = () => import(/* webpackChunkName: "HcTplSubApp" */ './views/home.vue');
export default [
{
path: '/',
component: index,
meta: {
title: '首页',
},
children: [
{
path: '', // 首页
name: 'RouteHome', // 使用路由名字进行跳转,有助于代码易读性
component: home,
meta: {
title: '首页',
keepAlive: true,
},
},
],
},
];
3. index父页面
<template>
<div class="page-index">
<!-- 错误提示 -->
<error-page v-if="error" :error="error || {}"></error-page>
<!-- 子页面 -->
<template v-if="initReady">
<!-- 缓存 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<!-- 非缓存 -->
<router-view v-if="!$route.meta.keepAlive"/>
</template>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
import ErrorPage from '@/components/common/ErrorPage';
export default {
name: 'PageIndex',
components: {
ErrorPage,
},
data() {
return {
// loading spinner
loading: null,
};
},
computed: {
...mapState('tpl/global', {
initReady: state => state.initReady,
constants: state => state.constants,
error: state => state.error,
}),
...mapState('tpl/cards', {
cardsList: state => state.cardsList,
currentCard: state => state.currentCard,
}),
},
created() {
this.initPage();
},
methods: {
...mapActions('tpl/global', [
'setInitReady',
'setError',
]),
...mapActions('tpl/cards', [
'setCurrentCard',
'setCardsList',
]),
/**
* 全局初始化
*/
async initPage() {
this.loading = this.$weui.loading('加载中');
// 校验参数
if (!this.validateQuery()) {
if (this.loading) this.loading.hide();
return;
}
// 所有初始化的异步请求
await Promise.all([this.initCard()]);
// 校验结果
if (!this.validateResult()) {
if (this.loading) this.loading.hide();
return;
}
// 触发ready事件
this.setInitReady(true);
},
/**
* 校验参数合法性
*/
validateQuery() {
const { hospitalId = '' } = this.$route.query;
if (!hospitalId) {
this.setError({ message: '医院标识[hospitalId]不能为空' });
return false;
}
return true;
},
/**
* 校验全局数据,报错或跳转
*/
validateResult() {
// 卡列表数据为空,跳转第三方链接建卡
if (this.cardsList.length === 0) {
this.setError({ message: '卡列表不能为空' });
return false;
}
return true;
},
/**
* 获取卡列表
*/
async initCard() {
// 利用卡列表插件读取卡列表
const { card, cards } = await this.$Card.init();
this.setCurrentCard(card);
this.setCardsList(cards);
// 校准url里的healthCardId参数
const { query } = this.$route;
if (card.healthCardId && query.healthCardId !== card.healthCardId) {
await this.$router.replace({
query: {
...query,
healthCardId: card.healthCardId,
ecardNo: card.ecardNo,
},
});
}
},
},
};
</script>
<style lang="scss" scoped>
.page-index {
}
</style>
4. home子页面
<template>
<div class="page-home">
page home
</div>
</template>
<script>
import { mapState } from 'vuex';
import pageMixin from 'hc-vue-mixins/page';
export default {
name: 'PageHome',
mixins: [pageMixin],
data() {
return {
// loading spinner
loading: null,
};
},
computed: {
...mapState('tpl/global', {
initReady: state => state.initReady,
}),
...mapState('tpl/cards', {
cardsList: state => state.cardsList,
currentCard: state => state.currentCard,
}),
},
// 利用hooks或者watch来订阅initReady事件
created() {
if (this.initReady) this.initPage();
},
watch: {
initReady() {
if (this.initReady) this.initPage();
},
},
methods: {
/**
* 初始化
*/
async initPage() {
this.loading = this.$weui.loading('加载中');
// do something in this page
console.log(this.currentCard);
console.log(this.cardsList);
this.loading.hide();
},
},
};
</script>
<style lang="scss">
.page-home {
}
</style>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。