如何使用 SvelteKit 构建计数器应用程序
什么是 SvelteKit
SvelteKit 是一个基于 Vite 的框架,用于构建 Svelte 应用程序。它使用户能够利用基于文件系统的路由,并构建各种应用程序,包括服务器端渲染、静态站点生成或单页应用程序。
但在我们开始使用 SvelteKit 之前,让我们复习一下我们的 Svelte。
什么是 Svelte?
Svelte 是一个用于构建快速交互式 Web 应用程序的前端开源 JavaScript 框架。
Svelte 的独特功能
- 构建时编译,向服务器交付更少的 js 代码
- 没有虚拟 DOM,需要对浏览器 DOM 进行直接和特定的修改
- 简单的反应性,更容易修改和维护应用程序状态
- 作用域 CSS,组件之间没有 CSS 泄漏
一个苗条的组件
.svelte
使用 HTML 语法的超集将 Svelte 组件写入文件。
每个.svelte
文件都包含一个组件,它是一个可重用的自包含代码块,封装了 HTML、CSS 和 JavaScript 或 Typescript。
Svelte 组件代码示例
<!-- evelutaed once -->
<script context="module">
/**
* can be imported as
* import { text } from filename.svelt
*/
export const text = 'hello'
</script>
<script>
let foo = 1
</script>
<h1>Template 1</h1>
<p>{foo}</p>
<style>
h1 {
color: blue;
}
</style>
.svelte 文件中的 4 个部分
模块
本节包含有关组件的代码和其他信息.svelte
。使用该模块,您可以将功能导出到其他组件。文件的默认导出.svelte
是组件本身。从模块部分导出的任何内容都独立于组件。
组件脚本
这包含组件本身的脚本和逻辑。这可以是 JavaScript 或 TypeScript 代码。如果要指定所需的语法,脚本代码可以如下所示:
// typescript
<script lang="ts">
let foo: number = 1
</script>
// javascript
<script lang="js">
let foo = 1
</script>
模板 本节包含组件的 HTML 模板代码。
样式 本节包含组件 UI 的样式表代码。
道具 要定义prop
组件,您将在脚本部分中声明一个变量,如下面的示例代码。
<script lang="ts">
import Child from './child.svelte'
let foo: number = 1
let prop1 = 1,
prop2 = 2,
obj = {
prop1,
prop2
};
</script>
<h1>Template 1</h1>
<p>{foo}</p>
<Child prop1={prop1} prop2={prop2} />
<Child {prop1} {prop2} />
<Child {...obj} />
child
声明的 props 可以通过赋值、简写赋值或像上面显示的代码那样破坏它来传递给组件。
处理事件
要声明一个事件,请导入createEventDispatcher
Svelte 提供的,并将on
指令附加到诸如on:click
.
例子
<script lang="ts">
import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher();
</script>
<button on:click={(e) => dispatch('foo', {foo: 'bar'})}>Button 1</button>
但您也可以使用其他方法在 Svelte 中声明和处理事件,例如:
<!-- handle on click event -->
<button on:click={(e) => console.log(e)}>Button Log</button>
<!-- event modifiers -->
<button on:click|preventDefault={(e) => console.log(e)}>Button Prevent</button>
<!-- bubble event to be used by the consumer of this component -->
<button on:click>Button 1</button>
Slots Svelte
- 插槽可用于创建接受和呈现任何子级的组件。
<slot>
通过使用可以接受组件或标记的内部组件在 Svelte 中创建插槽。
使用 Svelte Kit 构建计数器应用程序
现在我们了解了基础知识,让我们开始使用 Nacho 在 JS Marathon 培训期间教我们的 SvelteKit 构建计数器应用程序。
要创建 SvelteKit 应用程序,请运行:
npm init svelte
CLI 将提示您一些问题:
Where should we create your project?
(leave blank to use current directory) … todo
输入您要使用的目录的名称,如果您将使用当前目录,请留空。按照提示进行选择。
然后运行…
npm install
…为应用程序安装依赖项。
要测试应用程序,请运行:
npm run dev
这将在http://localhost:3000/
.
使用代码编辑器打开目录:
修改src/routes/index.svelte
文件并将其替换为以下内容
<script context="module" lang="ts">
export const prerender = true;
</script>
<section>
<h1>
<div class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
</div>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
h1 {
width: 100%;
}
.welcome {
position: relative;
width: 100%;
height: 0;
padding: 0 0 calc(100% * 495 / 2048) 0;
}
.welcome img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: block;
}
</style>
现在,src/routes/__layout.svelte
使用以下内容创建一个新文件,只需添加设计:
<script lang="ts">
import '../app.css';
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<main>
<slot />
</main>
<style>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>
计数器组件
在这一部分中,我们开始为待办事项创建各种组件,从Counter
组件开始。使用以下内容创建src/components/Counter.svelte
文件:
<script lang="ts">
import { spring } from 'svelte/motion';
let count = 0;
const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
function modulo(n: number, m: number) {
// handle negative numbers
return ((n % m) + m) % m;
}
</script>
<div class="counter">
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
</div>
</div>
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
<style>
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
touch-action: manipulation;
color: var(--text-color);
font-size: 2rem;
}
.counter button:hover {
background-color: var(--secondary-color);
}
svg {
width: 25%;
height: 25%;
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: var(--text-color);
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--accent-color);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
.hidden {
top: -100%;
user-select: none;
}
</style>
我们在Counter
组件中做什么?我们从中导入了spring
函数,svelte/motion
因为我们将处理一个频繁更改计数器值的点击事件。该spring
功能将有助于动画。
接下来,我们声明了count
一个初始值为 的变量,0
然后我们声明了一个displayed_count
函数,顾名思义,在触发 click 事件时显示计数,并spring()
为它分配了一个函数的实例。
然后,我们将 Svelte Reactive 语句分配给以在变量更改displayed_count
时运行该函数。count
然后,我们声明了offset
变量并分配了一个modulo
函数实例来处理负数。
对于模板,我们附加on:click
了用于减少和增加$displayed_count
变量的事件。
现在转到index.svelte
组件以导入Counter
组件。
<script context="module" lang="ts">
export const prerender = true;
</script>
// Start import here
<script lang="ts">
import Counter from '../components/Counter.svelte';
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<section>
<h1>
<div class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
</div>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
<!-- Add the Counter component here -->
<Counter />
</section>
在我们测试应用程序之前,让我们为应用程序添加一个关于页面和一个标题。
创建about.svelte
文件。路由的名称必须与路由名称相对应。要了解有关 SveltKit 路由的更多信息,请查看文档。
关于组件
为about
路线添加以下内容:
<svelte:head>
<title>About</title>
<meta name="description" content="About this app" />
</svelte:head>
<div class="content">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
following into your command line and following the prompts:
</p>
<pre>npm init svelte</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed.
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
the devtools network panel and reloading.
</p>
</div>
<style>
.content {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
}
</style>
该about
组件是纯静态 HTML。
标题组件
创建Header.svelte
文件并添加以下内容
<script lang="ts">
import { page } from '$app/stores';
</script>
<header>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<li class:active={$page.url.pathname === '/about'}>
<a sveltekit:prefetch href="/about">About</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
</header>
<style>
header {
display: flex;
justify-content: space-around;
}
nav {
display: flex;
justify-content: center;
--background: rgba(255, 255, 255, 0.7);
}
svg {
width: 2em;
height: 3em;
display: block;
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
li {
position: relative;
height: 100%;
}
li.active::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--accent-color);
}
</style>
Header
将组件导入文件__layout.svelte
,修改代码:
<script lang="ts">
// Import Header component here
import Header from '../components/Header.svelte';
import '../app.css';
</script>
<!-- Add the Header component -->
<Header />
<main>
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
</footer>
<style>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>
运行应用程序以查看最终结果
结论
希望您对 SvelteKit 有所了解,并能够跟随并构建一个简单的应用程序!