ZGH0709
ZGH0709
  • 发布:2019-05-23 20:22
  • 更新:2024-10-26 01:13
  • 阅读:183275

uni-app subNVue 原生子窗体开发指南

分类:uni-app

此功能需要 HBuilderX 版本 1.9.10+, 不支持非自定义组件模式。

需求背景

在我们的开发中,经常会遇到各种层级覆盖和原生界面自定义的问题:

  • 覆盖原生导航栏、tabbar 的弹出层组件。比如侧滑菜单盖不住地图、视频、原生导航栏,比如 popup盖不住tabbar
  • 弹出层内部元素可滚动,
  • 在地图、视频等组件上的添加复杂覆盖组件:比如直播视频上覆盖滚动的聊天记录。

在小程序中只能用 cover-view 来解决。App中,开发者希望有更强的解决方案。
当然在App端使用nvue是不存在前端元素无法覆盖原生元素的层级问题的,但app-vue页面仍然需要面对复杂的层级问题:

  1. app-vue的cover-view 不支持嵌套、只能在 videomap 上使用、样式和控件少;
  2. plus.nativeObj.view 虽然更灵活,但易用性比较差、没有动画、不支持内部内容滚动。

既然uni-app已经支持 nvue 的原生渲染,我们何不做一个subNVue,来替代 cover-view,实现更强的功能?

顾名思义,subNVuevue 页面的子窗体,它不是全屏页面,就是用于解决 vue 页面中的层级覆盖和原生界面自定义用的。它也不是组件,就是一个原生子窗体

在新版的hello uni-app里,接口-界面-原生子窗体新增了subNVue 示例。包括了4个 subNVue 示例:

  • 顶部原生的渐变背景色导航栏(注:此示例其实已过期,HBuilderX 2.6.6起pages.json自带的titleNView已经可以实现渐变背景色和更多自定义能力,性能是高于subnvue方案的)
  • 侧滑菜单,可以盖住原生视频
  • 弹出一个原生的 popup,并且内部内容可滚动
  • 视频上覆盖一个滚动聊天记录

有了 subNVue,插件市场的一些插件就没有意义了,比如这个原生增强提示框插件,完全可以用 subNVue 替代,免去原生插件打包的麻烦。

在通信方面: subNVue 页面可以和 vue 页面进行通信,来告知 vue 页面用户执行的操作。或者通过 vue 页面对 subNVue 进行数据和状态的更新。 subNVue 除了与 vue 页面进行通信,还 可以与 nvue 页面进行通信

使用 subNVue 子窗体的页面结构

我们建议 subNVue 子窗体与引用该子窗体的vue页面放在同一目录下,新建 subNVue 目录包含这些 subNVue 子窗体,例如:

|-- pages  
    |-- index               // index 目录  
    |   |-- subNVue         // subNVue 目录  
    |       |-- nav.nvue    // 自定义导航栏  
    |       |-- popup.nvue  // 弹出层子窗体  
    |-- index.vue           // index 页面

当然你也可以提供公共的 subNVue 子窗体,供多个 vue 页面引用,此时我们建议放在 最外层与 pages 文件同级的 platform\app-plus\subNVue 下。(只是建议,不是约束。不管放哪里,只要 pages.json 里引用了,都会编译到App端)

使用 subNVue 子窗体的 pages.json 配置

pages.json 中,新增了 subNVues 节点, 与 titleNView 在同一级别。支持配置 subNVue 子窗体的相关属性。配置结构如下:

subNVues:

  • id: [String], 全局唯一,不能重复
  • path: [String], subNVue 子窗体的路径。
  • type: [String], 内置的特殊子窗体类型,弹出(popup)和导航(navigationBar)。
  • style: [Object], 配置子窗体的位置,背景等样式属性。

代码示例:

