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

Svelte轨道拖拽吸附编辑器开发问题:Props无法程序化设置

解决Svelte轨道组件Props无法程序化设置的问题

看起来你在Svelte轨道组件里混淆了组件Props和全局Store数据的职责,这正是导致Props无法程序化设置的核心原因。咱们一步步拆解问题并修复:

问题根源分析

  1. Props与Store数据的职责冲突:你把轨道的所有属性(left/top/connects等)同时作为Props传入,又在组件里直接修改$tracks_arr Store的数据。Svelte的Props是单向数据流,外部传入的Props默认只读,直接修改解构后的Props变量不会生效。
  2. 错误的Props解构方式:你从$props()里解构了包括响应式状态(status/tag)在内的所有属性,但这些属性本质上属于Store管理的轨道数据,不应该作为Props传递。
  3. Store更新方式不规范:直接修改$tracks_arr[selfIndex].top这种对象属性,Svelte可能无法检测到变化,导致组件不更新。

修复方案

第一步:简化组件Props,完全依赖Store数据

组件只需要接收id作为唯一Props,所有轨道数据从tracks_arr Store中获取,这样程序化修改时只需要操作Store,组件会自动响应:

<script>
import { currentID, station_length, to_connect_to } from "./stores.svelte";
import { edit } from "./stores.svelte";
import { tracks_arr } from "./stores.svelte";
import { tracksBorder } from "./stores.svelte";
import { detectSnapping } from "./stores.svelte";
import { somethingIsMoving, width_of_moving } from "./stores.svelte";

// 只接收id作为Props,所有轨道数据从Store获取
let { id } = $props();

// 自动从Store中匹配当前轨道的完整数据,响应Store变化
let track = $derived($tracks_arr.find(v => v.id === id));

// 派生内部标签逻辑(依赖Store中的track数据)
let innerTag = $derived.by(() => {
    if (!track.status) {return track.number}
});

let moving = $state(false);
// ... 后续逻辑修改见下文
</script>

第二步:修正拖拽与吸附逻辑的Store更新方式

所有对轨道属性的修改,都通过不可变更新的方式修改Store(创建新对象/数组),确保Svelte能检测到变化:

// 拖拽移动逻辑
function onMouseMove(e) {
    if (!$edit || !moving) return;

    const trackIndex = $tracks_arr.findIndex(v => v.id === id);
    if (trackIndex === -1) return;

    // 复制原轨道数据,避免直接修改原对象
    let updatedTrack = { ...$tracks_arr[trackIndex] };

    if (updatedTrack.connects.includes("")) {
        updatedTrack.posClass = e.clientX < window.innerWidth/2 ? `connecting0` : `connecting1`;
        updatedTrack.connects = e.clientX < window.innerWidth/2 ? ["","not-defined"] : ["not-defined",""];
        updatedTrack.top = Math.max(e.clientY, $tracksBorder);
        $somethingIsMoving = false;
    } else {
        updatedTrack.left = e.clientX - (window.innerWidth * ((updatedTrack.length/$station_length)/2));
        updatedTrack.top = Math.max(e.clientY, $tracksBorder);
    };

    // 更新Store,触发组件响应
    $tracks_arr = [...$tracks_arr.slice(0, trackIndex), updatedTrack, ...$tracks_arr.slice(trackIndex + 1)];
    $to_connect_to = id;
    $width_of_moving = updatedTrack.width;
};

// 右侧吸附逻辑
function onRightSnap() {
    if ($tracks_arr.length <= 1 || !$somethingIsMoving) return;

    const trackIndex = $tracks_arr.findIndex(v => v.id === $to_connect_to);
    if (trackIndex === -1) return;

    let updatedTrack = { ...$tracks_arr[trackIndex] };
    updatedTrack.top = track.top;
    updatedTrack.left = track.connects.includes("") ? 0 : (track.width + track.left + 4);

    $tracks_arr = [...$tracks_arr.slice(0, trackIndex), updatedTrack, ...$tracks_arr.slice(trackIndex + 1)];
    moving = false;
};

// 左侧吸附逻辑同理修改...

第三步:调整模板中的属性绑定

模板中所有依赖轨道属性的地方,改为直接使用track对象的属性,并修正clientHeight/clientWidth的绑定逻辑:

{#if track.connects.includes("")}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div 
    onpointerdown={onMouseDown} 
    use:detectSnapping 
    onrightsnap={onRightSnap} 
    onleftsnap={onLeftSnap} 
    draggable="false" 
    ondragstart={(e) => e.preventDefault()} 
    id="parent" 
    style=" --length: {track.length}; --station_length: {$station_length};" 
    style:color={track.status ? "red" : "#c2c2c2"} 
    style:top={`${track.top}px`} 
    bind:clientHeight={(height) => {
        // 同步高度到Store
        const index = $tracks_arr.findIndex(v => v.id === id);
        if (index !== -1) {
            $tracks_arr = [...$tracks_arr.slice(0, index), {...$tracks_arr[index], height}, ...$tracks_arr.slice(index+1)];
        }
    }} 
    bind:clientWidth={(width) => {
        // 同步宽度到Store
        const index = $tracks_arr.findIndex(v => v.id === id);
        if (index !== -1) {
            $tracks_arr = [...$tracks_arr.slice(0, index), {...$tracks_arr[index], width}, ...$tracks_arr.slice(index+1)];
        }
    }} 
    class={track.posClass} 
    style:cursor={(moving && $edit ? "grabbing" : "default")}
>
    <div 
        id="track" 
        draggable="false" 
        ondragstart={(e) => e.preventDefault()} 
        style:background-color={track.status ? "red" : "#c2c2c2"}
    ></div><!--主要视觉元素-->
</div>
{/if}

额外注意事项

  1. 添加新轨道时,确保向tracks_arr Store中添加包含所有必要属性的完整对象(id/length/left/top/connects等)。
  2. 永远通过创建新对象/数组的方式更新Store,不要直接修改原对象的属性,这是Svelte响应式系统的要求。
  3. 如果确实需要某些属性从父组件传入,可以单独声明Props,并在组件内部同步到Store,避免数据流混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 06:42:41