ZGH0709
ZGH0709
  • 发布:2019-05-23 20:22
  • 更新:2022-10-27 18:38
  • 阅读:128092

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 中进行渲染。
  • 采用延时的策略,保证元素在页面渲染后,再去定位位置。
21 关注 分享
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

要回复文章请先登录注册

1***@qq.com

1***@qq.com

还有一个问题 pages 路由里面的subnvue 页面 分包之后的页面不能直接使用,要copy一份到自己的文件路劲内,一个nvue文件 在几个subpackage中用到就要复制几份 有全局管理方案吗?
2022-10-27 18:38
1***@qq.com

1***@qq.com

我看到文档里 globalStyle 可以 配置 app-plus subnvue 我配置了subnvue 但是不显示出来,
2022-10-27 18:36
1***@qq.com

1***@qq.com

回复 k***@163.com :
楼上的解决了吗?
2022-09-27 14:23
3***@qq.com

3***@qq.com

22:50:56.881 at <BuyTips__pageId=NaN__pagePath="pages/subnvue/buy_tips"__pageQuery={}>
22:50:56.917 reportJSException >>>> exception function:createInstanceContext, exception:white screen cause create instanceContext failed,check js stack ->Uncaught TypeError: Cannot read property 'id' of undefined


这个是怎么回事?
2022-09-09 09:47
3***@qq.com

3***@qq.com

回复 3***@qq.com :
这个问题解决了吗?
2022-09-08 16:22
k***@163.com

k***@163.com

subNVues 窗口,右滑退出窗口,onHide 、onBackPress 钩子函数都无法触发,请问有什么解决办法吗?
2022-09-07 16:36
k***@163.com

k***@163.com

机型:红米K50
系统:miui 13
全面屏手势

subNVues 窗口,右滑退出窗口,onHide 、onBackPress 钩子函数都无法触发,请问有什么解决办法吗?
2022-09-07 15:52
3***@qq.com

3***@qq.com

21:49:47.229 [Vue warn]: Failed to resolve component: uniSurfaceView
21:49:47.255 If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
21:49:47.275 at <Surfaceview __pageId=NaN __pagePath="pages/index/subNvue/surfaceview" __pageQuery= {} > __WARN

在subNVue里面,可以正常显示,就是报这个警告,而且原生组件的方法调用报错
2022-08-18 21:57
1***@qq.com

1***@qq.com

回复 1***@qq.com :
后面解决了吗?
2022-08-04 22:41
1***@qq.com

1***@qq.com

[Vue warn]: Error in v-on handler: "Error: Unable to find SubNVue, id=ejectPopup",啥情况找不到
2022-08-04 17:20