从0-1Vite + Svelte

1.前言

node版本:v20.11.1 
编辑器:trae
最终实现一个管理后台项目
https://gitee.com/tcyyyy/vite---svelte 后台项目仓库

2.学习目录:

a.创建 vite + svelte 应用
b.认识 svelte
c.学习svelte语法

2.创建 vite + svelte 应用

npx create-vite@latest . --template svelte --yes

 命令简单解析 .:路径参数在当前目录初始化项目  --template svelte:模板选择  --yes:自动应答 跳过所有交互式问答

 出现上图就是执行成功了
 咱们按照提示的 命令执行完毕 先把项目跑起来


上图 就是代表项目能正常跑起来了

 这个时候咱们看代码是没有着色的
 可以在插件市场安装 

Svelte for VS Code

 以上就是安装 Svelte for VS Code 之后的代码着色
 以上就是搭建的全部过程了

3.代码学习

3.1 从 hello svelte! 入门😁

上图就是 修改过后的 app.svelte 文件

 上图就是页面展示 字段

<script>
</script>

<h1>hello svelte!</h1>

<style>
</style>
script 标签里面 是我们写js代码的地方
style 是我们写样式的
html 代码 写在两个标签中间即可

3.2 声明变量添加数据

<script>
  let name = 'svelte'
</script>

<h1>hello {name}!</h1>

<style>
</style>

 页面还是 咱们刚才所见

3.3动态属性

<script>
  let name = 'svelte'
  let src = '/src/assets/image/svelte.png';
</script>

<div>
  <img {src} alt="{name} dances." />
  <h1>hello {name}!</h1>
</div>

<style>
</style>

 以上代码 页面展示

3.4声明组件

创建一个 svelte 文件 写入描述

 上图就是新建的一个组件

<script>
  import ONE from './component/one.svelte'
  let name = 'svelte'
  let src = '/src/assets/image/svelte.png';
</script>

<div>
  <ONE />
  <img {src} alt="{name} logo." />
  <h1>hello {name}!</h1>
</div>

<style>
</style>

以上代码描述主要是如何 引入组件  组件名称要大写用来区分html标签

3.5解析html标签

 

 

<script>
  import ONE from './component/one.svelte'
  let name = 'svelte'
  let src = '/src/assets/image/svelte.png';
  let htmlText = `<img src="/src/assets/image/svelte.png" alt="{name} logo." />`
</script>

<div>
  {@html htmlText}
  <ONE />
  <img {src} alt="{name} logo." />
  <h1>hello {name}!</h1>
</div>

<style>
</style>

3.6响应事件  深度反应性

<script>
  let count = $state(0);
function increment() {
  count += 1;
}
let numbers = $state([1, 2, 3, 4]);

function addNumber() {
  numbers.push(numbers.length + 1);
}
</script>

<div>

<p>{numbers.join(' + ')} = ...</p>

<button onclick={addNumber}>
  添加数字
</button>

  <button onclick={increment}>
    Clicked {count}
    {count === 1 ? 'time' : 'times'}
  </button>
</div>

<style>
</style>

 以上代码可以实现上图功能

3.7 $derived使用

<script>
    let numbers = $state([1, 2, 3, 4]);
    let total = $derived(numbers.reduce((t, n) => t + n, 0));

    function addNumber() {
        numbers.push(numbers.length + 1);
    }
</script>

<p>{numbers.join(' + ')} = {total}</p>

<button onclick={addNumber}>
    添加数字得出总数
</button>

以上代码 即可实现下方逻辑

$derived 作用
·自动响应依赖变化
·高效 - 只在依赖变化时重新计算
·可以在组件中多次使用
·替代了 Svelte 之前版本中的 $: 语法(虽然 $: 仍然可用)

 3.8 $inspect使用

<script>
    let numbers = $state([1, 2, 3, 4]);
    let total = $derived(numbers.reduce((t, n) => t + n, 0));

    function addNumber() {
        numbers.push(numbers.length + 1);
    }

    $inspect(numbers).with(console.trace);
</script>

<p>{numbers.join(' + ')} = {total}</p>

<button onclick={addNumber}>
    Add a number
</button>
$inspect作用
 ·监听 numbers 这个响应式变量的所有变化 (所示代码:$inspect(numbers))
 ·每当 numbers 被修改时,都会触发回调(所示代码:$inspect(numbers))
 ·将变化信息通过 console.trace 输出到控制台(所示代码:.with(console.trace))
 ·会显示完整的调用堆栈(trace),让你知道是代码中的哪一部分导致了 numbers 的变化(所示代码:.with(console.trace))

 3.9$effect使用

<script>
    let elapsed = $state(0);
    let interval = $state(1000);

    $effect(() => {
        const id = setInterval(() => {
            elapsed += 1;
        }, interval);

        return () => {
            clearInterval(id);
        };
    });
</script>

<button onclick={() => interval /= 2}>加速</button>
<button onclick={() => interval *= 2}>减速</button>

<p>elapsed: {elapsed}</p>

