模板语法基础 #

一、插值表达式 #

1.1 文本插值 #

svelte
<script>
  let name = 'Svelte';
  let count = 42;
</script>

<p>Hello {name}!</p>
<p>Count: {count}</p>

1.2 表达式插值 #

svelte
<script>
  let a = 10;
  let b = 20;
  let items = ['a', 'b', 'c'];
  let user = { name: 'Alice', age: 25 };
</script>

<p>Sum: {a + b}</p>
<p>Product: {a * b}</p>
<p>Items: {items.length}</p>
<p>User: {user.name}, Age: {user.age}</p>
<p>Method: {name.toUpperCase()}</p>
<p>Ternary: {a > b ? 'a大' : 'b大'}</p>

1.3 HTML 插入 #

svelte
<script>
  let htmlContent = '<strong>加粗文本</strong>';
  let markdown = '<a href="#">链接</a>';
</script>

<p>普通文本: {htmlContent}</p>
<p>HTML内容: {@html htmlContent}</p>

{@html markdown}

二、属性绑定 #

2.1 基本属性 #

svelte
<script>
  let src = 'https://example.com/image.jpg';
  let alt = 'Example Image';
  let href = 'https://example.com';
</script>

<img {src} {alt} />
<a {href}>Link</a>

<button disabled>Disabled Button</button>
<input type="text" placeholder="Enter text" />

2.2 动态属性 #

svelte
<script>
  let src = 'https://example.com/image.jpg';
  let disabled = true;
  let className = 'active';
</script>

<img src={src} alt="Dynamic Image" />
<button disabled={disabled}>Disabled</button>
<div class={className}>Dynamic Class</div>

2.3 属性展开 #

svelte
<script>
  let attrs = {
    id: 'my-input',
    class: 'form-control',
    placeholder: 'Enter text',
    disabled: false
  };
</script>

<input {...attrs} />

2.4 style 指令 #

svelte
<script>
  let color = 'red';
  let fontSize = 16;
  let isActive = true;
</script>

<p style="color: {color}; font-size: {fontSize}px;">
  Inline Style
</p>

<p style:color={color} style:font-size="{fontSize}px">
  Style Directive
</p>

<p style:background-color={isActive ? 'green' : 'gray'}>
  Conditional Style
</p>

2.5 class 指令 #

svelte
<script>
  let isActive = true;
  let isDisabled = false;
  let theme = 'dark';
  let size = 'large';
</script>

<button class:active={isActive} class:disabled={isDisabled}>
  Toggle Button
</button>

<div class="box {theme}" class:highlight={isActive}>
  Mixed Classes
</div>

<div class="btn" class="btn-{size}" class:btn-active={isActive}>
  Button
</div>

三、双向绑定 #

3.1 表单输入绑定 #

svelte
<script>
  let text = '';
  let number = 0;
  let email = '';
</script>

<input type="text" bind:value={text} placeholder="Text" />
<input type="number" bind:value={number} />
<input type="email" bind:value={email} />

<p>Text: {text}</p>
<p>Number: {number}</p>
<p>Email: {email}</p>

3.2 复选框绑定 #

svelte
<script>
  let checked = false;
  let hobbies = [];
</script>

<label>
  <input type="checkbox" bind:checked={checked} />
  Agree to terms
</label>

<fieldset>
  <legend>Hobbies</legend>
  <label>
    <input type="checkbox" value="reading" bind:group={hobbies} />
    Reading
  </label>
  <label>
    <input type="checkbox" value="gaming" bind:group={hobbies} />
    Gaming
  </label>
  <label>
    <input type="checkbox" value="sports" bind:group={hobbies} />
    Sports
  </label>
</fieldset>

<p>Checked: {checked}</p>
<p>Hobbies: {hobbies.join(', ')}</p>

3.3 单选框绑定 #

svelte
<script>
  let selected = '';
  let size = 'medium';
</script>

<fieldset>
  <legend>Choose Size</legend>
  <label>
    <input type="radio" value="small" bind:group={size} />
    Small
  </label>
  <label>
    <input type="radio" value="medium" bind:group={size} />
    Medium
  </label>
  <label>
    <input type="radio" value="large" bind:group={size} />
    Large
  </label>
</fieldset>

<p>Selected: {size}</p>

3.4 选择框绑定 #

svelte
<script>
  let selected = '';
  let selectedMultiple = [];
  let options = ['Apple', 'Banana', 'Orange', 'Grape'];
</script>

