Weex 動(dòng)態(tài)Modal設(shè)計(jì)
我們在項(xiàng)目中使用了各種自定義的對(duì)話框蜡豹、彈出框等控件,其中各種控件都是在各自的頁面中彈出久锥,其中最大的問題就是在tabbar中子頁面彈出的modal不能顯示全屏。本文的目的即是設(shè)計(jì)一個(gè)動(dòng)態(tài)的Modal放在tabbar外面,通過與子頁面的調(diào)用方法來開啟對(duì)應(yīng)的動(dòng)態(tài)組件听诸。
構(gòu)建動(dòng)態(tài)組件
Weex中用的組件實(shí)際是使用Vue.js生成的代碼,由于Vue 2.0之后的代碼都是預(yù)編譯的蚕泽,以前使用動(dòng)態(tài)template的方法已經(jīng)不適用晌梨,能構(gòu)建動(dòng)態(tài)組件的方法只有<component>
以及render
方法創(chuàng)建, 本文暫時(shí)先采用<component>
來實(shí)現(xiàn)须妻, 關(guān)于更多的<component>
的知識(shí)可以參考Vue動(dòng)態(tài)組件文檔仔蝌。
最基本的動(dòng)態(tài)組件(v-modal.vue)如下:
<template>
<div class="cModal" append="tree">
<component v-bind:is="modalCurrentView"></component>
</div>
</template>
<style>
.cModal{
width: 750;
background-color: transparent;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
</style>
<script>
module.exports = {
computed:{
modalCurrentView() {
return this.$store.state.modalCurrentView
}
},
components:{
"pop-call" : require("../module/personal/mainpage/v-pop-call.vue"),
"pop-head" : require("../module/personal/mainpage/v-pop-head.vue"),
"pop-version" : require("../module/personal/mainpage/v-pop-version.vue"),
"action-sheet" : require("../manage/camera/action_sheet/v-action-sheet.vue"),
}
}
</script>
<component>
主要是通過動(dòng)態(tài)修改is綁定的modalCurrentView
組件來進(jìn)行切換,可以用此方法來實(shí)現(xiàn)類似tabbar切換之類的效果荒吏。
其中我們的數(shù)據(jù)采用的是Vuex進(jìn)行管理敛惊,也就是存儲(chǔ)在store.js文件中:
import Vuex from 'vuex'
// Vuex is auto installed on the web
if (WXEnvironment.platform !== 'Web') {
Vue.use(Vuex)
}
var store = new Vuex.Store({
state:{
modalIsShow:false,
modalCurrentView:"pop-call"
},
mutations:{
CHANGE_MODAL_SHOW (state, isShow) {
state.modalIsShow = isShow
},
CHANGE_MODAL_VIEW (state, view) {
state.modalCurrentView = view
}
},
actions:{
changeModalShow(context, state) {
context.commit("CHANGE_MODAL_SHOW", state);
},
changeModalView(context, view) {
context.commit("CHANGE_MODAL_VIEW", view);
}
}
})
export default store
可以從store.js中看出,我們可以用changeModalShow
方法來控制Modal的顯示和隱藏绰更,用changeModalView
方法可以控制切換成那一個(gè)組件豆混,不過調(diào)用store的方法顯得有些麻煩,而且在WebStorm上沒有任何提示动知,我們可以在其中再次封裝一個(gè)modal.js來進(jìn)行調(diào)用:
import store from './store.js'
import Vue from 'vue'
export default {
closeModal () {
store.dispatch("changeModalShow", false)
},
openModal (view) {
store.dispatch("changeModalView", view);
store.dispatch("changeModalShow", true)
},
}
至此一個(gè)簡單的動(dòng)態(tài)modal架構(gòu)就出來了皿伺, 我們只需要在最外層的頁面增加此動(dòng)態(tài)控件即可:
<template>
<div class="cRoot" @androidback="onClickAndroidBack"
@viewappear="viewappear"
@viewdisappear="viewdisappear">
......
// 添加動(dòng)態(tài)modal
<modal v-if="modalIsShow"></modal>
</div>
</template>
<script>
var nav = weex.requireModule('event');
var storage = weex.requireModule("storage");
module.exports = {
......
components:{
......
"modal" : require("./v-modal.vue")
},
computed:{
modalIsShow () {
return this.$store.state.modalIsShow
}
}
}
</script>
我們需要調(diào)用顯示modal就可以使用:
import vModal from '../modal.js';
vModal.openModal("pop-head"); //其中"pop-head"是我們再v-modal.vue文件中引入的components;
vModal.closeModal(); //需要關(guān)閉的時(shí)候盒粮,只需要調(diào)用此方法即可
組件之間傳值
Modal的顯示和隱藏都沒有問題了鸵鸥,但是還有一個(gè)問題就是封裝的組件點(diǎn)擊事件是直接往上傳遞,也就是傳到動(dòng)態(tài)的modal后就直接傳到最外面的頁面了丹皱,類似tabbar子頁面就不會(huì)接收到事件妒穴。此時(shí)需要我們使用非父子關(guān)系之間的傳值,簡單的方法有兩種摊崭, 一種是通過Vuex來實(shí)現(xiàn)讼油, 另一種就是使用Vue組件通信, 因?yàn)槲覀兌x了單獨(dú)的modal.js呢簸,用第一種方法矮台,調(diào)用modal時(shí)還要額外多引入文件,因此我們采用第二種方法根时。
Vue非父子關(guān)系之間的通信非常簡單瘦赫,只需要在modal.js加入幾行代碼即可:
import store from './store.js'
import Vue from 'vue'
export default {
eventBus:new Vue({}), //定義一個(gè)通用的Vue對(duì)象進(jìn)行通信
closeModal () {
store.dispatch("changeModalShow", false)
},
openModal (view) {
store.dispatch("changeModalView", view);
store.dispatch("changeModalShow", true)
},
}
不過我們原來的組件點(diǎn)擊的時(shí)候不再采用原來的this.$emit("eventName", eventData);
,因?yàn)榇朔椒ㄖ粫?huì)冒泡到最外面的頁面蛤迎, 而傳入不到調(diào)用的頁面确虱;應(yīng)該改用如下的方法:
import vModal from '../modal.js'
vModal.eventBus.$emit("eventName", eventData);
而接收事件的組件可以mounted
方法中進(jìn)行監(jiān)聽:
mounted:function(){
var that= this;
vModal.eventBus.$on("eventName", (eventData)=> {
});
},
通過此方法, 我們就可以在動(dòng)態(tài)modal包括的組件和調(diào)用動(dòng)態(tài)modal的組件之間建立了聯(lián)系替裆。
動(dòng)態(tài)組件的參數(shù)傳遞
動(dòng)態(tài)modal還有一個(gè)很大的問題校辩,就是包含的組件如果需要傳遞參數(shù)窘问,怎么辦呢?
我們首先定義一個(gè)統(tǒng)一的變量作為動(dòng)態(tài)modal的傳入?yún)?shù)宜咒, 在store.js中進(jìn)行修改惠赫,加入?yún)?shù):
var store = new Vuex.Store({
state:{
modalIsShow:false,
modalCurrentView:"pop-call",
modalData:{}
},
mutations:{
...
CHANGE_MODAL_DATA (state, data) {
state.modalData = data
}
},
actions:{
...
changeModalData(context, data) {
context.commit("CHANGE_MODAL_DATA", data);
}
}
})
export default store
而在定義的modal.js中修改openModal
方法:
openModal (view, data) {
store.dispatch("changeModalData", data);
store.dispatch("changeModalView", view);
store.dispatch("changeModalShow", true)
},
定義的modalData
可以通過<component>
的參數(shù)傳入
<template>
<div class="cModal" append="tree">
<component v-bind:is="modalCurrentView" :modalData="modalData"></component>
</div>
</template>
......
<script>
import Vue from 'vue'
module.exports = {
computed:{
modalCurrentView() {
return this.$store.state.modalCurrentView
},
modalData() {
let data = this.$store.state.modalData;
}
},
......
}
</script>
在自定義的彈出顯示的組件內(nèi)容中我們可以使用props來接收這些參數(shù):
module.exports = {
props:{
modalData:{default:{}}
},
}
不過用此方法傳遞的參數(shù),對(duì)子組件的修改比較多荧呐, 更好的方法是使用render函數(shù)來動(dòng)態(tài)構(gòu)建一個(gè)組件, 通過修改控件的tag來實(shí)現(xiàn)纸镊,不過在實(shí)際使用的時(shí)候碰到了一些問題倍阐,暫時(shí)先用這種不太優(yōu)雅的方式實(shí)現(xiàn),后續(xù)再進(jìn)行改進(jìn)吧逗威。