ZGH0709
ZGH0709
  • 发布:2019-05-23 20:22
  • 更新:1 天前
  • 阅读:19933

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

分类:uni-app

此功能需要 HBuilderX 版本 1.9.10+, 需要使用自定义组件模式,关于编译模式差异详见文章

需求背景

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

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

在小程序中只能用 cover-view 来解决。App中,开发者希望有更强的解决方案。
过去在App端有很多问题:

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

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

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

在新版的hello uni-app里,接口-界面-原生子窗体新增了subNVue 示例。包括了4个 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 文件同级的 paltform\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": "100upx",  
                        "height": "150upx",  
                        "background": "transparent"  
                    }  
                }]  
            }  
        }  
    }]  
}

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

注意事项:

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

subNVue 子窗体书写

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

需要注意的是,nvuevue 页面的开发注意事项。两者开发起来还是有一些区别,目前我们正在优化 nuve,在之后会逐渐抹平 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 中进行渲染。
  • 采用延时的策略,保证元素在页面渲染后,再去定位位置。
7 关注 分享
DCloud_UNI_HT ilijiayin 今天回复我了吗 646729200@qq.com 289772676@qq.com 1063870911@qq.com 2788113729@qq.com

要回复文章请先登录注册

zhujianwei9823@163.com

zhujianwei9823@163.com

回复 飞翔的丫蛋:
mask 本来就是全屏的,你在配置文件里配置上mask就可以,subNvue不要用,建议你用nvue页面模拟弹框
1 天前
飞翔的丫蛋

飞翔的丫蛋

回复 zhujianwei9823@163.com:
mask的全屏怎么设置?nvue里面height:100%和100vh都无效的
5 天前
454196824@qq.com

454196824@qq.com

回复 454196824@qq.com:
原来是要重新编译一次里面的内容才会出现啊
2019-12-05 11:43
454196824@qq.com

454196824@qq.com

路径什么都是对的,加载不到popup.nvue里的内容 神奇。
2019-12-03 17:54
zhujianwei9823@163.com

zhujianwei9823@163.com

回复 375493129@qq.com:
我是加v-if 解决的,我是通过v-if 来控制刷新的
2019-11-28 19:05
1802256675@qq.com

1802256675@qq.com

回复 rootOfEchord:
不知道怎么解决,问了官方,没回答,你解决了吗
2019-11-26 15:18
1802256675@qq.com

1802256675@qq.com

一样的问题,不知道怎么解决
2019-11-26 15:01
737021310@qq.com

737021310@qq.com

更新了之后我以前项目的样式全蹦了
2019-11-26 14:46
zhujianwei9823@163.com

zhujianwei9823@163.com

想要监mask关闭的同学,不要用自带的mask, 自己把子窗口设置成全屏,开发的时候自己做一个mask来实现监听就行了
2019-11-25 17:21
375493129@qq.com

375493129@qq.com

请问一下,在Nvue中 我使用 vue数据绑定,从其他地方传递过来的数据变了,但是视图不刷新?不知道如何解决
2019-11-12 13:44