最近在做一个手机端的商城项目,框架使用uni-app,其中有一个类似于淘宝的商品首页轮播图效果,轮播图可以是视频也可以是图片;本来以为是一个很简单的功能,毕竟uni-app自带了swiper标签。直接判断使用就行,谁知道后来踩坑无数(无论是百度还是cloud社区都没有很好的文档),用了两天时间才解决;下面是我的解决过程。
一、video出现的问题
在真机调试中,video如果放在swiper中便会出现层级过高的问题,遮挡导致轮播不能滑动;这是因为在uni-app中为了性能考虑,video使用了原生组件,在编译中会遮挡住所有页面元素并且无法调整;uni-app官方给出的解决办法有三种
1.使用cover-image
2.使用nvue页面
3.使用subnvue
方法一原理上可行但有很大局限,具体可参考官方文档
方法二由于nvue语法还是和vue有所差异,包括只能使用flex布局,无法使用任何公共js和css,当页面结构较为复杂时非常麻烦,而且对于本文只需要做一个轮播而言没有必要,所以综合考虑使用subnvue
二、subnvue页面搭建
前半部分官方文档说明的还是非常仔细的:
首先在需要subnvue的页面中新建一个文件夹,文件夹中放置subnvue文件如图:
第二步:在pages.json中注册subnvue;以我的文件sp_details.vue为例,在pages.json中找到该文件,在其配置文件的“app-plus”属性中的“titlenview”同级,加入subnvue属性,如图:
具体的属性可以去官方文档中查看
第三步:在sp_details.vue中使用
在需要加入subnvue的页面的mounted函数中加入这段代码:
mounted() {
const subNvue = uni.getSubNVueById('concat');//初始化subnvue对象 'concat'为pages.josn中注册时的唯一标识
subNvue.show();//显示subnvue;更多方法请·查看官方文档
},
下面是我的subnvue中的轮播页面(图片加视频)
<template>
<div class="header">
<swiper class="swiper" :indicator-dots="indicatorDots" circular :autoplay="autoplay" :interval="interval" :duration="duration"
@change="get_current">
<swiper-item v-for="(item,i) of mybanner" :key='i'>
<!-- 判断:当alt为1时显示视频,其余显示图片 -->
<video :src="item.url" id="myVideo" controls enable-progress-gesture='false' class="myvideo" v-if="item.alt == 1"></video>
<image class="myimg" :src="item.url" mode="" v-else></image>
</swiper-item>
</swiper>
</div>
</template>
<script>
</script>
<style>
.header {
width: 750rpx;
height: 750rpx;
}
.swiper {
width: 750rpx;
height: 750rpx;
}
.myvideo {
width: 750rpx;
height: 750rpx;
}
.myimg {
width: 750rpx;
height: 750rpx;
}
</style>
css因为害怕有不可预知的问题,所以写的比较傻
完成到此,基本的subnvue显示是可以了,但是实际应用中肯定是要从服务器拿数据,而本文的坑才刚刚开始
三、和父文件传输数据
根据官方文档,推荐使用uni.on来发送,接收数据,
注意:使用uni.off
在数据传输时,可能会出现这种情况:subnvue中数据接收到了,也能打印出来,但是页面却没有正确显示;开始时我认为可能因为是异步函数,导致页面渲染完成后才拿到数据,因此进行了无数次调试,最终发现调试代码在ios真机运行时可以正确渲染数据,但在安卓真机调试时一直都是只有第一次打开包含subnvue的页面时才能正确显示,退出后再次打开的情况是数据可以打印出来,页面没有渲染。翻遍了官方文档和论坛也没有好的解决办法;好在nvue界面和vue界面公用内存,所以在安卓机上只能使用uni.getStorageSync方法拿取数据;
下面是我的两个文件间的数据传递代码
父文件:
//向子页面nvue子页面传输数据(ios可用)
to_nvue(){
let _this = this;
if(this.banner){
uni.$emit('lunbo',{
mybanner:_this.banner
})
}
},
this.$.set_data('banner',this.banner)//向内存中存入数据,安卓可用
//(this.$.set_data是封装的uni.getStorageSync,效果相同,方便记忆)
subnvue文件:
created() {
//this.listen()
this.banner = uni.getStorageSync('banner') //在安卓中uni-app的数据传输不可用,暂时用原始方法
},
onShow() {
//ios中数据传输可用
this.getbanner()
},
onUnload() {
uni.$off('lunbo')
},
methods: {
getbanner() {
uni.$on('lunbo', (res) => {
this.$nextTick(function() {
this.mybanner = res.mybanner;
})
console.log('获取轮播传值', this.mybanner)
})
},
}
经过测试,成功!!