uni-app中怎麼開發一個全域性彈層元件(程式碼範例)

2022-03-07 22:00:22
uni-app中怎麼開發一個全域性彈層元件?下面本篇文章給大家通過例子介紹一下uni-app中實現一個全域性彈層元件的方法,希望對大家有所幫助!

公司有一個採用uni-app框架寫的app應用,裡面的彈層基本是使用官方的uni.showModal之類的api實現彈層,在裝置上表現就是原生的彈層,在客戶的要求下,需要更換成設計的樣式,所以就開始實現這樣一個元件。

根據彈層經常使用的方法和方式可以大致列出他需要的屬性和方法:

  • 型別:alert/confirm
  • 展示圖示 icon
  • 展示內容 content
  • 可以api呼叫
  • 支援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,支援正則,比如匹配任意標籤 ".*"

    labelrootEle 支援在單獨頁面的style裡設定,優先順序高於全域性設定

到這,該元件就可以全域性使用了,不需要在每個頁面寫標籤使用,只需要呼叫api就可以。

後面可以再根據使用情況進行優化處理。水平有限,歡迎各位大佬指點。

推薦:《》

以上就是uni-app中怎麼開發一個全域性彈層元件(程式碼範例)的詳細內容,更多請關注TW511.COM其它相關文章!