此功能需要 HBuilderX 版本 1.9.10+, 不支持非自定义组件模式。
需求背景
在我们的开发中,经常会遇到各种层级覆盖和原生界面自定义的问题:
- 覆盖原生导航栏、
tabbar
的弹出层组件。比如侧滑菜单盖不住地图、视频、原生导航栏,比如popup
盖不住tabbar
。 - 弹出层内部元素可滚动,
- 在地图、视频等组件上的添加复杂覆盖组件:比如直播视频上覆盖滚动的聊天记录。
在小程序中只能用 cover-view
来解决。App中,开发者希望有更强的解决方案。
当然在App端使用nvue是不存在前端元素无法覆盖原生元素的层级问题的,但app-vue页面仍然需要面对复杂的层级问题:
- app-vue的
cover-view
不支持嵌套、只能在video
、map
上使用、样式和控件少; plus.nativeObj.view
虽然更灵活,但易用性比较差、没有动画、不支持内部内容滚动。
既然uni-app已经支持 nvue
的原生渲染,我们何不做一个subNVue
,来替代 cover-view
,实现更强的功能?
顾名思义,subNVue
是 vue
页面的子窗体,它不是全屏页面,就是用于解决 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
) 类型,且级别最高,一旦设置type
为navigationBar
或popup
,position
和dock
的值都会被忽略。position
为原生子窗体的定位方式。dock
表示原生子窗体的停靠位置,只有当position
值为dock
时才生效,如top
,bottom
,right
,left
等。- 在配置中可以使用 upx 单位,方便你进行响应式布局。
subNVue 子窗体书写
subNVue
子窗体引用的是 nvue
页面。所以只需要书写 nvue
页面。
需要注意的是,nvue
与 vue
页面的开发注意事项。两者开发起来还是有一些区别。
相关参考
- 使用
nvue
开发注意事项:https://uniapp.dcloud.io/use-weex - 使用
vue
开发注意事项:https://uniapp.dcloud.io/use
怎么在页面中使用 subNVue 子窗体
在 pages.json
中增加完配置,也写好了 subNVue
子窗体,接下来就是在 vue
/nvue
页面中使用了。 在 vue
和 nvue
页面中使用方式是一样的,这里以 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
页面之间,可以互相发送和传递消息,进而实现彼此之间的互相更新和表现协调。 在 vue
和 nvue
中进行通信的方式一致,这里仍然以 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 中进行渲染。
- 采用延时的策略,保证元素在页面渲染后,再去定位位置。
283 个评论
要回复文章请先登录或注册
初学者123
初学者123
d***@qq.com
[已删除]
zhangyq
zhangyq
zhangyq
初学者123
choin
dyjx2004