全局组件设计

全局组件设计

不管是React、还是Vue也好,只要是App都需要一些基础的组件,如一些弹出窗:提示框Toast、确认框Confirm、模态框Modal、警告框Alert等等。这些弹出窗在Z轴都是比较高的(z-index高),且都有一个有点透明的黑色背景打底。

打底背景一般样式(_PopBox.scss)如下。由于很多弹出窗都需要这种背景,且有这么一个功能是,点击黑色背景,就关闭弹出窗。那么我们可以设计一个可以存放弹出内容,且点击打底背景就关闭弹出窗的组件。

// 变量存在于专门记录全局变量的文件中,参考bootstrap的变量文件
$pop-z-index: 12 !default;
$pop-bg-color: rgba(0,0,0,0.7) !default;

// 弹出窗的背景
.pop-box{
    position: fixed;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    background-color: $pop-bg-color;
    z-index: $pop-z-index;
}

这里采用Vue的组件作示范

<template>
    <div class='pop-box' :style='{display: open ? "block" : "none"}'
        @click.stop.self='$emit("close")'>
        <slot></slot>
    </div>
</tempalte>

使用:用<pop-box>组合成<confirm>组件

<template>
    <pop-box :open='confirm_open' @close='confirm_close_fn'>
        <div class='pop-confirm'>...</div>
    <pop-box>
</template>

在设计组件布局中,我采用舞台/话剧布局(乱起名)。我们在看话剧或者舞台剧的时候,一般有如下三要素

  • 舞台
  • 幕帘
  • 背景

其中幕帘位于最前方,舞台在中间,背景位于最后方。

这三者对应组件那些部分?

一般用脚手架生成一个Vue项目的时候,都会有一个App.vue的组件。所有组件都是这个组件的childrenApp = 幕帘 + 舞台 + 背景。在单页面应用中,我们一般都会用到路由vue-router或者react-router。而 router = 舞台 + 背景 ,舞台就是页面组件。除了路由之外,剩下的就是幕帘了,所以,我们的那些弹出窗可以充当幕帘,在我们需要提示或者确认用户的操作,就调出弹出窗,这个操作和幕帘很像很像。

幕帘(舞台 + 背景)的一个特点是,他们都是独立的。这里没有将舞台背景独立,是由于每个路由代表一个页面,且一般背景用的都是同一个样式,就没有必要拆分出来,如果应用有多个背景,可以拆分出来。这样,不管切换了那个路由,都不会影响弹出窗,它一直存在于App的children中。那么,如果我要在某个路由,某个页面中想调出弹出窗那要怎么办?Vuex或者redux + react-redux

涉及到多个组件共享一个状态,且无关父子关系,这个时候考虑用Vuex 或者 Redux

以下使用Vuex

常量: pop-constant.js

// pop-constant.js
export const POP_CONFIRM_OPEN = 'pop-confirm-open'

状态模块module: pop.js

import * as types from '@/modules/pop-constant'

const state = {
    confirm: {
        open: false,
        text: '',
        cb: null
    }
}

const mutations = {
    [types.POP_CONFIRM_OPEN] (state, confirm) {
        state.confirm = Object.assign({}, state.confirm, confirm)
    }
}

const actions = {
    setConfirm ({commit}, confirm) {
        commit(types.POP_CONFIRM_OPEN, confirm)
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}

缓存模块的命名空间maps.js

import {createNamespacedHelpers} from 'vuex'

// 这里缓存模块`pop.js`命名空间
export const popMaps = createNamespacedHelpers('pop')

Confirm.vue组件的<script>中

import {popMaps} from '@/modules/maps'

// 映射pop.js模块的state和actions到组件上
const popActions = popMaps.mapActions(['setConfirm'])
const popState = popMaps.mapState(['confirm'])

export default {
    computed: {
        ...popState
    },
    methods: {
        ...popActions,
        closeConfirm (e) {
            this.setConfirm({open: false, cb: null})
        },
        // 要调出confirm就在那个需要调这个的组件中映射setConfirm且创建这个函数
        // 或者使用mixins 全局添加(所有组件都添加了,注意)或者局部添加
        // 像这种高频率的弹出窗(Toast),可以添加到全局mixin中
        // cb中的this要指定Vue实例 在vue组件中可以箭头函数或者bind this.openConfirm('haha', () => {})
        openConfirm (text, cb = null) {
            this.setConfirm({text, open: true, cb})
        },
        submit (res) {
            if (res) {
                const {cb} = this.confirm
                cb && cb()
            }
            return this.closeConfirm()
        }
    }
}

Confirm.vue组件中的<template>

<template>
    <pop-box :open='confirm.open' @close='closeConfirm'>
        <div class='pop-confirm'>
            <div class='confirm-text'>{{confirm.text}}</div>
            <!-- 下面是确认和取消按钮 -->
            <ul class='confirm-btns'>
                <li class='btn' @click='submit(false)'>取消</li>
                <li class='btn' @click='submit(true)'>确认</li>
            </ul>
        </div>  
    </pop-box>
<template>
posted @ 2018-05-16 11:17  烂拖鞋  阅读(230)  评论(0编辑  收藏  举报