以上代码 即可实现一下页面展示

$effect 说明
·$effect 是一个运行时声明 类似于 React 的 useEffect ,但语法更简洁
·Svelte 会自动检测 effect 内部使用的响应式变量(如 interval )
·当这些变量变化时,effect 会重新运行
注意:$effect 主要用于处理副作用(如定时器、事件监听、API 调用等),常规的状态计算应该使用 $derived

 

 3.10父组件向子组件单值传递

父组件:
<script> import NESTED from './component/Nested.svelte'; </script> <NESTED answer={100} /> <NESTED/>
子组件 使用$props接收 传递值
<script> let { answer } = $props(); </script> <p>answer: {answer}</p>

3.11父组件向子组件多值传递

父组件 传递使用...拓展符进行传递
<script>
 import NESTED from './component/Nested.svelte';
 const nums = {
    one: 1,
    two: 2,
  };
</script>

<NESTED {...nums} />
子组件 接收还是通过$props
<script>
    let { one, two } = $props();
</script>

<p>
    1: {one}
</p>
<p>
    2: {two}
</p>

以上代码即可实现下方逻辑

 3.12html表达逻辑 if

<script>
    let count = $state(0);

    function increment() {
        count += 1;
    }
</script>

<button onclick={increment}>
  count:{count}
</button>

