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

发布于 1周前 作者 htzhanglong 来自 Uni-App

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

需求背景

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

  • 覆盖原生导航栏、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", // 页面路径  
                    "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 中进行渲染。
  • 采用延时的策略,保证元素在页面渲染后,再去定位位置。
开发环境 版本号 项目创建方式
HBuilderX 1.9.10+ 新建uni-app项目

1 回复

在uni-app中,subNVue是一种原生子窗体技术,允许开发者在uni-app项目中嵌入原生页面以提升性能和用户体验。以下是一个关于如何在uni-app中使用subNVue的基本示例代码,展示了如何创建、显示和通信。

1. 创建subNVue页面

首先,在项目的pages.json文件中注册subNVue页面:

{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "subpages/nativePage/nativePage",
      "style": {
        "navigationBarTitleText": "原生页面",
        "app-plus": {
          "type": "subNVue", // 标记为subNVue页面
          "titleNView": false,
          "autoBackButton": true
        }
      }
    }
  ]
}

2. 编写subNVue页面的HTML和JS

subpages/nativePage/nativePage.nvue中编写原生页面的HTML:

<template>
  <div>
    <text>{{message}}</text>
    <button @click="sendMessageToMain">发送消息给主窗体</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "这是原生子窗体"
    };
  },
  methods: {
    sendMessageToMain() {
      // 使用postMessage发送消息给主窗体
      plus.webview.currentWebview().evalJS(`
        uni.postMessage({ data: 'Hello from subNVue' });
      `);
    }
  }
};
</script>

3. 在主窗体页面加载subNVue

在主窗体页面(如pages/index/index.vue)中,使用uni.createSubNVuePage创建并显示subNVue页面:

<template>
  <view>
    <button @click="openSubNVue">打开原生子窗体</button>
  </view>
</template>

<script>
export default {
  methods: {
    openSubNVue() {
      const subNVue = uni.createSubNVuePage({
        url: '/pages/subpages/nativePage/nativePage',
        style: {
          top: '0px',
          bottom: '0px'
        },
        success: (res) => {
          console.log('subNVue页面创建成功', res);
          // 监听来自subNVue的消息
          res.webview.addEventListener('message', (event) => {
            console.log('收到subNVue的消息', event.detail.data);
          });
        }
      });
      subNVue.show('slide-in-right', 300);
    }
  }
};
</script>

以上代码展示了如何在uni-app中创建、显示以及通过postMessage进行主窗体与subNVue页面之间的通信。开发者可以根据实际需求进一步扩展和定制这些功能。

回到顶部