本系列的前一篇文章(对 React 的非常温和的介绍)向读者介绍了用于开发 Web 应用程序的优秀 React 框架系统。 SvelteKit 是一个替代框架。它与 React 有什么不同,有什么更好的地方吗?
从功能上来说,我想没有那么大的区别。在 React 中可以做的大多数事情也可以在 SvelteKit 中完成。反之亦然。但当你深入细节时,很多人认为 SvelteKit 在轻松实现“响应式”目标方面具有优势。 Svelte 的意思是“优雅”——这就是它的本质——一种纤细、适应性强且实用的工具。
就我个人而言,我被 SvelteKit 所吸引,因为它还倾向于推动您进行服务器端设计 - 即在 Web 应用程序的云服务器上运行的代码,而不是在用户的 Web 浏览器中运行。这很讽刺,因为编写和调试客户端代码的便捷性最初让我对 Web 应用程序开发着迷。但后来我发现索引蜘蛛是多么不愿意投入精力来“滋润”客户端代码,并意识到我只需要在这里投入更多的精力(请参阅下面的 SvelteKit 中的调试,看看会带来什么)。但您也可能考虑使用服务器端代码还有其他原因。这里有一些:
一旦您开始使用 Postmark(电子邮件发送)或 Paypal(收款)等第三方服务,您就会意识到将其安全代码包含在客户端代码中并不是一个好主意。如果您可以使用“检查器”来查看这些内容,那么其他人也可以。运行服务器端的代码无法访问。
服务器端代码距离您的数据更近,并且在这里比在客户端笔记本电脑上运行得更快。
SvelteKit 可以轻松地播放音乐,指定 Web 应用程序的哪些部分要本地运行,哪些部分要远程运行。
让我们开始讨论更具体的事情。
从外部来看,Sveltekit Web 应用程序看起来与任何经典浏览器应用程序完全相同 - “页面”的层次结构,例如 mywebapp/dosomethingwithmyfiles。之所以会这样,是因为客户端用户期望并依赖这种类型的安排。但在表面之下,SvelteKit Web 应用程序以与 React Web 应用程序完全不同的方式提供这种安排。在 React 中,这些页面实际上是一大堆代码的所有部分,并且请求通过在 Web 界面上操作的重定向来路由到那里(如果这句话对您来说没有任何意义,请看看什么是“单-”)页的网络应用程序?)。 SvelteKit 通过使用项目结构来定义页面结构来实现这一点。因此,如果您想要一个 mywebapp/dosomethingwithmyfiles 页面,您需要有一个名为 dosomethingwithmyfiles 的文件夹,其中包含一个 +page.svelte 文件。一旦这种安排到位,您部署的应用程序就会为其每个 URL 提供一个单独的物理页面。
这是 SvelteKit 项目的示例源文件夹结构:
我的项目
├────src
│ └────路线
│ └───用我的文件做一些事情
一旦你安装了 SvelteKit(请参阅 Svelte for New Developers),这个结构将通过大量复杂的配置文件和构建文件夹等来增强。但是,目前,重点是在路由文件夹上。 这是您存储页面代码的地方 - 在这里您可能会开始怀疑 SvelteKit 是否适合您。现在要抓紧,因为这就是事情变得有点复杂的地方。
SvelteKit 要求您对页面文件夹的内容遵循非常严格的命名约定。以下是 dosomethingwithmyfiles 文件夹中可能出现的文件名列表:
乍一看,你可能会觉得这根本无法接受。但请注意,每个 +page.svelte 文件在编辑器栏上都通过其文件夹所有者的名称、dosomethingwithmyfiles 或其他名称进行限定。在开始编辑之前,训练自己检查 +page.svelte 的所有者并不是那么困难。一旦您开发了一两个 SvelteKit 项目,您就会开始欣赏约定在声明安排的目的方面的价值(正如您稍后会看到的,有很多变体) )
当你承受这种冲击时,让我给你一点鼓励。 在 +page.svelte 文件中,您可能期望找到与在等效 React 文件中看到的相同类型的代码 - 混合了用于操作页面状态的外来 useState 调用和用于“反应”的 JSX到此并生成 HTML。虽然 +page.svelte 文件确实可以完成相同的工作,但它设法放弃“异国情调”的部分,并使用纯 JavaScript 和纯粹的、未稀释的 HTMl,并添加了一些特殊关键字。您可能会发现这令人耳目一新。
以下是您可能会在 dosomethingwithmyfiles 文件夹中找到的一些标准文件名:
有趣的是,如果您过去在编写 Web API 代码以在浏览器中运行和 Node.js 风格在 Firebase 函数中在服务器端运行之间切换时,必须“重新编程”您的 javascript 大脑,那么您会我们会很高兴听到,在 Sveltekit 中,Web API 版本现在也非常乐意在服务器端运行。
当然,您会想知道如何组织事物,以便 +page.js 文件读取的数据最终出现在关联的 +page.svelte 中。我想说的是,就目前而言,这是通过 SvelteKit 魔法实现的。只有在我描述了 SvelteKit 定义“反应性”变量的安排后,确切的机制才会变得清晰。暂时戴上你的帽子。
如果 +layout.svelte 页面需要一些数据,它可以有一个附带的 +layout.server.js 文件
“反应变量”是指当数据项发生变化时导致浏览器页面重新渲染的数据项。我所说的“反应式 HTML”是指 HTML 能够响应这些变化。
在 React 中,您会记得,反应式变量是使用 useState 表达式来声明的,该表达式将变量定义为状态对象的属性。该声明还指定了初始属性值和更改它们的函数。
这是一个示例 - 一个 React Web 应用程序,它显示一个弹出窗口,当您单击它时该弹出窗口就会消失:
import React, { useState } from "react"; const [screenState, setScreenState] = useState({popupVisible: true,}); return ( <div> <h1 style={{textAlign: "center"}} onClick = {() => {setScreenState({popupVisible: !screenState.popupVisible})}}> Main Page - Click to toggle popup </h1> {screenState.popupVisible && <div style={{ textAlign: "center", marginLeft: "auto", marginRight: "auto", height: "2rem", width: "25rem", backgroundColor: "gainsboro" }} onClick = {() => {setScreenState({popupVisible: !screenState.popupVisible})}}> <h2> Popup Window - Click to Hide popup</h2> </div> } </div> )
在 Svelte 中(我现在谈论的是语言,而不是它运行的框架),你可以在 src/routes/demo/+ 中实现这种效果page.svelte 文件,只需将 popupVisible 声明为 javascript 变量
<script> let popupVisible = false; </script> <div> <h1 style="text-align: center" on:click={() => (popupVisible = !popupVisible)}> Main Page - Click to toggle popup </h1> {#if popupVisible} <div style="text-align: center; margin-left: auto; margin-right: auto; height: 2rem; width: 25rem; background-color: gainsboro" on:click={() => (popupVisible = !popupVisible)} > <h2>Popup Window - Click to Hide popup</h2> </div> {/if} </div>
以下是主要差异的摘要:
Svelte uses a standard Javascript let declaration to introduce state variables instead of the strange React useState expression
Svelte uses a down to earth #if 'logical expression' keyword to replace the awkward JSX {'logical expression' &&syntax. This makes your code much more readable. Svelte also provides associated else and each keywords.
Svelte uses plain CSS to define HTML classes rather than the perplexing JSX style objects (eg {{textAlign: "center"}}).
Note also that the demo/+pagesvelte file defined above will run directly in the browser as /demo. To run the React version you would have to put some code into an associated src/main.jsx file to define the new route.
Keyboard input in React generally uses the following pattern:
const [myState, setMyState] = useState({myProperty: "",}); function handleChange({ target }) { setMyState({ ...myState, [target.name]: target.value }); }; return ( <input name="myProperty" value={myState.myProperty} onChange={handleChange} /> )
Here, an input labelled as "myProperty" fires a general-purpose handleChange function every time you press a key. In handleChange its value is extracted and applied to the page's state to trigger a re-render.
Svelte thinks this is too complicated and introduces a "bind" keyword to its input syntax. This automatically transmits changes to an associated state variable. A Svelte version of the above thus looks like this:
<script> let myProperty = ""; </script> <input bind:value={myProperty} />
The bind keyword is also used to enable you to create two-way communication between parent and child components. This is a powerful feature.
An interesting feature of Svelte is that it encourages you to use forms and server-side processing for input handling. Thus it's perfectly permissible in Svelte to launch a client-side function like this:
<script> let myProperty = ""; function commitChange() { // Use the global myProperty variable to update server storage } </script> <span>myProperty = </span><input bind:value={myProperty} /> <button on:click={commitChange}>Commit Change</button> />
Svelte docs correctly insist that interactions like this are better handled by forms and server-side processing in a +page.server.js file. Here the validation and submission of the user input can be safely protected from the sort of interference possible in client-based code. Here also, any subsequent processing can be performed with maximum efficiency.
To implement this view, Svelte provide a neat automatic link between a form reading data on a +page.svelte and a function handling the processing of that data in the associated +page.server.js file. Here's an example:
src/routes/login/+page.svelte <form method="POST"> <span>myProperty = </span><input name="myProperty"> <button>Commit Change</button> </form> src/routes/login/+page.server.js export const actions = { default: async (event) => { // TODO handle the processing for the input read by the form on +page.svelte } };
Note that no Javascript has been used in the form - no "on click" or "on submit", for example. The linkage has been established entirely through "Svelte magic".
In practice, of course, a +page.svelte file is likely to want to be the source of multiple "actions". See Svelte Form Actions for details of how Svelte manages this. (Note that Svelte docs are organised under two URLs: kit.svelte.dev for framework topics like routing and svelte.dev for elements of the language itself)
Finally, to conclude this section, suppose you wanted users to be able to call on the service of an action by referencing it directly through a javascript "fetch" (or, at its simplest by launching a parameterised url via the browser - eg https:// mySite/myPage?param1=3 etc). This is where you would use a +server.js file to create an API "endpoint" function. Firebase users might well use such an arrangement where they had previously used a Firebase function. Not the least advantage of this would be that testing and debugging could be done in the Sveltekit server rather than the Firebase emulator.
Each +page.svelte file defines a component, and you mark variables declared here as "props" - ie make them accessible to "consumers" of the component - by adding the export keyword to their declarations. So, if you're still wondering how a +page.svelte file gets its data from +page.server.js - this is how it's done. A +page.svelte file wanting to receive "load" data from its +page.server.js (or +page.js) file just needs to put something like the following in its