{#if count > 10}
    <p>count:{count} count大于10了</p>
{/if}

以上代码即可实现以下逻辑

 

 3.13html表达逻辑 if else

<script>
  let count = $state(0);

  function increment() {
    count += 1;
  }
</script>

<button onclick={increment}>
  count:{count}
</button>

{#if count > 10}
  <p>count:{count} 大于10</p>
{:else if count < 5}
  <p>count:{count} 小于5</p>
{:else}
  <p>count:{count} 5-10</p>
{/if}

以上代码即可实现下方逻辑

3.14循环渲染模板

<script>
    const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
    let selected = $state(colors[0]);
</script>

<h1 style="color: {selected}">点击下方按钮修改字体颜色</h1>

<div>
    {#each colors as color, i}
        <button
            style="background: {color}"
            aria-label={color}
            aria-current={selected === color}
            onclick={() => selected = color}
        >{i + 1}</button>
    {/each}
</div>

<style>
    h1 {
        transition: color 0.2s;
        font-size: 2rem;
        font-weight: 700;
    }

    div {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        grid-gap: 5px;
        max-width: 400px;
    }

    button {
        aspect-ratio: 1;
        border-radius: 50%;
        background: var(--color, #fff);
        transform: translate(-2px,-2px);
        filter: drop-shadow(2px 2px 3px rgba(0,0,0,0.2));
        transition: all 0.1s;
        color: black;
        font-weight: 700;
        font-size: 2rem;
    }

    button[aria-current="true"] {
        transform: none;
        filter: none;
        box-shadow: inset 3px 3px 4px rgba(0,0,0,0.2);
    }
</style>
使用以上代码就能实现一下效果

主要是用 #each 来循环dom colors 数组 as color 数组项, i 下标
 ·简洁直观的语法
 ·自动响应式更新
 ·作用域隔离

3.15给每个块设置唯一键

父项目
<script> import Nested from './component/Nested.svelte'; let things = $state([ { id: 1, name: 'apple' }, { id: 2, name: 'banana' }, { id: 3, name: 'carrot' }, { id: 4, name: 'doughnut' }, { id: 5, name: 'egg' } ]); </script> <button onclick={() => things.shift()}> 删除第一个 </button> {#each things as thing (thing.id)} <Nested name={thing.name} /> {/each}
子项目
<script>
    const emojis = {
        apple: '🍎',
        banana: '🍌',
        carrot: '🥕',
        doughnut: '🍩',
        egg: '🥚'
    };
    let { name } = $props();
    const emoji = emojis[name];
</script>
<p>{emoji} = {name}</p>
 
主要是在 each 块里面加了 (thing.id)

 以上代码即可实现一下效果

3.16标记promis等待中值

<script>
    import { roll } from './utils.js';

    let promise = $state(roll());
</script>

<button onclick={() => promise = roll()}>
  掷骰子
</button>

{#await promise}
    <p>...加载</p>
{:then number}
    <p>你掷出了{number}!</p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}
utils.js 文件
export async function roll() {
    return new Promise((fulfil, reject) => {
        setTimeout(() => {
            if (Math.random() < 0.3) {
                reject(new Error('Request failed'));
                return;
            }
            fulfil(Math.ceil(Math.random() * 6));
        }, 1000);
    });
}

以上代码即可实现已选展示逻辑

 3.17DOM events

<script>
    let m = $state({ x: 0, y: 0 });
    function onpointermove(event) {
        m.x = event.clientX;
        m.y = event.clientY;
    }
</script>

<div onpointermove={onpointermove}>
    指针位于{Math.round(m.x)} x {Math.round(m.y)}处
</div>

<style>
    div {
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        padding: 1rem;
    }
</style>
onpointermove事件可以简写为 {onpointermove}

以下是内联事件处理
<div
  onpointermove={(event) => {
    m.x = event.clientX;
    m.y = event.clientY;
  }}
>指针位于{Math.round(m.x)} x {Math.round(m.y)}处
</div>

以上代码可以实现以下逻辑

3.18捕获Capturing

<div onkeydowncapture={(e) => alert(`<div> ${e.key}`)} role="presentation">
    <input onkeydowncapture={(e) => alert(`<input> ${e.key}`)} />
</div>

3.19组件事件

父组件
<script>
    import Stepper from './component/Stepper.svelte';

    let value = $state(0);
</script>

<p>当前值为 {value}</p>

<Stepper
    increment={() => value += 1}
    decrement={() => value -= 1}
/>  

子组件
<script>
    let { increment, decrement } = $props();
</script>

<button onclick={decrement}>-1</button>
<button onclick={increment}>+1</button>

以上代码实现以下逻辑

 3.20text input

<script>
    let name = $state('world');
</script>

<input bind:value={name} />

<h1>Hello {name}!</h1>

以上代码实现以下效果

 3.21复选框

<script>
    let yes = $state(false);
</script>

<label>
    <input type="checkbox" bind:checked={yes} />
    是否订阅
</label>

{#if yes}
    <p>
    订阅
    </p>
{:else}
    <p>
        不订阅
    </p>
{/if}

<button disabled={!yes}>订阅</button>

以上代码即可实现以下逻辑

3.22反转卡片实列

<script>
    let flipped = $state(false);
</script>

<div class="container">
    翻转卡片
    <button
        class={["card", { flipped }]}
        onclick={() => flipped = !flipped}
    >
        <div class="front">
            <span class="symbol">♠</span>
        </div>
        <div class="back">
            <div class="pattern"></div>
        </div>
    </button>
</div>

<style>
    .container {
        display: flex;
        flex-direction: column;
        gap: 1em;
        height: 100%;
        align-items: center;
        justify-content: center;
        perspective: 100vh;
    }

    .card {
        position: relative;
        aspect-ratio: 2.5 / 3.5;
        font-size: min(1vh, 0.25rem);
        height: 80em;
        border-radius: 2em;
        transform: rotateY(180deg);
        transition: transform 0.4s;
        transform-style: preserve-3d;
        padding: 0;
        user-select: none;
        cursor: pointer;
    }

    .card.flipped {
        transform: rotateY(0);
    }

    .front, .back {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        backface-visibility: hidden;
        border-radius: 2em;
        border: 1px solid var(hsl(206, 20%, 90%));
        box-sizing: border-box;
        padding: 2em;
    }

    .front {
        background-size: 8em 8em, 8em 8em;
    }

    .back {
        transform: rotateY(180deg);
    }

    .symbol {
        font-size: 30em;
    }

    .pattern {
        width: 100%;
        height: 100%;
        background-color: hsl(206, 20%, 90%);
        /* pattern from https://projects.verou.me/css3patterns/#marrakesh */
        background-image:
        radial-gradient(hsl(206, 20%, 80%) 0.9em, transparent 1em),
        repeating-radial-gradient(hsl(206, 20%, 80%) 0, hsl(206, 20%, 80%) 0.4em, transparent 0.5em, transparent 2em, hsl(206, 20%, 80%) 2.1em, hsl(206, 20%, 80%) 2.5em, transparent 2.6em, transparent 5em);
        background-size: 3em 3em, 9em 9em;
        background-position: 0 0;
        border-radius: 1em;
    }
</style>

以上代码即可实现以下逻辑

 3.23组件样式

父组件
<script>
    import Box from './component/box.svelte';
</script>

<div class="boxes">
    <Box --color="red" />
    <Box --color="green" />
    <Box --color="blue" />
</div>
子组件
<div class="box"></div>

<style>
    .box {
        width: 5em;
        height: 5em;
        border-radius: 0.5em;
        margin: 0 0 1em 0;
        background-color: var(--color, #ddd);
    }
</style>

以上代码可实现以下效果

 3.24动画交互

<script>
    import kitten from './kitten.png';

    let hereKitty = $state(false);
</script>

<svelte:body
    onmouseenter={() => hereKitty = true}
    onmouseleave={() => hereKitty = false}
/>
<img
    class={{ curious: hereKitty }}
    alt="Kitten wants to know what's going on"
    src={kitten}
/>

<style>
    img {
        position: absolute;
        left: 0;
        bottom: -60px;
        transform: translate(-80%, 0) rotate(-15deg);
        transform-origin: 100% 100%;
        transition: transform 0.4s;
    }

    .curious {
        transform: translate(-15%, 0) rotate(0deg);
    }

    :global(body) {
        overflow: hidden;
    }
</style>

以上代码可实现以下逻辑

猫的图放在这里了

 

https://gitee.com/tcyyyy/vite---svelte 后台项目仓库

 

posted @ 2025-04-21 16:15  樱桃树下的约定  阅读(69)  评论(0)    收藏  举报
返回顶端