{  
    "pages": [{  
        "path": "pages/index/index", //首页  
        "style": {  
            "app-plus": {  
                "subNVues":[{  
                    "id": "concat", // 唯一标识  
                    "path": "pages/index/subnvue/concat", // 页面路径  
                    /*"type": "popup",  这里不需要*/  
                    "style": {  
                        "position": "absolute",  
                        "dock": "right",  
                        "width": "100rpx",  
                        "height": "150rpx",  
                        "background": "transparent"  
                    }  
                }]  
            }  
        }  
    }]  
}

关于 subNVue 更多详细的配置见: 完整配置

注意事项:

  • id 属性是全局唯一的,
  • path 路径只能是 nuve 页面路径
  • type 属性目前只有导航栏 (navigationBar) 和弹出层 (popup) 类型,且级别最高,一旦设置 typenavigationBarpopuppositiondock 的值都会被忽略。
  • position 为原生子窗体的定位方式。
  • dock 表示原生子窗体的停靠位置,只有当 position 值为 dock 时才生效,如 top, bottom,right, left 等。
  • 在配置中可以使用 upx 单位,方便你进行响应式布局。

subNVue 子窗体书写

subNVue 子窗体引用的是 nvue 页面。所以只需要书写 nvue 页面。

需要注意的是,nvuevue 页面的开发注意事项。两者开发起来还是有一些区别。

相关参考

怎么在页面中使用 subNVue 子窗体

pages.json 中增加完配置,也写好了 subNVue 子窗体,接下来就是在 vue/nvue 页面中使用了。 在 vuenvue 页面中使用方式是一样的,这里以 vue 页面为例进行说明:

在页面中打开和关闭 subNVue 子窗体

// 通过 id 获取 nvue 子窗体  
const subNVue = uni.getSubNVueById('map_widget')  
// 打开 nvue 子窗体  
subNVue.show('slide-in-left', 300, function(){  
    // 打开后进行一些操作...  
    //   
});  
// 关闭 nvue 子窗体  
subNVue.hide('fade-out', 300)

动态修改 subNVue 子窗体位置,大小

subNVue.setStyle({  
    top: '100px',  
    left: '20px',  
    width: '100px',  
    height = '50px',  
})

subNVue 子窗体与 vue/nvue 页面通信

无论是页面与页面,子窗体与子窗体之间,如果没有了彼此之间的通信,都只是孤立的散件而已。 nvue 子窗体与使用子窗体的 vue/nvue 页面之间,可以互相发送和传递消息,进而实现彼此之间的互相更新和表现协调。 在 vuenvue 中进行通信的方式一致,这里仍然以 vue 页面为例:

推荐使用页面通讯完成与子窗体通讯(新增)

关于页面通讯的内容详见: 页面通讯指南

通讯实现方式

// 在 subNVue/vue 页面注册事件监听方法  
// $on(eventName, callback)  
uni.$on('page-popup', (data) => {  
    vm.title = data.title;  
    vm.content = data.content;  
})  

// 在 subNVue/vue 页面触发事件  
// $emit(eventName, data)  
uni.$emit('page-popup', {  
    title: '我是一个title',  
    content: '我是data content'  
});

使用页面通讯时注意事项: 要在页面卸载前,使用 uni.$off 移除事件监听器。

旧的通讯方式(推荐上述使用页面通讯机制)

vue 页面中监听 subNVue 子窗体的消息和向 subNVue 子窗体传递消息

// 获取要通信的 subNVue 子窗体  
const subNVue = uni.getSubNVueById('map_widget')  

// vue 向 subNVue 子窗体发送消息  
//  postMessage(<Object>)  
subNVue.postMessage({  
    type: 'message',  
    title: '我是来自 vue 页面的消息',  
    content: 'Hello, map_widget'  
});  

// vue 监听 subNVue 子窗体传递的消息  
subNVue.onMessage((res) => {  
    const data = res.data;  
    // 执行一些操作  
});

subNVue 子窗体监听 vue 页面的消息和向 vue 页面发送消息

// 获取当前 subNVue 子窗体  
// 可以使用 getSubNVueById 查找的方式,但推荐使用下面的方式  
const subNVue = uni.getCurrentSubNVue();  

