You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js 3 render()函数无法在同一父组件中渲染多个Event组件

Vue3 谷歌日历克隆应用渲染问题:同一父元素内重复渲染Event组件仅旧组件更新

我正在使用Vue.js 3开发一款谷歌日历克隆应用。目前遇到的问题是:当尝试使用Vue.js的render()和h()函数在同一父元素内渲染新的Event组件时,只有之前的Event组件会被更新(因修改了其props)。

Calendar.vue (父组件)

<script setup lang="ts">
import { ref, onMounted, computed, watch, h, render, reactive } from "vue";
import Event from "@components/Event.vue";

export interface IEvent {
  id: number;
  title: string;
  description?: string;
  startDate: Date | null;
  endDate: Date | null;
  color?: string;
}

const CELL_HEIGHT = 40;

const eventElementStyles = ref({
  position: "absolute",
  width: "",
  height: CELL_HEIGHT + "px",
  backgroundColor: "rgb(141, 224, 141)",
  zIndex: "100",
  top: "",
  left: "",
});

const columnElementRefs = ref<HTMLDivElement[] | null>(null);
const isClicked = ref<boolean>(false);
const indexOfClickedColumn = ref<number>(-1);

const newEvent = ref<IEvent>({
  id: 1,
  title: "New Event Title",
  description: "New Event Description",
  startDate: null,
  endDate: null,
  color: "rgb(141, 224, 141)",
});

function resetStyles() {
  eventElementStyles.value = {
    position: "absolute",
    width: "",
    height: CELL_HEIGHT + "px",
    backgroundColor: "rgb(141, 224, 141)",
    zIndex: "100",
    top: "",
    left: "",
  };
}

function resetNewEvent() {
  newEvent.value = {
    id: 1,
    title: "New Event Title",
    description: "New Event Description",
    startDate: null,
    endDate: null,
    color: "rgb(141, 224, 141)",
  };
}

function renderComponent(component, parent, props) {
  console.log(props);
  const vueComponent = h(component, props);
  render(vueComponent, parent);
}

function handleMousedown(
  startDate: Date,
  index: number,
  rowIndex: number
): void {
  indexOfClickedColumn.value = index;
  isClicked.value = true;
  resetNewEvent();
  resetStyles();
  newEvent.value.startDate = startDate;
  newEvent.value.endDate = new Date(startDate.getTime() + 60 * 60_000);

  eventElementStyles.value.width = `180.58px`;

  eventElementStyles.value.top = `${rowIndex * CELL_HEIGHT}px`;

  renderComponent(Event, columnElementRefs.value![index], {
    styles: eventElementStyles.value,
    event: newEvent.value,
  });
}

function isPartiallyResizable(colIndex: number): boolean {
  return isClicked.value && colIndex === indexOfClickedColumn.value;
}

function canExtendEventTime(colIndex: number, cellDate: Date): boolean {
  return (
    isPartiallyResizable(colIndex) &&
    cellDate.getTime() >= newEvent.value.endDate!.getTime()
  );
}

function canShrinkEventTime(colIndex: number, cellDate: Date): boolean {
  return (
    isPartiallyResizable(colIndex) &&
    cellDate.getTime() < newEvent.value.endDate!.getTime()
  );
}

function getIncreasedEventHeight(
  oldHeight: number,
  increaseBy: number
): number {
  return oldHeight + increaseBy;
}

function getDecreasedEventHeight(
  oldHeight: number,
  decreaseBy: number
): number {
  return oldHeight - decreaseBy;
}

function handleMouseover(colIndex: number, cellDate: Date): void {
  // increase event element height
  if (canExtendEventTime(colIndex, cellDate)) {
    newEvent.value.endDate = new Date(
      newEvent.value.endDate!.getTime() + 60 * 60_000
    );

    eventElementStyles.value.height =
      getIncreasedEventHeight(
        Number(eventElementStyles.value.height.split("px")[0]),
        CELL_HEIGHT
      ) + "px";
    return;
  }

  // decrease event time
  if (canShrinkEventTime(colIndex, cellDate)) {
    newEvent.value.endDate = new Date(
      newEvent.value.endDate!.getTime() - 60 * 60_000
    );

    eventElementStyles.value.height =
      getDecreasedEventHeight(
        Number(eventElementStyles.value.height.split("px")[0]),
        CELL_HEIGHT
      ) + "px";

    return;
  }
}

function handleMouseup(endDate: Date): void {
  isClicked.value = false;
}
</script>

Event.vue (子组件)

<template>
  <div :style="styles" class="eventContainer">
    <span class="title">(No title) </span>
    <span class="times">{{ time }}</span>
  </div>
</template>

<script setup lang="ts">
import { IEvent } from "@/pages/Calendar.vue";
import { computed } from "vue";

const { styles, event } = defineProps<{
  styles: any;
  event: IEvent;
}>();

const time = computed(() => {
  return `${extractTimeFromDate(event.startDate!)} - ${extractTimeFromDate(
    event.endDate!
  )}`;
});

function extractTimeFromDate(date: Date): string {
  return `${date.getHours().toString().padStart(2, "0")}:${date
    .getMinutes()
    .toString()
    .padStart(2, "0")}`;
}
</script>

我猜想问题在于始终传递相同的props(eventElementStyles和event),但疑惑为何在不同父元素中渲染组件时功能正常,而在同一父元素中渲染多个组件时仅旧组件会被更新。


内容的提问来源于stack exchange,提问作者Achraf FAWZI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:02:02