Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问clear审计未展示

svelteSvelte 开发

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

212

周安装

9

GitHub Stars

公开资料未说明

下载量

74
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:svelte(Svelte 开发)
来源仓库:https://github.com/fil512/upship
仓库路径:skills/svelte
安装命令:
npx skills add fil512/upship --skill "svelte"
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

AgentSkills.tonpx skills
npx skills add fil512/upship --skill "svelte"

简介

辅助生成 Svelte 框架下的前端组件与样式代码。

  • 适用于快速搭建交互式单页应用原型开发。svelte 属于研究检索类 Skill,可作为该场景下的辅助能力补充。
  • 遵循现代前端工程化最佳实践组织项目结构。
  • 安装命令:npx skills add fil512/upship --skill "svelte"。
  • 建议配合 Vite 或 SvelteKit 构建工具链使用。

SKILL.md

Svelte Skill

Overview

This skill provides expertise for building reactive web applications with Svelte. It covers component architecture, the reactivity system, stores for state management, real-time updates with WebSockets, and SvelteKit for full-stack applications.

Why Svelte

Comparison with Vanilla JS

AspectVanilla JSSvelte
ReactivityManual DOM updatesAutomatic - count++ just works
ComponentsTemplate stringsSingle-file components
StateGlobal variablesStores with subscriptions
Bundle size0kb (but more code)~2kb runtime
Learning curveNoneGentle (closest to vanilla)

Key Benefits

  1. Compile-time magic - No virtual DOM, compiles to efficient vanilla JS
  2. Less boilerplate - let count = 0 is reactive by default
  3. Built-in transitions - transition:fade for animations
  4. Scoped CSS - Styles in components don't leak
  5. Stores - Simple reactive state that works with WebSockets

Core Concepts

Reactivity

Svelte's reactivity is based on assignments:

<script>
  let count = 0;

  // Reactive statements run when dependencies change
  $: doubled = count * 2;
  $: console.log('count changed to', count);

  function increment() {
    count++;  // This triggers UI update automatically
  }
</script>

<button on:click={increment}>
  Count: {count} (doubled: {doubled})
</button>

Array/Object Reactivity

Svelte tracks assignments, not mutations:

<script>
  let items = ['a', 'b', 'c'];

  // BAD: mutation doesn't trigger update
  function addBad() {
    items.push('d');  // UI won't update!
  }

  // GOOD: reassignment triggers update
  function addGood() {
    items = [...items, 'd'];  // UI updates
  }

  // Also works: assign back to self
  function addAlso() {
    items.push('d');
    items = items;  // Triggers update
  }
</script>

Component Structure

Single-file components with script, markup, and style:

<!-- PlayerCard.svelte -->
<script>
  // Props with defaults
  export let name;
  export let cash = 0;
  export let isActive = false;

  // Local state
  let expanded = false;

  // Event dispatcher
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();

  function handleClick() {
    dispatch('select', { name });
  }
</script>

