公司有一個採用uni-app框架寫的app應用,裡面的彈層基本是使用官方的uni.showModal之類的api實現彈層,在裝置上表現就是原生的彈層,在客戶的要求下,需要更換成設計的樣式,所以就開始實現這樣一個元件。
根據彈層經常使用的方法和方式可以大致列出他需要的屬性和方法:
alert/confirm
等promise
,可以使用$api.xx().then
前幾項就很好做,就在data
中定義好欄位,外層直接拿官方的輪子uni-popup
,這樣少寫一些控制彈出的邏輯(懶的),這樣大致結構就寫好了
// template部分 <uni-popup ref="popup" :maskClick="maskClick"> <view class="st-layer" :style="{ width: width }"> <view class="st-layer__content"> <!-- #ifndef APP-NVUE --> <text class="st-layer__icon" :class="option.iconClass || getIconClass()" v-if="option.type !== 'none' && option.showIcon"></text> <!-- #endif --> <view class="st-layer__msg" v-if="option.msg"> <text>{{ option.msg }}</text> </view> </view> <view class="st-layer__footer" :class="{'is-reverse-cofirmcancel' : isReverseConfirmCancel}" v-if="option.showConfirmButton || option.showCancelButton"> <view class="st-layer__footer__btn st-layer__footer__btn--confirm" @tap.stop="confirmClick" v-if="option.showConfirmButton"><text>確認</text></view> <view class="st-layer__footer__btn st-layer__footer__btn--cancel" @tap.stop="cancelClick" v-if="option.showCancelButton"><text>取消</text></view> </view> </view> </uni-popup>
然後js部分先簡單實現了一些open和close方法
data() { return { option: {} } }, methods: { open(option) { let defaultOption = { showCancelButton: false, // 是否顯示取消按鈕 cancelButtonText: '取消', // 取消按鈕文字 showConfirmButton: true, // 是否顯示確認按鈕 confirmButtonText: '取消', // 確認按鈕文字 showIcon: true, // 是否顯示圖示 iconClass: null, // 圖示class自定義 type: 'none', // 型別 confirm: null, // 點選確認後的邏輯 cancel: null, // 點選取消後的邏輯 msg: '' } this.option = Object.assign({}, defaultOption, option) this.$refs.popup.open() }, close() { this.$refs.popup.close() }, confirmClick() { const confirmHandler = this.option.confirm if (confirmHandler && typeof confirmHandler === 'function') { confirmHandler() } this.close() this.$emit('confirm') }, cancelClick() { const cancelHandler = this.option.cancel if (cancelHandler && typeof cancelHandler === 'function') { cancelHandler() } this.close() this.$emit('cancel') } }
目前在其他頁面已經可以使用
// test.vue 可以使用uni-app的 [easycom元件規範](https://uniapp.dcloud.io/component/README?id=easycom%e7%bb%84%e4%bb%b6%e8%a7%84%e8%8c%83),不用寫import語句 <st-layer ref="stLayer"></st-layer> // js部分 this.$refs.stLayer.open({ msg: '測試', confirm: () => { console.log('點選了確認') }, cancel: () => { console.log('點選了取消') } })
現在基本功能已經實現,但是有人要說了,這樣呼叫不方便,我想這樣呼叫
open(msg).then(() => { console.log('點選了確認') }).catch(() => { console.log('點選了取消') })
那如何實現promise
化呢?最簡單的方法就是讓open方法返回一個promise
。如何點選確認或取消的時候進入then
方法呢,看下面的寫法
... open() { return new promise((reoslve, reject) => { ... this.option.confirm = this.option.confirm || function confirmResolve () { resolve() } this.option.cancel = this.option.cancel || function cancelReject () { reject() } }) } ...
如果要封裝其他單獨的方法,比如confirm
之類,可以在open基礎上擴充套件:
confirm(msg, option = {}) { if (typeof msg === 'object') { option = msg } else { option.msg = msg } return this.open({ ...option, showCancelButton: true, type: 'confirm' }) } // 呼叫方式 this.$refs.stLayer.confirm('是否確認?').then().catch()
這樣基本的彈層元件已經實現。下面也就是最後一步全域性使用
原有vue專案寫的layer元件要全域性使用通常是採用下面的方法注入到頁面中
import main from './main.vue' const LayerConstructor = vue.extend(main) const initInstance = () => { instance = new LayerConstructor({ el: document.createElement('div') }) instance.callback = defaultCallback document.getElementById('app').appendChild(instance.$el) }
直接拉過來用,結果報錯,提示error: document is undefined
,才想起uni-app
跟普通vue專案的有一個很大的區別,在它的執行原理中有介紹:
uni-app
邏輯層和檢視層分離,在非H5端執行時,從架構上分為邏輯層和檢視層兩個部分。邏輯層負責執行業務邏輯,也就是執行js程式碼,檢視層負責頁面渲染。雖然開發者在一個vue頁面裡寫js和css,但其實,編譯時就已經將它們拆分了。邏輯層是執行在一個獨立的jscore裡的,它不依賴於本機的webview,所以一方面它沒有瀏覽器相容問題,可以在Android4.4上跑es6程式碼,另一方面,它無法執行window、document、navigator、localstorage等瀏覽器專用的js API。
所以這種註冊全域性的方法已經不可用。那該如何在uni-app
中實現呢?
翻看官方論壇,找到了一個實現loadervue-inset-loader
,實現原理就是獲取sfc模板內容,在指定位置插入自定義內容(也就是需要全域性的元件),使用方式如下:
// 第一步 npm install vue-inset-loader --save-dev // 第二步 在vue.config.js(hbuilderx建立的專案沒有的話新建一個)中注入loader module.export = { chainWebpack: config => { // 超級全域性元件 config.module .rule('vue') .test(/\.vue$/) .use() .loader(path.resolve(__dirname, "./node_modules/vue-inset-loader")) .end() } } // 支援自定義pages.json檔案路徑 // options: { // pagesPath: path.resolve(__dirname,'./src/pages.json') // } // 第三步 pages.json組態檔中新增insetLoader "insetLoader": { "config":{ "confirm": "<BaseConfirm ref='confirm'></BaseConfirm>", "abc": "<BaseAbc ref='BaseAbc'></BaseAbc>" }, // 全域性設定 "label":["confirm"], "rootEle":"div" }
config
(default: {}
)
定義標籤名稱和內容的鍵值對
label
(default: []
)
需要全域性引入的標籤,打包後會在所有頁面引入此標籤
rootEle
(default: "div"
)
根元素的標籤型別,預設值為div,支援正則,比如匹配任意標籤 ".*"
label
和 rootEle
支援在單獨頁面的style裡設定,優先順序高於全域性設定
到這,該元件就可以全域性使用了,不需要在每個頁面寫標籤使用,只需要呼叫api就可以。
後面可以再根據使用情況進行優化處理。水平有限,歡迎各位大佬指點。
推薦:《》
以上就是uni-app中怎麼開發一個全域性彈層元件(程式碼範例)的詳細內容,更多請關注TW511.COM其它相關文章!