开始使用Fastify和Svelte
Fastify是一个低开销的后端网络框架,它建立在Node.js运行时间之上。由于其轻量级设计和自带的插件生态系统,其受欢迎程度和关注度正在上升。
在Fastify中,我们的想法是,所有东西都是一个插件,允许开发人员扩展其功能。这使我们能够快速地将我们项目中的功能封装为一个插件,然后可以在其他项目中分发和使用。
在客户端,我们将使用Svelte,它与其他现代客户端JavaScript框架(如React)有很大不同。
Svelte是一个编译器,它将你的工作转移到编译步骤中,而不是使用虚拟DOM。这发生在你通过将组件转换为高效的代码来构建你的应用程序时。
目标
在这个Fastify和Svelte教程中,我们将建立一个获取博客文章的CRUD应用。
我们将。
- 在Node.js环境中建立一个Fastify。
- 为我们的Fastify后端定义API路由。
- 添加请求的验证。
- 加载和使用Fastify插件。
- 使用
degit建立一个Svelte前端应用程序。 - 在Svelte中获取数据和构建组件。
前提条件
- 使用JavaScript和Node.js环境编程的基础知识。
- 在你的开发系统中安装Node.js JavaScript运行时。
- 一个IDE或代码编辑器,如VS Code。
- 对REST APIs有基本的了解会有帮助。
- 在你的开发机器上有一个网络浏览器,如谷歌浏览器。
用Node.js和Fastify设置后端
为了启动该项目,创建一个文件夹,并将其命名为fastify-svelte-app 。在其中,我们将为我们的Svelte客户端创建另一个文件夹,并将其命名为svelte-client 。
我们的后端代码将在主文件夹fastify-svelte-app 的根部。这就是我们现在要开始使用的文件夹。
打开终端,运行命令npm init -y 。这将用默认配置初始化npm ,同时创建一个package.json 文件来管理我们的依赖关系。
在初始化Node.js服务器之前,我们要把fastify 和fastify-cors 作为一个依赖项添加到我们的项目中。
npm i fastify fastify-cors --save
设置Fastify
我们在index.js 文件中设置了Fastify服务器。首先,加载Fastify应用对象,将其实例化并启用日志到控制台。
// Require the framework and instantiate it
const app = require('fastify')({logger: true})
// handle CORS
app.register(require('fastify-cors'), {
origin: true,
methods: ["GET","POST", "DELETE", "PUT", "PATCH"]
})
为了消费API,我们的服务器需要一个方法,我们可以在服务器中启用跨源资源共享(CORS)。
CORS是一种机制,根据HTTP请求的域来限制网络服务器上的资源请求。当使用 CORS 中间件时,我们可以允许或限制一些路由。在我们的案例中,我们将允许所有路由的请求。
//Parse Content-Type
app.addContentTypeParser('*', function (request, payload, done) {
var data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
接下来,我们声明一个索引路由。我们的应用程序将以JSON作为解析的数据格式来接收和响应。
app.get('/', (req, res)=>{
res.send('You are in the index route')
})
app.listen(3000, (err, address)=>{
if(err){
app.log.error(err)
process.exit(1)
}
app.log.info(`Server listening on ${address}`)
})
上述代码中的app.listen() 方法允许我们监听端口3000 ,这样我们的应用程序就可以允许传入的请求。
添加控制器
为了分离关注点,我们将在项目的根文件夹中创建一个controller 文件夹。在controller 文件夹内,我们定义posts.js 文件。
在MVC架构中,控制器负责控制客户端与应用程序的交互方式。它控制逻辑流程,以确定当服务器收到请求时,要发送什么响应。
该文件将包含一些作为对象数组的API演示数据。每一个都是一个具有ID和标题字段的单一帖子。
然后我们在这个文件中定义所有路由的处理程序。处理程序将请求和响应作为其参数。
让我们把我们的帖子数据定义为一个对象的数组。该对象有ID和title 的属性。
在posts.js 文件中,添加以下代码。
// Demo data
let posts = [
{
id: 1,
title: 'This is an experiment'
},
{
id: 2,
title: 'Fastify and Svelte pretty cool'
},
{
id: 3,
title: 'Another post, here we go!'
}
];
路径处理方法分别包括添加帖子、获取所有帖子和删除帖子。当添加帖子时,我们通过req.body 访问请求的主体。
最后,我们需要将处理程序导出为模块,在我们的路由中使用。
// Handlers
const addPost = async (req, res) => {
const id = posts.length + 1 // adding new post will generate a new ID
const newPost = {
id,
title: req.body.title,
}
posts.push(newPost)
return res.send(newPost)
}
const getAllPosts = async (req, res) => {
return posts
}
const deletePost = async (req, res) => {
const id = Number(req.params.id)
posts = posts.filter(blog => blog.id !== id)
return { msg: `Blog with ID ${id} is deleted` }
}
module.exports = {
addPost,
getAllPosts,
deletePost
}
在下一步,我们将把这些处理程序添加到Fastify路由对象中。
创建路由
使用Fastify,我们可以将路由对象定义为一个API端点。然后我们将所有先前定义的处理程序绑定到不同的路由。
下面是实现这一目的的代码。
const blogController = require('../controller/blogs')
const routes = [{
method: 'GET',
url: '/api/blogs',
handler: blogController.getAllPosts
},
{
method: 'POST',
url: '/api/blogs',
handler: blogController.addPost
},
{
method: 'DELETE',
url: '/api/blogs/:id',
handler: blogController.deletePost
}
]
module.exports = routes
现在让我们测试一下我们的服务器是否正常工作。再次打开终端并输入。
node server.js
在控制台,你应该看到我们的应用程序正在运行的服务器端口的输出(port 5000 )。
现在我们已经定义了所有的路由,我们需要在我们的Fastify应用对象中注册它们。
注册Fastify路由
在这一步,我们将注册Fastify路由到应用对象中。
首先,我们加载所有的博客路由。接下来,我们在所有的路由上循环,逐一注册它们。
// Register the routes to handle posts
const blogRoutes = require('./routes/blogs')
blogRoutes.forEach((route, index) => {
app.route(route)
})
现在,是时候验证一下这是否有效了。
使用node index.js ,在终端上启动Fastify服务器。如果你在浏览器上访问http://localhost:3000/blogs/ ,应该会得到JSON对象格式的演示数据中的所有博客。
我们的Fastify Node.js后端已经完成,现在我们将在接下来的步骤中前往Svelte。
用Svelte添加前端
在搭建一个新的Svelte应用程序之前,先创建一个新的Svelte项目。我们将使用degit Svelte CLI。
npx degit sveltejs/template svelte-app
为了安装依赖性,在我们的svelte-app 项目中导航并运行npm install 。
cd svelte-app
npm install
在你的IDE中打开应用程序文件夹,用Rollup启动应用程序。
npm run dev
我们的Svelte文件夹有一个src ,这个文件夹将包含我们所有的代码。
它里面的两个文件包括。
-
App.svelte- 这是主Svelte组件。它包含诸如 和scriptstyle -
main.js- 这个文件是应用程序的入口点。它创建了一个新的 的实例。App.svelte
import App from './App.svelte'
const app = new App({
target: document.body,
props: {
name: 'world'
}
})
export default app
属性target: document.body 表示我们将在HTML主体中渲染我们的应用程序。最初,它渲染了props: { name: 'world' } 的道具。
你会看到一个rollup.config.js ,它是用于rollup packager bundler的配置文件。
在rollup.config.js 文件中,你可以看到main.js 被指定为输入。main.js 被应用程序调用,后者又调用App.svelte 。
如果你想吹毛求疵:main.js 在运行时被包含在bundle.js 中。bundle.js 被public.html 调用。
既然这是模板代码,让我们开始编辑和构建组件,在接下来的步骤中连接到我们的Fastify后端。
创建一个Blog.svelte组件
为了保持简单,我们将使用这个组件来执行我们应用程序中的所有帖子任务。由于我使用了bootstrap来做造型,所以在public 目录中添加bootstrap CDN。
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
我们的Svelte组件的任务包括。
- 在
<script>块内从Node.js服务器上获取帖子。 - 一个插入新帖子的函数,这些帖子将被暂时保存在服务器上。
- 一个按ID删除单个帖子的函数。
前往你的Posts.svelte 组件文件,执行以下步骤。
我们的Posts.svelte 文件有<script> ,<section> 容器,和<style> 块。在<script> 标签之间,我们编写JavaScript代码来添加动态功能。
首先,我们创建一个newPostData 对象并将其导出。这个对象将保存新的todo 的文本,并带有一个title 属性。
后台通过在我们每次添加新的帖子时递增它来确定ID 。为了插入一个新的帖子,我们创建一个函数createPost() ,以JSON格式向我们的后端发送一个POST 请求。
为了实现这一点,我们使用<input> HTML元素的值,并将其绑定到newPostData 。
Svelte丰富了我们的HTML标记,因此我们可以使用编程逻辑,如条件和循环。如果我们点击Add 按钮,createPost 方法就会被调用,以创建一个新的帖子,而我们的后台会处理其余的事情。
<script>
export let newPostData ={
title: "",
}
const createPost = async ()=>{
const new_post = {
title: newPostData.title
}
await fetch('http://localhost:3000/api/blogs', {
method: "POST",
mode: "cors",
headers:{
"Content-Type": "application/json"
},
body: JSON.stringify(new_post)
})
.then(result=>console.log(result))
.catch(err=>console.error(err))
}
</script>
接下来,我们需要了解如何fetchPosts ,检索我们所有的帖子。
数据的获取是一个异步任务。我们的函数和标记使用async-await 语法来实现这一点。
fetch API发出一个网络请求并返回帖子。在标记中,我们检查fetchPosts 方法是否已经获取了数据,然后再将其呈现在用户界面上。错误处理是在catch块上实现的。
const fetchPosts = (async () => {
const response = await fetch('http://localhost:3000/api/blogs')
return await response.json()
})()
const deletePost = (async ()=>{
})()
Svelte使用的是HTML的超集,它有。
- 一个
<section>标签,有一个<div>元素作为容器。 - 一个用于创建新帖子的表单
- 一个标签和一个用于输入新任务的文本框。
- 一个有序的列表,为每个任务保存一个列表项,以及一个删除任务的按钮。
<section>
<div class="container">
<div class="row mt-5">
<div class="col-md-6">
<div class="card p-2 shadow">
<div class="card-body">
<div class="card-title mb-4">
<h2>Add New Post</h2>
<form>
<div class="form-group">
<label for="title">Title:</label>
<input bind:value={newPostData.title}
type="text" class="form-control" id="text" placeholder="Note Post">
</div>
<button on:click|preventDefault={createPost}
type="submit" class="btn btn-primary">Add</button>
</form>
</div>
</div>
</div>
<div class="col-md-">
<div class="card">
<h2 class="card-header">Posts</h2>
<div class="card-body">
{#await fetchPosts}
<p>...waiting</p>
{:then data}
<div>
{#each data as item}
<h3 class="card-title underline">{item.id}</h3>
<p class="card-text">
{item.title}
<span><button class="btn btn-danger">Delete</button></span>
</p>
{/each}
</div>
{:catch error}
<p>An error occurred!</p>
{/await}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
上面的Html网页应该是这样的。

按ID删除一个帖子
要删除一个帖子,我们将使用fetch API向我们的后端发出一个DELETE 请求。其逻辑是过滤ID ,并返回除过滤后的帖子外的其余帖子。
让我们创建一个处理这个问题的函数。
const deletePost = async (id)=>{
const response = await fetch(`http://localhost:3000/api/blogs/${id}`, {
method: 'DELETE'
})
const msg = await response.json()
console.log(msg)
// .then(res => console.log(res)).catch(err=>console.log(err))
}
我们需要将其与HTML模板中的删除按钮绑定。
<span><button class="btn btn-danger" on:click={deletePost}>Delete</button></span>
因此,我们已经建立了一个CRUD应用程序,使用Fastify和Svelte创建、阅读、更新和删除博客文章。
总结
Fastify和Svelte是现代的JavaScript框架,为性能和良好的开发者体验而构建。Fastify框架有丰富的插件生态系统,因此代码可重复使用。就像Express一样,Fastify是简单而优雅的,有一个很好的日志系统。