<div class="player-card" class:active={isActive} on:click={handleClick}>
  <h3>{name}</h3>
  <p>Cash: £{cash}</p>

  {#if expanded}
    <slot />  <!-- Nested content goes here -->
  {/if}
</div>

<style>
  /* Scoped to this component only */
  .player-card {
    padding: 1rem;
    border: 2px solid #333;
    border-radius: 8px;
  }

  .player-card.active {
    border-color: #4a9eff;
    background: rgba(74, 158, 255, 0.1);
  }
</style>

Using Components

<!-- Game.svelte -->
<script>
  import PlayerCard from './PlayerCard.svelte';

  let players = [
    { id: 1, name: 'Germany', cash: 15 },
    { id: 2, name: 'Britain', cash: 12 }
  ];
  let activePlayerId = 1;

  function handleSelect(event) {
    console.log('Selected:', event.detail.name);
  }
</script>

{#each players as player (player.id)}
  <PlayerCard
    name={player.name}
    cash={player.cash}
    isActive={player.id === activePlayerId}
    on:select={handleSelect}
  >
    <p>Ships: {player.ships?.length ?? 0}</p>
  </PlayerCard>
{/each}

Stores

Writable Stores

For shared state across components:

// stores/gameState.js
import { writable, derived } from 'svelte/store';

// Create a writable store
export const gameState = writable(null);

// Derived stores compute from other stores
export const currentPlayer = derived(
  gameState,
  $state => $state?.players?.[$state?.currentPlayerIndex]
);

export const isMyTurn = derived(
  [gameState, currentPlayer],
  ([$state, $player]) => $player?.id === myPlayerId
);

// Helper functions to update state
export function updateGameState(newState) {
  gameState.set(newState);
}

export function updatePlayer(playerId, changes) {
  gameState.update(state => ({
    ...state,
    players: {
      ...state.players,
      [playerId]: { ...state.players[playerId], ...changes }
    }
  }));
}

Using Stores in Components

<script>
  import { gameState, currentPlayer, isMyTurn } from './stores/gameState.js';

  // $ prefix auto-subscribes to store
  $: console.log('Game state updated:', $gameState);
</script>

<div>
  <h2>Turn: {$gameState?.turn}</h2>
  <p>Current player: {$currentPlayer?.name}</p>

  {#if $isMyTurn}
    <button>Take Action</button>
  {:else}
    <p>Waiting for {$currentPlayer?.name}...</p>
  {/if}
</div>

Custom Stores

Create stores with custom methods:

// stores/player.js
import { writable } from 'svelte/store';

function createPlayerStore() {
  const { subscribe, set, update } = writable({
    cash: 0,
    officers: 0,
    engineers: 0,
    gasCubes: { hydrogen: 0, helium: 0 }
  });

  return {
    subscribe,
    set,
    reset: () => set({ cash: 0, officers: 0, engineers: 0, gasCubes: { hydrogen: 0, helium: 0 } }),
    addCash: (amount) => update(p => ({ ...p, cash: p.cash + amount })),
    spendCash: (amount) => update(p => ({ ...p, cash: p.cash - amount })),
    buyGas: (type, amount) => update(p => ({
      ...p,
      gasCubes: { ...p.gasCubes, [type]: p.gasCubes[type] + amount }
    }))
  };
}

export const player = createPlayerStore();

Real-Time Updates with WebSocket

Socket Store Pattern

// stores/socket.js
import { writable, get } from 'svelte/store';
import { io } from 'socket.io-client';
import { gameState } from './gameState.js';

export const connected = writable(false);
export const connectionError = writable(null);

let socket = null;

export function connect(serverUrl) {
  socket = io(serverUrl, {
    reconnection: true,
    reconnectionAttempts: 10,
    reconnectionDelay: 1000
  });

  socket.on('connect', () => {
    connected.set(true);
    connectionError.set(null);
    console.log('Connected to server');
  });

  socket.on('disconnect', () => {
    connected.set(false);
  });

  socket.on('connect_error', (error) => {
    connectionError.set(error.message);
  });

  // Game state updates from server
  socket.on('state-update', (newState) => {
    gameState.set(newState);
  });

  socket.on('state-sync', (fullState) => {
    gameState.set(fullState);
  });

  return socket;
}

export function joinGame(gameId, playerId) {
  if (socket) {
    socket.emit('join-game', { gameId, playerId });
  }
}

export function sendAction(action) {
  if (socket) {
    socket.emit('game-action', action);
  }
}

export function disconnect() {
  if (socket) {
    socket.disconnect();
    socket = null;
    connected.set(false);
  }
}

Using Socket in Components

<!-- Game.svelte -->
<script>
  import { onMount, onDestroy } from 'svelte';
  import { connect, joinGame, sendAction, disconnect, connected } from './stores/socket.js';
  import { gameState, currentPlayer } from './stores/gameState.js';

  export let gameId;
  export let playerId;

  onMount(() => {
    connect('http://localhost:3000');
    joinGame(gameId, playerId);
  });

  onDestroy(() => {
    disconnect();
  });

  function handleEndTurn() {
    sendAction({ type: 'END_TURN' });
  }
</script>

{#if !$connected}
  <div class="connecting">Connecting to server...</div>
{:else if !$gameState}
  <div class="loading">Loading game state...</div>
{:else}
  <div class="game">
    <h1>Turn {$gameState.turn}</h1>
    <p>Current player: {$currentPlayer?.name}</p>

    <button on:click={handleEndTurn}>End Turn</button>
  </div>
{/if}

Conditional Rendering and Loops

If/Else Blocks

{#if loading}
  <Spinner />
{:else if error}
  <ErrorMessage {error} />
{:else if items.length === 0}
  <EmptyState />
{:else}
  <ItemList {items} />
{/if}

Each Blocks with Keys

<!-- Key is crucial for list updates -->
{#each ships as ship (ship.id)}
  <Ship {...ship} on:launch={handleLaunch} />
{:else}
  <p>No ships in hangar</p>
{/each}

Await Blocks

{#await fetchGameState()}
  <p>Loading...</p>
{:then state}
  <GameBoard {state} />
{:catch error}
  <p>Error: {error.message}</p>
{/await}

Transitions and Animations

Built-in Transitions

<script>
  import { fade, fly, slide, scale } from 'svelte/transition';
  import { flip } from 'svelte/animate';

  let visible = true;
  let items = [];
</script>

{#if visible}
  <div transition:fade={{ duration: 300 }}>
    Fades in and out
  </div>
{/if}

<!-- One-way transitions -->
{#if showNotification}
  <div in:fly={{ y: -50, duration: 300 }} out:fade>
    Notification!
  </div>
{/if}

<!-- Animate list reordering -->
{#each items as item (item.id)}
  <div animate:flip={{ duration: 300 }}>
    {item.name}
  </div>
{/each}

Custom Transitions

<script>
  function whoosh(node, { duration = 400 }) {
    return {
      duration,
      css: (t) => {
        const eased = t;  // Could use easing function
        return `
          transform: scale(${eased}) rotate(${(1 - eased) * 360}deg);
          opacity: ${eased};
        `;
      }
    };
  }
</script>

{#if show}
  <div transition:whoosh>Whoooosh!</div>
{/if}

Event Handling

DOM Events

<button on:click={handleClick}>Click</button>
<button on:click={() => count++}>Inline</button>

<!-- Event modifiers -->
<button on:click|preventDefault={submit}>Submit</button>
<button on:click|stopPropagation={handleClick}>Stop Bubble</button>
<button on:click|once={init}>Initialize Once</button>
<form on:submit|preventDefault={handleSubmit}>...</form>

<!-- Keyboard events -->
<input on:keydown|self={(e) => e.key === 'Enter' && submit()} />

Component Events

<!-- Child component -->
<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();

  function handleSelect() {
    dispatch('select', { id: item.id, name: item.name });
  }
</script>

<!-- Parent component -->
<Card on:select={(e) => console.log(e.detail.name)} />

<!-- Forward DOM events -->
<button on:click>
  This click bubbles to parent
</button>

Bindings

Two-Way Binding

<script>
  let name = '';
  let agreed = false;
  let selected = 'a';
  let quantity = 1;
</script>

<input bind:value={name} />
<input type="checkbox" bind:checked={agreed} />
<input type="number" bind:value={quantity} min="1" max="10" />

<select bind:value={selected}>
  <option value="a">Option A</option>
  <option value="b">Option B</option>
</select>

<!-- Group binding -->
<script>
  let selectedColors = [];
</script>
{#each ['red', 'green', 'blue'] as color}
  <label>
    <input type="checkbox" bind:group={selectedColors} value={color} />
    {color}
  </label>
{/each}

Element Bindings

<script>
  let inputElement;
  let divWidth;
  let divHeight;
</script>

<input bind:this={inputElement} />
<button on:click={() => inputElement.focus()}>Focus</button>

<div bind:clientWidth={divWidth} bind:clientHeight={divHeight}>
  Size: {divWidth}x{divHeight}
</div>

SvelteKit

Project Structure

my-app/
├── src/
│   ├── lib/           # Shared components and utilities
│   │   ├── components/
│   │   │   ├── PlayerCard.svelte
│   │   │   └── GameBoard.svelte
│   │   ├── stores/
│   │   │   ├── gameState.js
│   │   │   └── socket.js
│   │   └── utils/
│   ├── routes/        # File-based routing
│   │   ├── +page.svelte       # /
│   │   ├── +layout.svelte     # Shared layout
│   │   ├── game/
│   │   │   ├── +page.svelte   # /game
│   │   │   └── [id]/
│   │   │       └── +page.svelte  # /game/:id
│   │   └── api/       # API routes
│   │       └── games/
│   │           └── +server.js
│   ├── app.html
│   └── app.css
├── static/            # Static assets
├── svelte.config.js
└── package.json

Page Load Functions

// routes/game/[id]/+page.js
export async function load({ params, fetch }) {
  const response = await fetch(`/api/games/${params.id}`);

  if (!response.ok) {
    throw error(404, 'Game not found');
  }

  const game = await response.json();

  return {
    game,
    gameId: params.id
  };
}
<!-- routes/game/[id]/+page.svelte -->
<script>
  export let data;  // From load function

  $: ({ game, gameId } = data);
</script>

<h1>Game: {game.name}</h1>

API Routes

// routes/api/games/+server.js
import { json } from '@sveltejs/kit';

export async function GET({ url }) {
  const games = await db.getGames();
  return json(games);
}

export async function POST({ request }) {
  const { name, playerId } = await request.json();
  const game = await db.createGame(name, playerId);
  return json(game, { status: 201 });
}

TypeScript Support

<script lang="ts">
  interface Player {
    id: string;
    name: string;
    cash: number;
    faction: 'germany' | 'britain' | 'usa' | 'italy';
  }

  interface Ship {
    id: string;
    name: string;
    status: 'hangar' | 'on_route' | 'destroyed';
  }

  export let player: Player;
  export let ships: Ship[] = [];

  let selectedShip: Ship | null = null;

  function selectShip(ship: Ship): void {
    selectedShip = ship;
  }
</script>

Migration from Vanilla JS

Before (Vanilla)

// Vanilla JS pattern
let gameState = null;
const stateElement = document.getElementById('game-state');

function render() {
  stateElement.innerHTML = `
    <h2>Turn ${gameState.turn}</h2>
    <p>Cash: £${gameState.players[userId].cash}</p>
    ${gameState.players[userId].ships.map(ship => `
      <div class="ship">${ship.name}</div>
    `).join('')}
  `;
}

async function fetchState() {
  const res = await fetch(`/api/state/${gameId}`);
  gameState = await res.json();
  render();
}

// Poll every 2 seconds
setInterval(fetchState, 2000);

After (Svelte)

<script>
  import { onMount } from 'svelte';
  import { gameState } from './stores/gameState.js';
  import { connect, joinGame } from './stores/socket.js';

  export let gameId;
  export let userId;

  $: player = $gameState?.players?.[userId];

  onMount(() => {
    connect('http://localhost:3000');
    joinGame(gameId, userId);
  });
</script>

{#if $gameState}
  <h2>Turn {$gameState.turn}</h2>
  <p>Cash: £{player.cash}</p>

  {#each player.ships as ship (ship.id)}
    <div class="ship">{ship.name}</div>
  {/each}
{:else}
  <p>Loading...</p>
{/if}

Best Practices

Component Organization

lib/components/
├── ui/               # Generic reusable components
│   ├── Button.svelte
│   ├── Modal.svelte
│   └── Tooltip.svelte
├── game/             # Game-specific components
│   ├── GameBoard.svelte
│   ├── PlayerPanel.svelte
│   └── ShipCard.svelte
└── layout/           # Layout components
    ├── Header.svelte
    └── Sidebar.svelte

Props and Events Naming

<script>
  // Props: noun or adjective
  export let player;
  export let isActive = false;
  export let maxItems = 10;

  // Events: on:verbNoun pattern
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();

  // dispatch('select'), dispatch('launch'), dispatch('close')
</script>

<!-- Usage follows same pattern -->
<ShipCard
  ship={myShip}
  isSelected={selectedId === myShip.id}
  on:launch={handleLaunch}
  on:select={handleSelect}
/>

Reactive Statement Order

<script>
  export let items;
  export let filter;

  // Derived values first (these update when deps change)
  $: filteredItems = items.filter(i => i.type === filter);
  $: totalCount = filteredItems.length;

  // Side effects last (log, dispatch events, etc.)
  $: if (totalCount === 0) {
    console.log('No items match filter');
  }
</script>

Avoiding Common Mistakes

<script>
  // MISTAKE 1: Mutating without reassignment
  let items = [1, 2, 3];
  items.push(4);  // Won't trigger update!
  items = [...items, 4];  // Correct

  // MISTAKE 2: Destructuring props loses reactivity
  export let player;
  const { name } = player;  // name won't update!
  $: ({ name } = player);   // Reactive destructure

  // MISTAKE 3: Not using key in each
  {#each items as item}  // Bad for updates
  {#each items as item (item.id)}  // Good

  // MISTAKE 4: Store in template without $
  import { count } from './stores';
  // {count} shows store object, not value
  // {$count} shows the value
</script>

Testing Svelte Components

// PlayerCard.test.js
import { render, fireEvent } from '@testing-library/svelte';
import PlayerCard from './PlayerCard.svelte';

describe('PlayerCard', () => {
  it('displays player name and cash', () => {
    const { getByText } = render(PlayerCard, {
      props: { name: 'Germany', cash: 15 }
    });

    expect(getByText('Germany')).toBeInTheDocument();
    expect(getByText('Cash: £15')).toBeInTheDocument();
  });

  it('dispatches select event on click', async () => {
    const { getByRole, component } = render(PlayerCard, {
      props: { name: 'Germany', cash: 15 }
    });

    const selectHandler = vi.fn();
    component.$on('select', selectHandler);

    await fireEvent.click(getByRole('button'));

    expect(selectHandler).toHaveBeenCalledWith(
      expect.objectContaining({
        detail: { name: 'Germany' }
      })
    );
  });
});

When This Skill Activates

Use this skill when:

  • Building Svelte components
  • Managing state with Svelte stores
  • Implementing real-time updates via WebSocket
  • Migrating vanilla JS to Svelte
  • Setting up SvelteKit projects
  • Adding TypeScript to Svelte
  • Creating reactive UI patterns
  • Optimizing Svelte performance

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

windsurf

29.55%
按下载量换算22

OpenCode

26.17%
按下载量换算19

weavefox

17.18%
按下载量换算13

Codex

12.78%
按下载量换算9

Claude Code

7.73%
按下载量换算6

Antigravity

4.11%
按下载量换算3

安全审计

暂无安全审计结果可展示。

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills