uni-app框架 微信小程序页面显示正常,但安卓页面样式显示异常

开发者在uni-app项目中遇到search组件样式在模拟器和真机上显示异常的问题,经排查发现是由于在渲染过程中访问undefined的children属性。通过学习和调整代码,解决了Vue渲染时的TypeError,强调了在处理接口返回的复杂数据时使用computed和三元表达式的必要性。
摘要由CSDN通过智能技术生成

问题

今天在继续复习uni-app项目时,使用模拟器运行时,突然发现封装的search组件样式无法正常显示,但是小程序页面又是正常的,打包后真机也是一样的结果。在uni-app的控制台报如下错误:
[Vue warn]: Error in render: “TypeError: Cannot read property ‘children’ of undefined”
TypeError: Cannot read property ‘children’ of undefined

问题截图

小程序界面搜索样式正常

安卓界面运行异常
在这里插入图片描述

问题分析

这是分类页面中的源码,其中my-search组件即为上方图中的固定定位搜索框

<template>
  <view>
    <my-search ></my-search>
    <view class="scrollContainer" >
      <scroll-view scroll-y="true" class="leftScrollContainer" :style="{height: scrollHeight + 'px' }" :scroll-top="leftScrollTop"> 
        <view :class="'firstCateItem ' + (index===currentSelectedIndex? 'active' :'') " v-for="(item1,index) in cateList" :key="index" @click="changeSelectedIndex(index)">
          {{item1.cat_name}}
        </view>
      </scroll-view>
    <scroll-view scroll-y="true" class="rightScrollContainer" :style="{height: scrollHeight + 'px'}" :scroll-top="rightScrollTop">
        <view class="childCateContainer" v-for="(item2,index2) in cateList[currentSelectedIndex].children " :key="index2">
          <view>
           <text class="secondCate">/ {{item2.cat_name}} /</text>
        <view class="thirdCateContainer" >
             <view class="thirdCateItem" v-for="(item3,index3) in item2.children" :key="index3" @click="goToGoodList(item3)">
               <image :src="item3.cat_icon" mode=""></image>
               <text>{{item3.cat_name}}</text>
             </view>
           </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>
<script>
import bus from "@/utils/bus";
  export default {
    data() {
      return {
        scrollHeight:'',
        cateList:[],
        currentSelectedIndex:0,
        rightScrollTop:0,
        leftScrollTop:0,
      };
    },
    created(){
      bus.on('resetScroll', () => {
        this.currentSelectedIndex = 0
        this.leftScrollTop = this.leftScrollTop ? 0 : 1
        this.rightScrollTop = this.rightScrollTop === 0 ? 1 : 0
      })
    },
    onUnload: () => {
      bus.off('resetScroll')
    },
    onLoad() {
      this.getViewUsedHeight()
      this.getCateList()
      console.log("哈哈哈",this.cateList);
    },
    methods:{
      goToGoodList(item3){
        console.log(item3);
        uni.navigateTo({
          url:'/subpkg/goods_list/goods_list?cid=' + item3.cat_id
        })
      },
      changeSelectedIndex(index){
        this.currentSelectedIndex = index
        // this.secondCateList = this.cateList[index].children
        this.rightScrollTop = this.rightScrollTop === 0 ? 1 : 0
      },
      getViewUsedHeight(){
        uni.getSystemInfo({
          success: (res) => {
            console.log("这是获取到的设备信息:",res);
            if(res.errMsg === "getSystemInfo:ok" && res.windowHeight){
              uni.$showMsg("窗口可用高度为"+res.windowHeight)
              this.scrollHeight = res.windowHeight - 60
            }else{
              return uni.$showMsg("获取当前屏幕高度失败!")
            }
          },fail: (err) => {
            console.log("获取屏幕高度失败,信息为:",err);
          }
        })
      },
      async getCateList(){
        const {data:res} = await uni.$http.get('/api/public/v1/categories')
        console.log("这是获取到的分类数据:",res);
        if(res.meta.status !== 200) return uni.$showMsg("没有成功获取到分类数据!")
        this.cateList = res.message
        console.log("获取分类的请求已经结束了!!!");
        // this.secondCateList = this.cateList[0].children
        console.log(this.cateList);
      }
    }
  }
</script>

在控制台中报了这样的错误:

[Vue warn]: Error in render: “TypeError: Cannot read property ‘children’ of undefined”
TypeError: Cannot read property ‘children’ of undefined

之前第一次写的时候也没问题,就没处理,但这次出了问题,只好从这个提示入手。

分析过程:

1.根据提示显然是使用v-for循环页面渲染二级和三级分类使用一级分类cateList对象里面的数据即cateList[currentSelectedIndex].children 时出了问题,但是思考了半天没找到问题所在,我在请求数据的时候就已经将获取到的数据赋值给了data函数中的cateList身上,currentSelectedIndex也在data函数中默认给了定值0。

2.查阅网上资料说是,页面在第一次渲染时数据还没有拿到造成的,使用v-if,判断要渲染的页面数据
是否存在,不存在时不渲染,根据提示我使用了v-if将cateList[currentSelectedIndex].children放在了class="rightScrollContainer"的scroll-view标签上,但是依然无效,报错依旧。

3.后面查阅了项目文档,确实是Vue在第一次渲染页面时拿不到数据导致。于是我修改了使用属性嵌套进行页面渲染的方法,将cateList[currentSelectedIndex].children改为变量名secondCateList声明在data函数中,并在onLoad函数中调用this.getCateList()方法获取分类数据后,将cateList[0].children的值赋值给sendCondList的变量放到页面去渲染,但是报错依旧。

