vue2升级vue3: TSX Vue 3 Composition API Refs
Author:zhoulujun Date:
在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> 组件,是无法获取内容的,具体参看:
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