برنامه Todo بدون جاوا اسکریپت سمت کلاینت با استفاده از @lazarv/react-server

همه هنگام ارزیابی یک چارچوب جدید با یک برنامه ساده Todo شروع می کنند. پس بیایید این بار با استفاده از @lazarv/react-server، یک متا فریمورک مینیمالیستی React با استفاده از Vite این کار را انجام دهیم!
هدف ما از این مثال استفاده از جاوا اسکریپت سمت کلاینت و هیدراتاسیون React است. ما فقط می خواهیم از React Server Components و Server Actions استفاده کنیم. آیا این ممکن است؟ کاملا!
راه اندازی پروژه
بیایید با ایجاد یک پوشه جدید، مقداردهی اولیه pnpm و نصب تمام وابستگی های مورد نیاز، یک پروژه جدید ایجاد کنیم.
mkdir todo
cd todo
pnpm init
pnpm config set auto-install-peers true --location project
pnpm add @lazarv/react-server better-sqlite3 zod
pnpm add -D @types/better-sqlite3 @types/react @types/react-dom autoprefixer postcss tailwindcss typescript
pnpx tailwindcss init -p
برای ذخیره اقلام Todo خود، از یک پایگاه داده محلی Sqlite استفاده می کنیم. برای اعتبار سنجی از Zod و برای استایل سازی از Tailwind CSS استفاده می کنیم. برای گنجاندن تمام کد منبع ما به عنوان محتوای Tailwind، آن را تغییر دهید tailwind.config.js به این:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.tsx"],
theme: {
extend: {},
},
plugins: [],
};
از آنجایی که ما هیچ طراحی هیجان انگیز Tailwind انجام نمی دهیم، تنظیمات Tailwind 3 خطی معمولی را در آن قرار دهید. src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
سلام دنیا!
خوب این چیزی بهتر از یک «سلام جهان» خوب قدیمی نیست! برنامه، بنابراین بیایید یک نقطه ورودی برای برنامه Todo خود ایجاد کنیم! کد زیر را در آن قرار دهید src/index.tsx:
export default function Index() {
return (
<h1>Hello World!h1>
);
}
برای اجرای این میکرو اپلیکیشن فقط می توانید از آن استفاده کنید pnpm exec react-server ./src/index.tsx
. برای آسانتر کردن زندگیمان، بیایید چند اسکریپت npm را به آن اضافه کنیم package.json:
"scripts": {
"dev": "react-server ./src/index.tsx",
"build": "react-server build ./src/index.tsx",
"start": "react-server start"
},
پس از انجام این کار، استفاده کنید pnpm dev
برای راه اندازی سرور توسعه پس از اجرای سرور توسعه ما، http://localhost:3000 را باز کنید و به Hello World ما افتخار کنید! برنامه همچنین می توانید استفاده کنید pnpm dev --open
برای انجام این کار.
چیدمان
برنامه Todo ما نیاز به طرح بندی دارد، پس بیایید ایجاد کنیم src/Layout.tsx:
export default function Layout({ children }: React.PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todotitle>
head>
<body>
<div className="p-4">
<h1 className="text-4xl font-bold mb-4">
<a href="https://dev.to/">Todoa>
h1>
{children}
div>
body>
html>
);
}
هیچ چیز جالبی در اینجا وجود ندارد، فقط یک قالب سند معمولی HTML. ما از این مؤلفه Layout به عنوان یک پوشش برای برنامه خود استفاده خواهیم کرد.
صفحه
صفحه اصلی ما برنامه Todo خواهد بود، جایی که ما از تمام بلوک های سازنده برای ایجاد برنامه خود استفاده خواهیم کرد. بیایید با Hello World خداحافظی کنیم! و کامپوننت را به این تغییر دهید:
import "./index.css";
import { allTodos } from "./actions";
import AddTodo from "./AddTodo";
import Item from "./Item";
import Layout from "./Layout";
export default function Index() {
const todos = allTodos();
return (
<Layout>
<AddTodo />
{todos.length === 0 && <p className="text-gray-500">No todos yet!p>}
{todos.map((todo) => (
<Item key={todo.id} title={todo.title} id={todo.id} />
))}
Layout>
);
}
در این React Server Component، همه موارد ذخیره شده Todo را با تماس جمع آوری می کنیم allTodos()
و از نتیجه برای رندر JSX استفاده کنید. ما از مؤلفه Layout برای قرار دادن محتوای خود در یک سند HTML استفاده می کنیم.
مورد
برای رندر کردن آیتم هایمان، بیایید یک جزء Item در آن ایجاد کنیم src/Item.tsx:
import { deleteTodo } from "./actions";
type Props = {
id: number;
title: string;
};
export default function Item({ id, title }: Props) {
return (
<div className="flex row items-center justify-between py-1 px-4 my-1 rounded-lg text-lg border bg-gray-100 text-gray-600 mb-2">
<p className="flex-1">{title}p>
<form action={deleteTodo}>
<input type="hidden" name="id" value={id} />
<button className="font-medium">Deletebutton>
form>
div>
);
}
مؤلفه Item مورد Todo ما را با استفاده از یک نشان می دهد id
و title
پشتیبانی اما در مورد آن چه؟ ? It’s a server action! When the user will submit the form by clicking on the “Delete” button, the browser will call our server action. This is possible without any JavaScript on the frontend, as React supports progressive enhancement for server actions and the initial form action will call the server action by including a hidden input field in the form:
<input type="hidden" name="$ACTION_ID_/Users/lazarv/Projects/tutorials/todo/src/actions.ts#deleteTodo">
چارچوب این را حل خواهد کرد $ACTION_ID_
مسیر پیشوند اکشن سرور را تعیین می کند و تابع عملکرد سرور ما را فراخوانی می کند!
اقدامات سرور
ما از اقدامات سرور برای اجرای تمام عملکردهای برنامه Todo خود استفاده خواهیم کرد. این پیچیده ترین بخش برنامه است، اما خجالت نکشید، هنوز هم بسیار ساده است، بیایید ایجاد کنیم src/actions.ts:
"use server";
import { redirect } from "@lazarv/react-server";
import Database from "better-sqlite3";
import * as zod from "zod";
const db = new Database("db.sqlite");
db.exec(
"CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT)"
);
type Todo = {
id: number;
title: string;
};
const addTodoSchema = zod.object({
title: zod
.string()
.min(3, "Title must be at least 3 characters")
.max(100, "Title must be at most 100 characters")
.refine((value) => value.length > 0, "Title is required")
.transform((value) => value.trim()),
});
const deleteTodoSchema = zod.object({
id: zod.string().transform((value) => parseInt(value.trim(), 10)),
});
export async function addTodo(formData: FormData) {
const result = addTodoSchema.safeParse(Object.fromEntries(formData));
if (!result.success) {
throw result.error.issues;
}
const { title } = result.data;
db.prepare("INSERT INTO todos(title) VALUES (?)").run(title);
redirect("/");
}
export function allTodos() {
return db.prepare("SELECT * FROM todos").all() as Todo[];
}
export async function deleteTodo(formData: FormData) {
const result = deleteTodoSchema.safeParse(Object.fromEntries(formData));
if (!result.success) {
throw result.error.issues;
}
const { id } = result.data;
db.prepare("DELETE FROM todos WHERE id = ?").run(id);
redirect("/");
}
در خط اول این فایل، فریم ورک را ابزار می کنیم تا این فایل را به عنوان یک ماژول عمل سرور با استفاده از “use server”;
بخشنامه همه توابع غیرهمگام صادر شده برای استفاده به عنوان عملکرد سرور در دسترس ما خواهد بود.
ما پایگاه داده Sqlite را در هنگام وارد کردن ماژول مقداردهی اولیه می کنیم و طرحواره های Zod را برای عملیات افزودن و حذف آیتم ایجاد می کنیم.
در تمام اکشن های سرور، یک عدد دریافت خواهید کرد FormData
به عنوان مثال، شامل تمام فیلدهایی که در فرم ها تعریف می کنیم. ما safeParse
اینها پس از تبدیل به اشیاء جاوا اسکریپت با استفاده از Object.fromEntries
.
اگر اعتبار سنجی Zod ناموفق باشد، مسائل اعتبار سنجی را به عنوان یک خطا مطرح می کنیم.
در صورت موفقیت، یک فرمان پایگاه داده را برای درج یا حذف آیتم Todo اجرا می کنیم.
در پایان، ما استفاده می کنیم redirect
برای هدایت کاربر از تماس اقدام سرور. این مورد نیاز است زیرا نمیخواهیم کاربر از بازخوانی صفحه مرورگر برای ایجاد یا حذف مجدد مورد Todo، با استفاده مجدد از فرم ارسال استفاده کند.
ما هم اجرا کردیم allTodos
در اینجا عمل کنید تا همه کدهای مربوط به ذخیره سازی را در یک فایل واحد داشته باشید.
مورد جدیدی اضافه کنید
برای پیاده سازی کامپوننت AddTodo، یک را ایجاد کنید src/AddTodo.tsx فایل با محتوای زیر:
import { useActionState } from "@lazarv/react-server/router";
import type { ZodIssue } from "zod";
import { addTodo } from "./actions";
export default function AddTodo() {
const { formData, error } = useActionState<
typeof addTodo,
string & Error & ZodIssue[]
>(addTodo);
return (
<form action={addTodo} className="mb-4">
<div className="mb-2">
<input
name="title"
type="text"
className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg p-2.5"
defaultValue={formData?.get?.("title") as string}
autoFocus
/>
div>
<button
className="text-white bg-blue-700 hover:bg-blue-800 rounded-lg px-5 py-2 mb-2 text-center"
type="submit"
>
Submit
button>
{error?.map?.(({ message }, i) => (
<p
key={i}
className="bg-red-50 border rounded-lg border-red-500 text-red-500 p-2.5 mb-2"
>
{message}
p>
)) ??
(error && (
<p className="bg-red-50 border rounded-lg border-red-500 text-red-500 p-2.5">
{error}
p>
))}
form>
);
}
ما قبلاً می دانیم که چگونه از اقدامات سرور از a استفاده کنیم . But what about the result of our server action call?
useActionState
برای نجات!
با عبور از addTodo
مرجع عملکرد عملکرد سرور به useActionState
، می توانیم نتیجه فراخوانی عمل سرور را زمانی که این اکشن سرور خاص فراخوانی شد دریافت کنیم، بنابراین می توانیم آن را جمع آوری کنیم error
نتیجه این مسائل مربوط به خطای Zod خواهد بود که در عمل افزودن سرور پرتاب کردیم. بنابراین میتوانیم در اینجا همه مسائل مربوط به اعتبار سنجی Zod را تکرار کنیم و پیامهای خطای اعتبارسنجی را در سمت سرور ارائه کنیم.
ساخت تولید
مانند @lazarv/react-server
در حال استفاده از Vite برای سرور توسعه و ساخت تولید است، ما باید یک پیکربندی کوچک Vite ایجاد کنیم تا بسازیم بهتر-sqlite3 وابستگی خارجی در ساخت تولید ما نمی خواهیم این با برنامه ما همراه شود.
export default {
build: {
rollupOptions: {
external: ["better-sqlite3"],
},
},
};
موارد فوق را در a قرار دهید vite.config.ts فایل. چارچوب این را هنگام ساخت برای تولید انتخاب می کند.
در حین استفاده از سرور توسعه، می توانید متوجه شوید که صفحه برخی از ماژول های جاوا اسکریپت را در مرورگر بارگذاری کرده است. این فقط برای تعویض ماژول داغ استفاده می شود. در ساخت تولید، فقط سند و یک دارایی CSS در مرورگر بارگذاری میشوند.
برای ساختن برای تولید، اجرا کنید pnpm build
و سپس می توانید سرور تولید را با استفاده از آن راه اندازی کنید pnpm start
.
سخنان پایانی
خودشه! ما یک نمونه کار کوچک Todo داریم. شما همچنین می توانید نمونه را در GitHub پیدا کنید. با استفاده از روش فوق، اضافه کردن ویژگی به روز رسانی مورد Todo برای شما آسان خواهد بود و یک مورد را به عنوان تکمیل شده علامت گذاری می کند. امیدوارم لذت برده باشید و تجربه توسعه دهنده ای که فریم ورک @lazarv/react-server ارائه می دهد را دوست داشته باشید! بسیاری از ویژگی های هیجان انگیز دیگر توسط React و خود فریم ورک ارائه شده است! ما در اینجا از هیچ مؤلفه کلاینت استفاده نکرده ایم، این در یک آموزش دیگر خواهد بود. سایت مستندات @lazarv/react-server
با استفاده از فریم ورک ایجاد شد و ساخت و استقرار آن در Vercel حدود 5 ثانیه طول می کشد! از آنجایی که چارچوب از Vite استفاده می کند، تجربه توسعه دهنده بسیار سریع است!