4.后面联想到了Vue的声明周期,并且根据AI的提示,我尝试在onLoad函数中打印请求返回的数据this.cateList值的情况,主要源码和运行截图如下:

    onLoad() {
      this.getViewUsedHeight()
      this.getCateList()
      console.log("这是onLoad函数中当前this.cateList的值:",this.cateList);
    },
    async getCateList(){
        const {data:res} = await uni.$http.get('/api/public/v1/categories')
        console.log("这是获取到的分类数据:",res);
        if(res.meta.status !== 200) return uni.$showMsg("没有成功获取到分类数据!")
        this.cateList = res.message
        console.log("获取分类的请求已经结束了!!!");
      }

在这里插入图片描述
可以看到,在onLoad函数中的请求方法this.getCateList()运行结束之前,onLoad函数体中的最后一行打印语句就已经执行了,这也解释了为什么我在请求函数"结束"之后修改值依然无效,页面渲染报前面提到的错的原因。

解决方法:

知道了原因,我们就可以将二级分类数据secondCateList声明在computed之中,并且使用三元表达式判断它的值是否存在,不存在时返回一个空数组,这样Vue在页面首次加载时就不会去渲染了,报错消失后,这个小程序页面样式正常,安卓样式异常的问题就解决了。

修改后的页面源码和运行截图如下:

<template>
  <view>
    <my-search ></my-search>
    <view class="scrollContainer" >
      <scroll-view scroll-y="true" class="leftScrollContainer" :style="{height: scrollHeight + 'px' }" :scroll-top="leftScrollTop"> 
        <view :class="'firstCateItem ' + (index===currentSelectedIndex? 'active' :'') " v-for="(item1,index) in cateList" :key="index" @click="changeSelectedIndex(index)">
          {{item1.cat_name}}
        </view>
      </scroll-view>
    <scroll-view scroll-y="true" class="rightScrollContainer" :style="{height: scrollHeight + 'px'}" :scroll-top="rightScrollTop">
        <view class="childCateContainer" v-for="(item2,index2) in secondCateList " :key="index2">
          <view>
           <text class="secondCate">/ {{item2.cat_name}} /</text>
        <view class="thirdCateContainer" >
             <view class="thirdCateItem" v-for="(item3,index3) in item2.children" :key="index3" @click="goToGoodList(item3)">
               <image :src="item3.cat_icon" mode=""></image>
               <text>{{item3.cat_name}}</text>
             </view>
           </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
import bus from "@/utils/bus";
  export default {
    data() {
      return {
        scrollHeight:'',
        cateList:[],
        currentSelectedIndex:0,
        rightScrollTop:0,
        leftScrollTop:0,
      };
    },
    computed:{
      secondCateList(){
        //必须要设置一个空数组,在第一次渲染的时候,this.cateList是没有的,再调用它的子属性children会报错,虽然小程序不影响结果,但是安卓上会导致页面样式渲染出现异常
        return this.cateList[this.currentSelectedIndex] ? this.cateList[this.currentSelectedIndex].children : []
      }
    },
    created(){
      bus.on('resetScroll', () => {
        this.currentSelectedIndex = 0
        this.leftScrollTop = this.leftScrollTop ? 0 : 1
        this.rightScrollTop = this.rightScrollTop === 0 ? 1 : 0
      })
    },
    onUnload: () => {
      bus.off('resetScroll')
    },
    onLoad() {
      this.getViewUsedHeight()
      this.getCateList()
      console.log("这是onLoad函数中当前this.cateList的值:",this.cateList);
    },
    methods:{
      goToGoodList(item3){
        console.log(item3);
        uni.navigateTo({
          url:'/subpkg/goods_list/goods_list?cid=' + item3.cat_id
        })
      },
      changeSelectedIndex(index){
        this.currentSelectedIndex = index
        // this.secondCateList = this.cateList[index].children
        this.rightScrollTop = this.rightScrollTop === 0 ? 1 : 0
      },
      getViewUsedHeight(){
        uni.getSystemInfo({
          success: (res) => {
            console.log("这是获取到的设备信息:",res);
            if(res.errMsg === "getSystemInfo:ok" && res.windowHeight){
              uni.$showMsg("窗口可用高度为"+res.windowHeight)
              this.scrollHeight = res.windowHeight - 60
            }else{
              return uni.$showMsg("获取当前屏幕高度失败!")
            }
          },fail: (err) => {
            console.log("获取屏幕高度失败,信息为:",err);
          }
        })
      },
      async getCateList(){
        const {data:res} = await uni.$http.get('/api/public/v1/categories')
        console.log("这是获取到的分类数据:",res);
        if(res.meta.status !== 200) return uni.$showMsg("没有成功获取到分类数据!")
        this.cateList = res.message
        console.log("获取分类的请求已经结束了!!!");
      }
    }
  }
</script>

在这里插入图片描述

总结
刚学完uni-app时,看到有人说这个框架就是一端运行多端报错还有点怀疑,这下不得不信了。不过也有原因在于我当时没有去处理复杂对象中的嵌套数据在页面渲染错误所导致的,同时也重新认识了下onLoad函数体语句和异步请求方法的执行顺序问题。总而言之,在使用接口返回的复杂数据渲染页面时,优先使用computed计算属性,结合三元表达式去判断要渲染的数据源是否已经存在,不存在应给定空数组或对象,以避免一端运行,多端报错的可能。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值