如何使用Fastify和Svelte

612 阅读7分钟

开始使用Fastify和Svelte

Fastify是一个低开销的后端网络框架,它建立在Node.js运行时间之上。由于其轻量级设计和自带的插件生态系统,其受欢迎程度和关注度正在上升。

在Fastify中,我们的想法是,所有东西都是一个插件,允许开发人员扩展其功能。这使我们能够快速地将我们项目中的功能封装为一个插件,然后可以在其他项目中分发和使用。

在客户端,我们将使用Svelte,它与其他现代客户端JavaScript框架(如React)有很大不同。

Svelte是一个编译器,它将你的工作转移到编译步骤中,而不是使用虚拟DOM。这发生在你通过将组件转换为高效的代码来构建你的应用程序时。

目标

在这个Fastify和Svelte教程中,我们将建立一个获取博客文章的CRUD应用。

我们将。

  • 在Node.js环境中建立一个Fastify。
  • 为我们的Fastify后端定义API路由。
  • 添加请求的验证。
  • 加载和使用Fastify插件。
  • 使用degit 建立一个Svelte前端应用程序。
  • 在Svelte中获取数据和构建组件。

前提条件

  1. 使用JavaScriptNode.js环境编程的基础知识。
  2. 在你的开发系统中安装Node.js JavaScript运行时。
  3. 一个IDE或代码编辑器,如VS Code
  4. REST APIs有基本的了解会有帮助。
  5. 在你的开发机器上有一个网络浏览器,如谷歌浏览器

用Node.js和Fastify设置后端

为了启动该项目,创建一个文件夹,并将其命名为fastify-svelte-app 。在其中,我们将为我们的Svelte客户端创建另一个文件夹,并将其命名为svelte-client

我们的后端代码将在主文件夹fastify-svelte-app 的根部。这就是我们现在要开始使用的文件夹。

打开终端,运行命令npm init -y 。这将用默认配置初始化npm ,同时创建一个package.json 文件来管理我们的依赖关系。

在初始化Node.js服务器之前,我们要把fastifyfastify-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 ,这个文件夹将包含我们所有的代码。

它里面的两个文件包括。

  1. App.svelte - 这是主Svelte组件。它包含诸如 和script style

  2. 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.jspublic.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网页应该是这样的。

compelete.png

按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是简单而优雅的,有一个很好的日志系统。