PHP前端开发

uni-app中怎么开发一个全局弹层组件(代码示例)

百变鹏仔 4周前 (11-20) #uniapp
文章标签 示例

uni-app中怎么开发一个全局弹层组件?下面本篇文章给大家通过例子介绍一下uni-app中实现一个全局弹层组件的方法,希望对大家有所帮助!

公司有一个采用uni-app框架写的app应用,里面的弹层基本是使用官方的uni.showModal之类的api实现弹层,在设备上表现就是原生的弹层,在客户的要求下,需要更换成设计的样式,所以就开始实现这样一个组件。

根据弹层经常使用的方法和方式可以大致列出他需要的属性和方法:

前几项就很好做,就在data中定义好字段,外层直接拿官方的轮子uni-popup,这样少写一些控制弹出的逻辑(懒的),这样大致结构就写好了

// template部分<uni-popup><view><view><!-- #ifndef APP-NVUE --><text v-if="option.type !== 'none' &amp;&amp; option.showIcon"></text><!-- #endif --><view><text>{{ option.msg }}</text></view></view><view><view v-if="option.showConfirmButton"><text>确认</text></view><view 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 &amp;&amp; typeof confirmHandler === 'function') {                    confirmHandler()            }            this.close()            this.$emit('confirm')    },    cancelClick() {            const cancelHandler = this.option.cancel            if (cancelHandler &amp;&amp; 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></st-layer>// js部分this.$refs.stLayer.open({    msg: '测试',    confirm: () =&gt; {        console.log('点击了确认')    },    cancel: () =&gt; {        console.log('点击了取消')    }})

现在基本功能已经实现,但是有人要说了,这样调用不方便,我想这样调用

open(msg).then(() =&gt; {    console.log('点击了确认')}).catch(() =&gt; {     console.log('点击了取消')})

那如何实现promise化呢?最简单的方法就是让open方法返回一个promise。如何点击确认或取消的时候进入then方法呢,看下面的写法

...open() {     return new promise((reoslve, reject) =&gt; {        ...        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 = () =&gt; {  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创建的项目没有的话新建一个)中注入loadermodule.export = {    chainWebpack: config =&gt; {            // 超级全局组件            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></baseconfirm>",          "abc": "<baseabc></baseabc>"      },      // 全局配置      "label":["confirm"],      "rootEle":"div"  }

配置说明

到这,该组件就可以全局使用了,不需要在每个页面写标签使用,只需要调用api就可以。

后面可以再根据使用情况进行优化处理。水平有限,欢迎各位大佬指点。

推荐:《uniapp教程》