写点什么

vue2 升级 vue3: TSX Vue 3 Composition API Refs

作者:zhoulujun
  • 2022 年 7 月 30 日
  • 本文字数:4150 字

    阅读完需:约 14 分钟

在 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

发布于: 刚刚阅读数: 3
用户头像

zhoulujun

关注

还未添加个人签名 2021.06.25 加入

15年草根站长,尽在:zhoulujun.cn

评论

发布
暂无评论
vue2升级vue3: TSX Vue 3 Composition API Refs_Vue3_zhoulujun_InfoQ写作社区