vant的picker组件数据更新时视图却没有更新???

1、背景

最近项目有个需求,需要使用vant的picker选择器,并且搭配弹出层使用,并且picker的数据是异步获取的,但是在测试的过程中,数据已经正确获取到,页面也实现了响应式,但是picker选择器的数据却没有更新,这是为什么呢???

  • 代码:
    • html
      <van-popup position="bottom" v-model:show="showPicker">
            <!-- 
              loading:是否显示加载状态,默认为false
              columns:对象数组,配置每一列显示的数据
              value-key已经弃用,所以需要columns-field-names自定义 Columns 的结构
              show-toolbar:是否显示顶部栏,默认为true
              confirm:点击完成按钮时触发
              cancel:点击取消按钮时触发
             -->
            {{ sourceData }}
            <van-picker
              default-index="0"
              :loading="loading"
              :columns="sourceData"
              :columns-field-names="customFieldName"
              show-toolbar
              @confirm="onConfirm"
              @cancel="onCancel"
            />
          </van-popup>
    • js:
      let res = await initSelectData({
            data: {},
            method: 'GET',
            url: codeId
      });
      // sourceData = res.data; //直接赋值丢失了响应性
      res.data
          ? res.data.forEach(el => {
              sourceData.push(el);
             })
          : '';
  • 加载数据的现象:

         

  • 加载数据成功后的现象:

           

2、分析

官网地址:https://vant-contrib.gitee.io/vant/v3/#/zh-CN/picker

刚开始的时候我认为是vue的响应式数据问题引起的,后来在popup中打印了数据源sourceData之后,发现页面的数据已经响应式更新了,但是picker中的下拉选项数据却并没有发生变化,所以接下来我查阅了一下vant-picker的官方文档,发现picker实例上有一个方法 setColumnValues 可以设置对应列中所有的选项,所以我给picker设置了一个ref

  •  html
    <van-picker
            default-index="0"
            ref="picker"
            :loading="loading"
            :columns="sourceData"
            :columns-field-names="customFieldName"
            show-toolbar
            @confirm="onConfirm"
            @cancel="onCancel"
          />
  • js
    /**
         * @description: onMounted 可以用来加载页面的初始化数据
         * @author: wangxinghua1
         */
        onMounted(async () => {
          console.log(picker.value, '.....');
        });
  • 问题:在onMounted中的结果为:null '.....'。  这就导致页面加载选择器数据的时候,没有办法获取到实例,而导致调用 setColumnValues 报错,这是因为popup弹层在打开前并没有提前渲染到页面上(dom加载时并没加载popup),所以导致ref获取不到它里面的picker选择器,解决方法:
    <van-popup
      :lazy-render="false"
    /> 
  • 最终结果是
    • html
      <!-- 
            lazy-render:是否在显示弹层时才渲染节点,默认为true,防止popup弹出层
                        在打开前并没有提前渲染到页面上(dom加载时并没加载popup),
                        所以导致ref获取不到它里面的picker选择器(picker.value),
                        从而使得picker.value.setColumnValues报错
           -->
          <van-popup :lazy-render="false" position="bottom" v-model:show="showPicker">
            <!-- 
              loading:是否显示加载状态,默认为false
              columns:对象数组,配置每一列显示的数据
              value-key已经弃用,所以需要columns-field-names自定义 Columns 的结构
              show-toolbar:是否显示顶部栏,默认为true
              confirm:点击完成按钮时触发
              cancel:点击取消按钮时触发
             -->
            {{ sourceData }}
            <van-picker
              default-index="0"
              ref="picker"
              :loading="loading"
              :columns="sourceData"
              :columns-field-names="customFieldName"
              show-toolbar
              @confirm="onConfirm"
              @cancel="onCancel"
            />
          </van-popup>
    • js
      // 定义是否显示弹出层
          let showPicker = ref(false);
      
          // 定义选择器的数据
          let sourceData = reactive([]);
      
          // 选择器数据是异步获取的,可以通过 loading 属性显示加载提示
          let loading = ref(false);
      
          // 获取picher的dom节点,即picker的实例
          let picker = ref(null);
      
          /**
           * @description: onMounted 可以用来加载页面的初始化数据
           * @author: wangxinghua1
           */
          onMounted(async () => {
            // 当选择器的数据来源于后端时,判断是否有请求地址,进行初始化数据
            if (codeId) {
              loading.value = true;
              await loadData();
            }
          });
      
          /**
           * @description: loadData  获取select的数据
           * @author: wangxinghua1
           */
          const loadData = async () => {
            try {
              let res = await initSelectData({
                data: {},
                method: 'GET',
                url: codeId
              });
              // sourceData = res.data; //直接赋值丢失了响应性
              res.data
                ? res.data.forEach(el => {
                    sourceData.push(el);
                  })
                : '';
              res.data ? (loading.value = false) : (loading.value = false);
              console.log('9999999', picker.value);
              picker.value.setColumnValues(0, sourceData);
              console.log('initSelectData', res, sourceData);
            } catch (e) {
              console.log('select-loaddata', e.errmsg);
            }
          };

3、vue3使用ref的步骤

  1. 给元素添加ref属性<div ref="box"></div>
  2. 在setup函数中,可以使用ref函数,用于创建一个响应式数据const box = ref(null)
  3. 在setup函数中,使用return返回box数据
  4. 在onMounted函数里面访问
lazy  [ˈleɪzi]  详细X
基本翻译
adj. 懒惰的;懒洋洋的;怠惰的;慢吞吞的
n. (Lazy)人名;(德)拉齐
网络释义
lazy: 懒惰的
Lazy Susan: 餐桌转盘
Lazy evaluation: 惰性求值

北栀女孩儿
原文地址:https://www.cnblogs.com/wxh0929/p/15437249.html