如何使用 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 可以通过赋值、简写赋值或像上面显示的代码那样破坏它来传递给组件。

处理事件

要声明一个事件,请导入createEventDispatcherSvelte 提供的,并将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

截图 2022-07-26 1.58.58 PM

CLI 将提示您一些问题:

Where should we create your project?
  (leave blank to use current directory) … todo

输入您要使用的目录的名称,如果您将使用当前目录,请留空。按照提示进行选择。

然后运行…

npm install

…为应用程序安装依赖项。

要测试应用程序,请运行:

npm run dev

这将在http://localhost:3000/.

截图 2022-07-26 2.00.33 PM

使用代码编辑器打开目录:

截图 2022-07-26 2.01.45 PM

修改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>

运行应用程序以查看最终结果

截图 2022-07-26 2.06.29 PM

结论

希望您对 SvelteKit 有所了解,并能够跟随并构建一个简单的应用程序!