<select bind:value={selected}>
  <option value="">Select a fruit</option>
  {#each options as option}
    <option value={option}>{option}</option>
  {/each}
</select>

<select multiple bind:value={selectedMultiple}>
  {#each options as option}
    <option value={option}>{option}</option>
  {/each}
</select>

<p>Selected: {selected}</p>
<p>Multiple: {selectedMultiple.join(', ')}</p>

3.5 文本域绑定 #

svelte
<script>
  let content = '';
</script>

<textarea bind:value={content} placeholder="Enter your message"></textarea>

<p>Characters: {content.length}</p>
<pre>{content}</pre>

3.6 内容可编辑元素 #

svelte
<script>
  let html = '<p>Edit this text</p>';
</script>

<div bind:innerHTML={html} contenteditable="true"></div>

<pre>{html}</pre>

四、元素引用 #

4.1 bind:this #

svelte
<script>
  let inputRef;
  let canvasRef;
  
  function focusInput() {
    inputRef.focus();
  }
  
  function drawOnCanvas() {
    const ctx = canvasRef.getContext('2d');
    ctx.fillStyle = 'red';
    ctx.fillRect(10, 10, 50, 50);
  }
</script>

<input bind:this={inputRef} placeholder="Click button to focus" />
<button onclick={focusInput}>Focus Input</button>

<canvas bind:this={canvasRef} width="200" height="200"></canvas>
<button onclick={drawOnCanvas}>Draw</button>

4.2 组件引用 #

svelte
<script>
  import Modal from './Modal.svelte';
  
  let modalRef;
  
  function openModal() {
    modalRef.open();
  }
</script>

<button onclick={openModal}>Open Modal</button>
<Modal bind:this={modalRef} />

五、媒体绑定 #

5.1 音频/视频属性 #

svelte
<script>
  let audioRef;
  let currentTime = 0;
  let duration = 0;
  let paused = true;
  let volume = 1;
</script>

<audio 
  bind:this={audioRef}
  bind:currentTime
  bind:duration
  bind:paused
  bind:volume
  src="audio.mp3"
/>

<p>Current: {currentTime.toFixed(2)}s</p>
<p>Duration: {duration.toFixed(2)}s</p>
<p>Status: {paused ? 'Paused' : 'Playing'}</p>
<p>Volume: {(volume * 100).toFixed(0)}%</p>

<button onclick={() => audioRef.play()}>Play</button>
<button onclick={() => audioRef.pause()}>Pause</button>

5.2 视频尺寸 #

svelte
<script>
  let videoWidth = 0;
  let videoHeight = 0;
</script>

<video 
  bind:videoWidth 
  bind:videoHeight 
  src="video.mp4"
/>

<p>Size: {videoWidth}x{videoHeight}</p>

六、尺寸绑定 #

6.1 元素尺寸 #

svelte
<script>
  let clientWidth = 0;
  let clientHeight = 0;
  let offsetWidth = 0;
  let offsetHeight = 0;
</script>

<div 
  bind:clientWidth
  bind:clientHeight
  bind:offsetWidth
  bind:offsetHeight
  class="box"
>
  Resize me
</div>

<p>Client: {clientWidth}x{clientHeight}</p>
<p>Offset: {offsetWidth}x{offsetHeight}</p>

<style>
  .box {
    resize: both;
    overflow: auto;
    width: 200px;
    height: 100px;
    border: 1px solid #ccc;
  }
</style>

6.2 窗口绑定 #

svelte
<script>
  let innerWidth = 0;
  let innerHeight = 0;
  let outerWidth = 0;
  let outerHeight = 0;
  let scrollX = 0;
  let scrollY = 0;
</script>

<svelte:window 
  bind:innerWidth
  bind:innerHeight
  bind:outerWidth
  bind:outerHeight
  bind:scrollX
  bind:scrollY
/>

<p>Inner: {innerWidth}x{innerHeight}</p>
<p>Outer: {outerWidth}x{outerHeight}</p>
<p>Scroll: ({scrollX}, {scrollY})</p>

七、表单修饰符 #

7.1 事件修饰符 #

svelte
<script>
  function handleSubmit() {
    console.log('Form submitted');
  }
  
  function handleClick() {
    console.log('Button clicked');
  }
</script>

<form onsubmit|preventDefault={handleSubmit}>
  <button type="submit">Submit</button>
</form>

<button onclick|once={handleClick}>
  Click once
</button>

<div onclick={() => console.log('div')}>
  <button onclick|stopPropagation={() => console.log('button')}>
    Click me
  </button>
</div>

7.2 修饰符列表 #

修饰符 说明
preventDefault 调用 event.preventDefault()
stopPropagation 调用 event.stopPropagation()
stopImmediatePropagation 调用 event.stopImmediatePropagation()
capture 使用捕获模式
once 只触发一次
self 只在 event.target 是元素本身时触发
passive 设置 passive 为 true
nonpassive 设置 passive 为 false
trusted 只在 event.isTrusted 为 true 时触发

7.3 组合修饰符 #

svelte
<button onclick|preventDefault|stopPropagation|once={handleClick}>
  Multiple Modifiers
</button>

八、完整示例:表单组件 #

svelte
<script>
  let form = $state({
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
    gender: '',
    hobbies: [],
    country: '',
    bio: '',
    agree: false
  });
  
  let errors = $state({});
  
  let passwordStrength = $derived(() => {
    const pwd = form.password;
    if (!pwd) return '';
    if (pwd.length < 6) return 'weak';
    if (pwd.length < 10) return 'medium';
    return 'strong';
  });
  
  function validate() {
    const newErrors = {};
    
    if (!form.username) newErrors.username = '用户名必填';
    if (!form.email) newErrors.email = '邮箱必填';
    if (!form.password) newErrors.password = '密码必填';
    if (form.password !== form.confirmPassword) {
      newErrors.confirmPassword = '密码不匹配';
    }
    if (!form.agree) newErrors.agree = '必须同意条款';
    
    errors = newErrors;
    return Object.keys(newErrors).length === 0;
  }
  
  function handleSubmit(e) {
    if (validate()) {
      console.log('Form submitted:', form);
    }
  }
</script>

<form onsubmit|preventDefault={handleSubmit}>
  <div>
    <label>Username</label>
    <input type="text" bind:value={form.username} />
    {#if errors.username}
      <span class="error">{errors.username}</span>
    {/if}
  </div>
  
  <div>
    <label>Email</label>
    <input type="email" bind:value={form.email} />
    {#if errors.email}
      <span class="error">{errors.email}</span>
    {/if}
  </div>
  
  <div>
    <label>Password</label>
    <input type="password" bind:value={form.password} />
    <span class="strength {passwordStrength()}">
      {passwordStrength()}
    </span>
    {#if errors.password}
      <span class="error">{errors.password}</span>
    {/if}
  </div>
  
  <div>
    <label>Confirm Password</label>
    <input type="password" bind:value={form.confirmPassword} />
    {#if errors.confirmPassword}
      <span class="error">{errors.confirmPassword}</span>
    {/if}
  </div>
  
  <fieldset>
    <legend>Gender</legend>
    <label>
      <input type="radio" value="male" bind:group={form.gender} />
      Male
    </label>
    <label>
      <input type="radio" value="female" bind:group={form.gender} />
      Female
    </label>
  </fieldset>
  
  <fieldset>
    <legend>Hobbies</legend>
    <label>
      <input type="checkbox" value="reading" bind:group={form.hobbies} />
      Reading
    </label>
    <label>
      <input type="checkbox" value="gaming" bind:group={form.hobbies} />
      Gaming
    </label>
    <label>
      <input type="checkbox" value="sports" bind:group={form.hobbies} />
      Sports
    </label>
  </fieldset>
  
  <div>
    <label>Country</label>
    <select bind:value={form.country}>
      <option value="">Select</option>
      <option value="cn">China</option>
      <option value="us">USA</option>
      <option value="uk">UK</option>
    </select>
  </div>
  
  <div>
    <label>Bio</label>
    <textarea bind:value={form.bio}></textarea>
  </div>
  
  <div>
    <label>
      <input type="checkbox" bind:checked={form.agree} />
      I agree to the terms
    </label>
    {#if errors.agree}
      <span class="error">{errors.agree}</span>
    {/if}
  </div>
  
  <button type="submit">Register</button>
</form>

<style>
  form {
    max-width: 400px;
    margin: 0 auto;
  }
  
  div {
    margin-bottom: 1rem;
  }
  
  label {
    display: block;
    margin-bottom: 0.25rem;
  }
  
  input, select, textarea {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
  }
  
  .error {
    color: red;
    font-size: 0.8rem;
  }
  
  .strength {
    font-size: 0.8rem;
    margin-left: 0.5rem;
  }
  
  .strength.weak { color: red; }
  .strength.medium { color: orange; }
  .strength.strong { color: green; }
</style>

九、总结 #

语法 说明
{expression} 插值表达式
{@html html} 插入 HTML
{prop} 属性简写
bind:value 双向绑定
bind:this 元素引用
class:name 条件类名
style:name 条件样式
`on:event modifier`

模板语法要点:

  • 使用 {} 进行插值
  • bind: 实现双向绑定
  • class:style: 简化条件样式
  • 使用修饰符简化事件处理
  • bind:this 获取元素引用
最后更新:2026-03-28