• home > webfront > ECMAS > vue3 >

    vue2升级vue3: TSX Vue 3 Composition API Refs

    Author:zhoulujun Date:

    v-for 中的 Ref 数组在tsx如何获取?this $refs gridlayout $children[index]; 在tsx 实现的解决方案

    在vue2时代,$refs 直接操作子组件

    this.$refs.gridlayout.$children[index];

    虽然不推荐这么做,但是确实非常好用。但是vue2快速迁移到vue3,之前的这个写法因为干进度,不想重构,直接搬迁,发现不行?

    看了下官方的文档:https://v3.cn.vuejs.org/guide/migration/array-refs.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5

    <template>
        <div v-for="item in list" :ref="setItemRef"></div>
    </template>
    <script setup>
        let itemRefs = ref()
        const setItemRef = el => {
          if (el) {
            itemRefs.push(el)
          }
        }
        onBeforeUpdate(() => {
          itemRefs = []
        })
        onUpdated(() => {
          console.log(itemRefs)
        })
    </script>

    注意:

    • itemRefs 不必是数组:它也可以是一个对象,其 ref 可以通过迭代的 key 被设置

    • 如有需要,itemRefs 也可以是响应式的,且可以被侦听。


    在tsx 这个怎么弄呢?

    TSX refs

    网上的大部分教程都是 template 的

    Typing Template Refs

    const el = ref<HTMLInputElement | null>(null)

    普通元素

    Typing Component Template Refs

    import MyModal from './MyModal.vue'
    
    const modal = ref<InstanceType<typeof MyModal> | null>(null)
    
    const openModal = () => {
      modal.value?.open()
    }

    对于子组件,其实和 let timer: ReturnType<typeof setTimeout> = null; 类似。

    如果不进行类型声明,modal 方法无法调用。 需要是as  魔法了

    还有一个需要特别注意,就是子组件内容是暴露出来的,如果是 <script setup> 组件,是无法获取内容的,具体参看:

    vue2升级vue3:单文件组件概述 及 defineExpos/expose https://www.zhoulujun.cn/html/webfront/ECMAScript/vue3/8872.html


    Refs 获取子元素,并操作子元素

    import { defineComponent, nextTick, onBeforeUpdate, onMounted, onUnmounted, ref } from 'vue';
    import ChartWrapper from '@/components/chart-wrapper';
    import props from './props';
    import { AddChartType, PanelModel, IGridPos } from '@/typings';
    
    import { DashboardModule, sortGridPanels } from '@modules/dashboard';
    import { getPluginTypes, handleInitChartPlugin, handleInitDataSource } from '@/utils/dashboard';
    import Loading from '@/components/loading';
    import { GRID_COL_NUM, GRID_ROW_HEIGHT, GRID_ROW_MARGIN, initPanel } from '@/constants';
    import { debounce } from 'lodash';
    import { deepClone } from '@/utils';
    import AddPanel from '@dashboard/grid-panel/add-panel';
    import TabPanel from '@dashboard/tab-panel/index';
    import { GridItem, GridLayout } from 'v3-grid-layout';
    import 'v3-grid-layout/dist/style.css';
    import './index.scss';
    import Row from './Row';
    import EventBus from '@/utils/eventBus';
    
    export default defineComponent({
      name: 'GridPanelPlugin',
      props,
      emits: ['ready', 'delete', 'mounted'],
      setup(props, { emit }) {
        const layout = ref<IGridPos[]>([]);
        const gridLayout = ref<InstanceType<typeof GridLayout>>(null);
        const gridItem = ref<InstanceType<typeof GridItem>[]>([]);
        const dashboardPanel = ref<Element>(null);
        onBeforeUpdate(() => {
          gridItem.value = [];
        });
        function drag(e: DragEvent) {
          const parentRect = dashboardPanel.value.getBoundingClientRect();
          let mouseInGrid = false;
          if (
            ((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right))
            && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
            mouseInGrid = true;
          }
          if (mouseInGrid === true && (layout.value.findIndex(item => item.i === 'drop')) === -1) {
            layout.value.push({
              x: (layout.value.length * 2) % (GRID_COL_NUM || 12),
              y: layout.value.length + (GRID_COL_NUM || 12), // puts it at the bottom
              w: 1,
              h: 1,
              i: 'drop',
            });
          }
          const index = layout.value.findIndex(item => item.i === 'drop');
          if (index !== -1) {
            const el = gridItem.value[index];
            el.dragging.data = { top: mouseXY.y - parentRect.top, left: mouseXY.x - parentRect.left };
            const new_pos = el.calcXY(mouseXY.y - parentRect.top, mouseXY.x - parentRect.left);
            if (mouseInGrid === true) {
              gridLayout.value.dragEvent('dragstart', 'drop', new_pos.x, new_pos.y, 1, 1);
              DragPos.i = String(index);
              DragPos.x = layout.value[index].x;
              DragPos.y = layout.value[index].y;
            }
            if (mouseInGrid === false) {
              gridLayout.value.dragEvent('dragend', 'drop', new_pos.x, new_pos.y, 1, 1);
              layout.value = layout.value.filter(obj => obj.i !== 'drop');
            }
          }
        }
    
        function dragend(e: DragEvent) {
          // const parentRect = document.getElementById('dashboard-panel').getBoundingClientRect();
          const parentRect = dashboardPanel.value.getBoundingClientRect();
          let mouseInGrid = false;
          if (((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right))
            && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
            mouseInGrid = true;
          }
          if (mouseInGrid === true) {
            gridLayout.value.dragEvent('dragend', 'drop', DragPos.x, DragPos.y, 1, 1);
            layout.value = layout.value.filter(obj => obj.i !== 'drop');
            // UNCOMMENT below if you want to add a grid-item
            this.layout.push({
                x: DragPos.x,
                y: DragPos.y,
                w: 1,
                h: 1,
                i: DragPos.i,
            });
            this.$refs.gridLayout.dragEvent('dragend', DragPos.i, DragPos.x,DragPos.y,1,1);
            try {
                this.$refs.gridLayout.$children[this.layout.length].$refs.item.style.display="block";
            } catch {
            }
          }
        }
    
        return {
          gridLayout,
          gridItem,
          dashboardPanel,
          layout,
          movedId,
          inited,
          isLayoutReady,
          isShowEditRowDialog,
          rowForm,
        };
      },
      render() {
        if (!this.inited) {
          return (<Loading/>);
        }
        return (
          <div
            ref='dashboardPanel'
            id='dashboard-panel'
            class={'dashboard-panel flex-1'}>
            <GridLayout
              ref='gridLayout'
              layout={this.layout}
              col-num={GRID_COL_NUM}
              row-height={GRID_ROW_HEIGHT}
              is-resizabl={this.editable}
              is-draggable={this.editable}
              vertical-compact={true}
              use-css-transforms={false}
              margin={GRID_ROW_MARGIN}
              on-layout-updated={this.handleLayoutUpdated}
              on-layout-ready={this.onLayoutReady}
            >
              {
                this.layout.map((item) => {
                  return (
                    <GridItem
                      ref={(el: any) => {
                        if (el) {
                          this.gridItem.push(el);
                        }
                      }}
                      {{...item}}
                     >
                      {chart}
                    </GridItem>
                  );
                })
              }
            </GridLayout>
          </div>
        );
      },
    });


    当然,这个代码是抽离出来的。

    这个vue3-grid-layout,自己写了弄了一版,https://github.com/zhoulujun/vue3-grid-layout





    转载本站文章《vue2升级vue3: TSX Vue 3 Composition API Refs》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue3/8873.html