全局组件设计
全局组件设计
不管是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
的组件。所有组件都是这个组件的children
。App
= 幕帘 + 舞台 + 背景
。在单页面应用中,我们一般都会用到路由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>