// subNVue 子窗体向 vue 页面发送消息  
//  postMessage(<Object>)  
subNVue.postMessage({  
    type: 'message',  
    title: '我是来自 subNVue 子窗体的消息',  
    content: 'Hello, map_widget'  
});  

// subNVue 子窗体监听 vue 页面传递的消息  
subNVue.onMessage((res) => {  
    const data = res.data;  
    // 执行一些操作  
});

总结

基本的使用方式和场景已经介绍完了, 对于使用 subNVue 在更多的应用场景中去实现更多的功能,就需要大家去不断的尝试和创新了。

当然如果一些简单的需求,如果 cover-view 已经能搞定,那也没必要使用subNVue,毕竟能跨端,内存占用也更低。

强大的东西往往也意味着消耗更多内存,为了保证更好的性能体验,一个vue页面不要加载太多 subNVue 子窗体,建议控制在三个以内。

注意事项
在使用 subNVue 子窗体的页面中,同时满足下面两种情形时:

  • 页面包含 map, video 之类的原生组件

  • 页面使用了 type 为 navigationBar 的 subNVue 子窗体

原生组件可能会出现错位的问题,目前可以使用以下方法进行解决:

  • 将此类元素放在页面的 onReady 中进行渲染。
  • 采用延时的策略,保证元素在页面渲染后,再去定位位置。
23 关注 分享
DCloud_UNI_HT ilijiayin 今天回复我了吗 6***@qq.com 2***@qq.com 1***@qq.com 2***@qq.com JinTap dqjia sonicsunsky 睡不醒哎 仔仔_iZaiZaiA chinahappybeer YIChen xiaoyueliang 九涯 3***@qq.com 一抱一个胖猪猪 w***@163.com 悬剑 4***@qq.com HRK_01 d***@sina.com

要回复文章请先登录注册

1***@163.com

1***@163.com

回复 c***@outlook.com :
你去看看pages.json里面的path,path最后需要加上.nvue文件后缀
2024-10-26 01:13
1***@163.com

1***@163.com

目前遇到两个问题:1.type为popup的时候官方说的自带遮罩层,这个遮罩只在主页面有背景色,跳转到子页面后,遮罩颜色会丢失 2,因为多个地方用到subnvue组件,传递的数据不一样,subnvue页面v-for遍历数据列表时会出现模板动态渲染问题,导致列表加载出来的时候闪动
2024-10-26 01:12
c***@outlook.com

c***@outlook.com

回复 20020225 :
我也是这样的,用的是vue3语法糖,请问你最后解决了吗?
2024-10-25 22:56
1***@163.com

1***@163.com

目前遇到的bug是vue页面路由跳转后,在跳转后的页面中再调subNVue窗体,有效果,但是窗体的遮罩层颜色会消失
2024-10-25 09:42
1***@163.com

1***@163.com

回复 1***@163.com :
有动画,点击遮罩不会关闭,不会侧滑
2024-10-23 17:47
1***@163.com

1***@163.com

"style": {
"width": "100%",
"height": "40%",
"top": "60%",
"popGesture": "none", //width和height为100%时候,禁止侧滑
"background": "rgba(0, 0, 0, 0)", //整个子窗体背景色(width:100% height:100%),transparent会穿透,rgba不会
"mask": "rgba(0, 0, 0, 0.5)"
} 这样就可以了
2024-10-23 17:46
1***@qq.com

1***@qq.com

点击返回窗口就消失了啊
2024-10-14 19:40
6***@qq.com

6***@qq.com

想问下 popup 类型的subNVUE页面 隐藏时会触发什么事件么
2024-10-14 15:21
1***@qq.com

1***@qq.com

回复 2***@qq.com :
是因为uniapp的video标签本来就层级太高吧
2024-09-24 18:20
hoen

hoen

为什么初始化时不隐藏,非得onload 再hide,使用hide经常失效
2024-09-13 15:19