平时用vuejs写业务页面时,发现常常会用到雷同的逻辑去搭建页面,初始化数据、校验参数、子页体面路由的渲染等。所以在这里总结一下这种页面的初始化套路,不便后续创立页面或者优化逻辑。

设计准则

利用公布-订阅的模式去初始化子页面,父页面解决全局的相干操作。

  • 父页面:校验参数、数据的合法性,并发网络申请,疏导至全局的谬误页面,最终触发initReady=true。
  • 子页面:订阅initReady事件,胜利后进行子页面初始化。

逻辑图

代码设计

1. vuex状态数据

// store.jsimport 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>