ساخت TodoList با Svelte & Firebase

Svelte فریم ورک جدید و درخشان برای توسعه دهندگان فرانت اند است. به نظر می رسد که عملکرد بسیار متفاوتی نسبت به سه چارچوب بزرگ دارد. ما در این مقاله به بررسی عمیق آن خواهیم پرداخت.
فهرست مطالب
-
پیش نیازها
-
معرفی
-
راه اندازی Firebase
-
ساخت Frontend با Svelte
-
نتیجه
پیش نیازها
- درک صحیح جاوا اسکریپت و اکوسیستم آن
- Node v14 یا بالاتر
- یک Node Package Manaager (npm یا yarn)
معرفی
Svelte یک چارچوب جدید جاوا اسکریپت (در واقع یک کامپایلر) است که توسط Rich Harris ایجاد شده است. این یک تغییر اساسی در نحوه ویرایش DOM (ماژول شیء سند) ایجاد کرده است.
Svelte در زمان ساخت اجرا می شود و کدهای ضروری را که DOM را به روز می کند، بیرون می زند. این امر نیاز به DOM مجازی مورد استفاده در سایر فریمورک ها را از بین می برد. همچنین دارای ویژگیهایی مانند «طراحی محدوده» و «تخصیص واکنشی» است. علاوه بر این، Svelte همچنین دارای “فروشگاه” برای کمک به ذخیره وضعیت است.
راه اندازی Firebase
ایجاد پروژه
-
اگر حساب Google ندارید، میتوانید برای ایجاد آن و ورود به سیستم ادامه دهید.
-
به صفحه اصلی Firebase بروید و روی دکمه “Go To Console” کلیک کنید
در کنسول، با کلیک کردن روی نماد ‘+’ یک پروژه جدید ایجاد کنید و هر نامی که می خواهید به آن بدهید.
من مال خود را ‘svelte-todo’ می نامم و تیک Google Analytics را برداریم زیرا به آن نیازی نداریم.
راه اندازی Google OAuth
ما باید یک اپلیکیشن برای پلتفرمی که می سازیم بسازیم. روی نماد وب کلیک کنید.
پس از ثبت نام برنامه با نام مستعار انتخابی خود، این را مشاهده خواهید کرد:
شی firebaseConfig را کپی کنید زیرا بعداً از آن استفاده خواهیم کرد. سعی نکنید از متغیرهای موجود در تصویر استفاده کنید زیرا حذف شده بودند.
برگردید به کنسول، روی Authentication در نوار کناری کلیک کنید
روی «شروع به کار» کلیک کنید و Google را به عنوان ارائه دهنده تأیید اعتبار خود انتخاب کنید.
ارائه دهنده را فعال کنید، ایمیل پشتیبانی خود را انتخاب کنید و روی «ذخیره» کلیک کنید.
راه اندازی Firestore
برای راه اندازی Firestore، به صفحه اصلی کنسول برگردید و روی “Cloud Firestore” کلیک کنید.
روی “ایجاد پایگاه داده” کلیک کنید، آن را روی “حالت تست” تنظیم کنید و مکان را همانطور که هست رها کنید.
در این مرحله، Firestore باید برای پروژه شما فعال می شد. می توانید مجموعه ها یا اسناد را از اینجا اضافه کنید تا با آن بازی کنید.
ساخت پیشانی با Svelte
برای ایجاد یک پروژه Svelte، اجرا کنید
npx degit sveltejs/template [PROJECT-NAME]
سپس اجرا کنید
cd [PROJECT-NAME]
برای ورود به آن دایرکتوری
این یک پروژه Svelte اساسی را از این الگو برای ما ایجاد می کند. با این حال، ما هنوز باید بستههایی را که با الگو ارائه میشوند نصب کنیم. برای این کار می توانیم از نخ یا npm استفاده کنیم اما من از نخ استفاده می کنم.
اجرا کن
yarn
برای نصب بسته ها، سپس اجرا کنید
yarn dev
برای راه اندازی سرور
به URL مشخص شده در “Local” بروید و برنامه Svelte خود را در حال اجرا خواهید دید.
فعال کردن SCSS در پروژه
برای انجام این کار، باید چند بسته جدید را نصب کنیم تا به تجزیه SCSS کمک کنیم. اجرا کن
yarn add -D node-sass svelte-preprocess
را ویرایش کنید rollup.config.js
فایل در ریشه دایرکتوری به شکل زیر باشد:
وارد كردن شیک از جانب "rollup-plugin-svelte";
وارد كردن commonjs از جانب "@rollup/plugin-commonjs";
وارد كردن برطرف کردن از جانب "@rollup/plugin-node-resolve";
وارد كردن بار زنده از جانب "rollup-plugin-livereload";
وارد كردن { ترسر } از جانب "rollup-plugin-terser";
وارد كردن css از جانب "rollup-plugin-css-only";
//خط جدید اینجا
وارد كردن پیش پردازش از جانب "پیش فرآیند نرم";
پایان تولید = !روند.env.ROLLUP_WATCH;
//کد برای اختصار حذف شده است
صادرات پیش فرض {
ورودی: "src/main.js"،
خروجی: {
نقشه منبع: درست است، واقعی،
قالب: "iife"،
نام: "برنامه"،
فایل: "public/build/bundle.js"،
}،
پلاگین ها: [
svelte({
//NEW LINE HERE
preprocess: preprocess(),
compilerOptions: {
dev: !production,
},
}),
css({ output: "bundle.css" }),
//CODE OMITTED FOR BREVITY
To test if SCSS works, go into the App.svelte
file and paste the below:
<script>
export let name;
</script>
<main>
<h1>Hello {name}!</h1>
<p>
Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn
how to build Svelte apps.
</p>
</main>
<style lang="scss">
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
h1 {
color: #1900ff;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
I’m sure you’re already wondering how this file works. Well, Svelte expects each component to have three sections:
- The Script: It is represented by the
<script></script>
tag and contains most javascript related functionalities. - The Component Body: This is the main part of our component. It includes the HTML and dictates what is to be displayed.
- The Component Styles: It is represented by the
<style></style>
tag and contains our styling code which is scoped to this component only. In our case, we’re using SCSS so we set the lang attribute on the style tag toscss
.
Our component is to accept a prop called name
from its parent. export let name
achieves this functionality for us. We can accept as many props as we want.
In the component body, we can display any of the variables in our script by wrapping them in curly braces.
Now that we understand the component, you can start the server again by running
yarn dev
and you should see this:
Enabling Routing with page.js
Since we’re building an app that has users, we’ll need a page to log in and another to add/view your todos. In the src
folder, create a folder pages
and add home.svelte
and login.svelte
into it.
The folder structure should look like this:
We’re going to use a package called page
. Run
yarn add page
to install it and open package.json
when it’s done installing. Change the start
script to
"start": "sirv public --single"
Finally, go into the App.svelte
file and paste the below.
<script>
import router from "page";
import Home from "./pages/home.svelte";
import Login from "./pages/login.svelte";
let page;
router("/", () => page = Home);
router("/login", () => page = Login);
router.start();
</script>
<svelte:component this={page} />
We’re importing the router, adding our different routes and starting the router. Our pages are empty but before adding their content, we need to set up our store. In the src
folder, create a stores.js
file and paste the following into it:
import { writable } from "svelte/store";
export const TodoStore = writable([])
صادرات پایان فروشگاه کاربران = قابل نوشتن(خالی)
writable
به ما کمک می کند فروشگاهی ایجاد کنیم که بتوانیم آن را ویرایش کنیم. سپس 2 فروشگاه جدید ایجاد می کنیم، الف TodoStore
و الف UserStore
به ترتیب لیست کارهای انجام شده و مشخصات کاربر را نگه دارید. همه writable
فروشگاه ها 2 روش اصلی دارند:
- Set: برای تنظیم ارزش فروشگاه.
- به روز رسانی: برای به روز رسانی ارزش فروشگاه بر اساس عملکرد پاسخ به تماس.
باز کن login.svelte
فایل و پیست:
<script>
import { UserStore } from "./../stores";
import page from "page";
$: if ($UserStore) page.redirect("/");
</script>
<div class="login container">
<button>Log In</button>;
</div>
<style lang="scss">
div.login.container {
background: hsla(238, 100%, 71%, 1);
background: linear-gradient(
135deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
background: -moz-linear-gradient(
135deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
background: -webkit-linear-gradient(
135deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
button {
background-color: hsla(350, 100%, 69%, 1);
height: 200px;
width: 200px;
box-shadow: -10px -10px 15px rgb(255, 119, 142),
10px 10px 15px rgb(231, 88, 112);
border: none;
border-radius: 50%;
border: 8px solid hsla(350, 100%, 69%, 1);
font-weight: bold;
color: white;
font-size: 2rem;
&:focus {
box-shadow: -10px -10px 15px rgb(255, 119, 142),
10px 10px 15px rgb(231, 88, 112),
inset -10px -10px 15px rgb(255, 119, 142),
inset 10px 10px 15px rgb(231, 88, 112);
}
}
}
</style>
در اینجا، ما یک دکمه بزرگ در مرکز نمایش داده میشویم که با کلیک روی آن کاربر وارد سیستم میشود. را $UserStore
برای دسترسی به مقدار داخل فروشگاه استفاده می شود.
در Svelte، اظهارات بعد از آن نوشته شده است $:
مانند $: if ($UserStore) page.redirect("https://dev.to/")
نامیده می شوند Reactive Statements
. اگر مقادیری که به آنها وابسته هستند تغییر کرده باشند، قبل از بهروزرسانی یک مؤلفه اجرا میشوند. دلار در $UserStore
و Reactive Statements
عملکرد یکسانی ندارند
عبارت reactive در جزء ما بررسی می کند که آیا $UserStore
دارای یک مقدار است، یعنی اگر کاربر در حال حاضر وارد شده باشد و در صورت وجود آن به صفحه اصلی هدایت شود.
این را در home.svelte
فایل:
<script>
import EditTodo from "../components/edit-todo.svelte";
import ViewTodos from "../components/view-todos.svelte";
import Header from "./../components/header.svelte";
import { UserStore } from "./../stores";
import page from "page";
$: if (!$UserStore) page.redirect("/login");
</script>
<section class="home">
<div class="wrapper">
<Header />
<EditTodo />
<ViewTodos />
</div>
</section>
<style>
section.home {
display: flex;
justify-content: center;
align-items: center;
}
div.wrapper {
width: 100%;
max-width: 800px;
}
</style>
این مؤلفه نسبتاً کوتاه است زیرا هدف آن این است که همه اجزای دیگر ما را در خود جای دهد. همچنین دارای یک عبارت واکنشی است که مانند آنچه در آن است کار می کند login
جزء. ما به سادگی بررسی می کنیم که آیا هیچ کاربری در حال حاضر وارد نشده است یا خیر، و به آن هدایت می کنیم login
صفحه اگر درست باشد
قطعات وارد شده در home
صفحه هنوز ساخته نشده است، پس بیایید جلو برویم و آنها را بسازیم. ایجاد یک components
پوشه در src
پوشه و اضافه کنید edit-todo.svelte
، header.svelte
، و view-todos.svelte
به آن
باز کن edit-todo.svelte
این را نیز فایل و پیست کنید:
<script>
let name = "";
let priority = "1";
const submitTodo = () => {
const newTodo = {
priority,
name,
completed: false,
};
console.log('Submit Todo', newTodo)
};
</script>
<form>
<header>Add New Todo</header>
<label for="name">
Todo Name
<input bind:value={name} id="name" type="text" />
</label>
<label for="priority">
Todo Priority
<input
bind:value={priority}
id="priority"
min="1"
step="1"
max="5"
type="range"
/>
</label>
<button on:click|preventDefault={submitTodo}>Add Todo</button>
</form>
<style lang="scss">
form {
background: #fff;
border-bottom-left-radius: 2rem;
border-bottom-right-radius: 2rem;
padding: 2rem 4rem;
header {
font-size: 2rem;
text-align: center;
font-weight: bold;
margin-bottom: 1rem;
}
label {
font-size: 1.4rem;
margin-bottom: 1rem;
display: block;
input {
display: block;
width: 100%;
margin-top: 0.5rem;
border-radius: 1rem;
}
&:nth-child(2) {
input {
padding: 0.5rem;
}
}
}
button {
padding: 1rem;
border: none;
background: #f2f9ff;
font-size: 1.4rem;
border-radius: 5px;
margin-top: 1rem;
border: solid 1px #ccc;
position: relative;
left: 50%;
transform: translateX(-50%);
}
}
</style>
در این کامپوننت، فرمی را نمایش می دهیم که کاربران را قادر می سازد تا با وارد کردن نام todo و اولویت آن، todo را ایجاد کنند. متوجه خواهید شد bind:value
در هر دو ورودی الف نامیده می شود directive
و مقدار فعلی آن ورودی را به متغیر مشخص شده متصل می کند.
دکمه موجود در فرم دارای یک ویژگی است on:click|preventDefault
. این نیز یک است directive
اما دارای یک modifier
همچنین. اصلاحکنندهها به سادگی دستورالعملی را که در حال حاضر استفاده میکنیم سفارشی میکنند. on:click
دستورالعملی است که به ما کمک می کند کنترل کننده رویداد کلیک خود را به دکمه متصل کنیم. را |
برای اضافه کردن اصلاح کننده ها و preventDefault
اصلاح کننده را فرا می خواند event.preventDefault()
روش برای ما قبل از فراخوانی کنترل کننده رویداد.
را submitTodo
تابع در حال حاضر از کاری که ما ایجاد کرده ایم خارج می شود. به زودی آن را به Firestore وصل خواهیم کرد.
باز کن header.svelte
فایل و پیست:
<script>
import { UserStore } from "./../stores";
</script>
<header>
{#if $UserStore}
<img src={$UserStore.photoURL} alt="User" />
<p>Welcome, {$UserStore.displayName.split(" ")[0]}</p>
{/if}
<button> Log Out </button>
</header>
<style lang="scss">
header {
width: 100%;
background-color: lightsteelblue;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
& > img {
height: 6rem;
width: 6rem;
border-radius: 50%;
}
& > p {
font-size: 2rem;
text-align: center;
}
& > button {
font-size: 1.5rem;
border: none;
padding: 1rem;
background-color: rgba(255, 0, 0, 0.5);
color: white;
border-radius: 0.5rem;
}
}
</style>
را header
جزء به سادگی نام و تصویر کاربر فعلی و همچنین دکمه خروج را نشان می دهد.
در نهایت، ما آن را داریم view-todos
مؤلفه ای که همه کارها را نمایش می دهد. فایل را باز کنید و موارد زیر را Paste کنید:
<script>
import TodoItem from "./todo-item.svelte";
import { TodoStore } from "./../stores";
let activeTab = "incomplete";
$: incompleteTodos = $TodoStore
.filter((todo) => !todo.completed)
.sort((todoA, todoB) => todoB.priority - todoA.priority);
$: completeTodos = $TodoStore.filter((todo) => todo.completed);
</script>
<main>
<header>
<button
on:click={() => (activeTab = "incomplete")}
class:active={activeTab === "incomplete"}
>Current Todos ({incompleteTodos.length})</button
>
<button
on:click={() => (activeTab = "complete")}
class:active={activeTab === "complete"}
>Completed Todos ({completeTodos.length})</button
>
</header>
<div class="todos">
{#if activeTab === "incomplete"}
{#each incompleteTodos as todo (todo.id)}
<TodoItem {todo} />
{/each}
{:else}
{#each completeTodos as todo (todo.id)}
<TodoItem {todo} />
{/each}
{/if}
</div>
</main>
<style lang="scss">
main {
header {
display: flex;
justify-content: space-between;
padding: 2rem;
button {
background: none;
border: none;
font-size: 1.4rem;
padding-bottom: 0.5rem;
&.active {
border-bottom: solid 1px #333;
}
}
}
.todos {
padding: 1rem 2rem;
}
}
</style>
دو عبارت واکنشی در این مؤلفه به ما کمک می کند تا آرایه هایی را برای کارهای «تکمیل شده» و «ناقص» تولید کنیم. کارهای «ناتمام» را بر اساس اولویت مرتب میکنیم، اما «تکمیلشده» را همانطور که هست رها میکنیم. سپس برگه فعلی را که در آن قرار داریم ردیابی می کنیم و بر اساس آن کارهای درست را نمایش می دهیم.
ما این کار را با {#if CONDITION}
که یک جزء را بسته به شرایط نمایش می دهد. ما نیز استفاده می کنیم {#each ARRAY as ARRAY_ITEM (ITEM_ID)}
برای کمک به ما برای نمایش یک جزء برای هر آیتم در یک آرایه. هر دوی آنها باید با تطبیق بسته شوند {/if}
و {/each}
به ترتیب. به دستور دیگری برمی خوریم، class:active
که بسته به شرایط یک کلاس به عنصر اضافه می کند.
ما یک جزء دیگر وارد کردیم، TodoItem
.
یک فایل جدید در components
پوشه فراخوانده شد todo-item.svelte
و موارد زیر را در فایل قرار دهید:
<script>
export let todo = null;
import TickIcon from "./../assets/tick.svg";
import CancelIcon from "./../assets/cancel.svg";
import StarIcon from "./../assets/star.svg";
$: ratings = new Array(todo.priority);
</script>
<div class="todo">
<button>
{#if todo.completed}
<CancelIcon width="100%" fill="#fff" />
{:else}
<TickIcon width="100%" fill="#fff" />
{/if}
</button>
<div class="info">
<p class:cancel={todo.completed}>{todo.name}</p>
<div class="stars">
{#each ratings as _}
<StarIcon width="20px" />
{/each}
</div>
</div>
</div>
<style lang="scss">
.todo {
display: flex;
align-items: center;
width: 100%;
&:not(:last-child) {
margin-bottom: 10px;
}
button {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
padding: 1rem;
border: none;
background: #1890ff;
margin-right: 1rem;
}
.info {
p {
font-size: 1.4rem;
margin-bottom: 0.5rem;
&.cancel {
text-decoration: line-through;
}
}
}
}
</style>
این جزء الف را می گیرد todo
به عنوان یک پایه و نام خود و همچنین ستاره ها را برای نشان دادن اولویت خود ارائه می دهد. همچنین دارای دکمه ای است که آن را مشخص می کند complete
یا incomplete
حالت.
فقط یک مشکل وجود دارد، Svelte نمی تواند وارد کند .svg
فایل ها به تنهایی ما باید بسته هایی را اضافه کنیم تا به ما در انجام این کار کمک کنند. اجرا کن
yarn add -D rollup-plugin-svelte-svg
و جایگزین کنید rollup.config.js
فایل با این:
//کد برای اختصار حذف شده است
وارد كردن css از جانب "rollup-plugin-css-only";
//خط جدید اینجا
وارد كردن { svelteSVG } از جانب "rollup-plugin-svelte-svg";
//کد برای اختصار حذف شده است
پلاگین ها: [
//NEW LINE HERE
svelteSVG({
svgo: {},
}),
svelte({
preprocess: preprocess(),
compilerOptions: {
dev: !production,
},
}),
//CODE OMITTED FOR BREVITY
Running
yarn dev
would still throw an error as we currently have no assets to import.
Create a folder named assets
in the src
directory and paste these three files:
<?xml version="1.0" ?><svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:none;}</style></defs><title/><g data-name="Layer 2" id="Layer_2"><path d="M4,29a1,1,0,0,1-.71-.29,1,1,0,0,1,0-1.42l24-24a1,1,0,1,1,1.42,1.42l-24,24A1,1,0,0,1,4,29Z"/><path d="M28,29a1,1,0,0,1-.71-.29l-24-24A1,1,0,0,1,4.71,3.29l24,24a1,1,0,0,1,0,1.42A1,1,0,0,1,28,29Z"/></g><g id="frame"><rect class="cls-1" height="32" width="32"/></g></svg>
<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 47.94 47.94" version="1.1" viewBox="0 0 47.94 47.94" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m26.285 2.486 5.407 10.956c0.376 0.762 1.103 1.29 1.944 1.412l12.091 1.757c2.118 0.308 2.963 2.91 1.431 4.403l-8.749 8.528c-0.608 0.593-0.886 1.448-0.742 2.285l2.065 12.042c0.362 2.109-1.852 3.717-3.746 2.722l-10.814-5.685c-0.752-0.395-1.651-0.395-2.403 0l-10.814 5.685c-1.894 0.996-4.108-0.613-3.746-2.722l2.065-12.042c0.144-0.837-0.134-1.692-0.742-2.285l-8.749-8.528c-1.532-1.494-0.687-4.096 1.431-4.403l12.091-1.757c0.841-0.122 1.568-0.65 1.944-1.412l5.407-10.956c0.946-1.919 3.682-1.919 4.629 0z" fill="#ED8A19"/></svg>
<?xml version="1.0" ?><svg id="Layer_1" style="enable-background:new 0 0 50 50;" version="1.1" viewBox="0 0 50 50" xml:space="preserve" fill="#fff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Layer_1_1_"><polygon points="47.293,6.94 14,40.232 2.707,28.94 1.293,30.353 14,43.06 48.707,8.353 "/></g></svg>
We should be error-free now but on running it, the styling might look a bit off. Replace the global.css
file in the public/build
directory with:
html,
body {
position: relative;
width: 100%;
height: 100%;
font-size: 10px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
color: #333;
margin: 0;
box-sizing: border-box;
font-family: Verdana, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a,
button {
cursor: pointer;
}
input,
button,
select,
textarea {
font-family: inherit;
font-size: inherit;
border: 1px solid #ccc;
}
Let’s test out our current setup before hooking up Firebase. Head back to the stores.js
file so we can add some initial data.
import { writable } from "svelte/store";
export const TodoStore = writable([
{
completed: false,
id: 0,
name: "Read my books",
priority: 3,
user: "IBle5KaZruTFAobDKLIqpOksKZ23",
},
{
completed: false,
id: 1,
name: "Cut my hair",
priority: 2,
user: "IBle5KaZruTFAobDKLIqpOksKZ23",
},
{
completed: true,
id: 2,
name: "Write the article",
priority: 3,
user: "IBle5KaZruTFAobDKLIqpOksKZ23",
},
])
صادرات پایان یوزر استور = قابل نوشتن({
نمایش نام: "پسر تایو"،
پست الکترونیک: "Lorddro1532@gmail.com"،
شماره تلفن: خالی،
photoURL:
"https://lh3.googleusercontent.com/a-/AOh14GjhCUWFu5BpCrEK3t2TWml7mvmhTGBM5ROPf7NTWA=s96-c"،
uid: "IBle5KaZruTFAobDKLIqpOksKZ23"،
})؛
حالا باید چیزی شبیه به این بگیریم:
اتصال frontend با Firebase
ما باید متغیرهای پیکربندی Firebase خود را به پروژه اضافه کنیم. به یاد بیاورید که قبلاً آنها را از کنسول Firebase دریافت کردیم. ما نمی توانیم متغیرها را به پروژه اضافه کنیم زیرا آنها باید مخفی نگه داشته شوند. به جای آن از متغیرهای محیطی برای حفظ آنها استفاده می کنیم. یک فایل در ریشه پروژه ایجاد کنید، نام آن را بگذارید .env
و آن را به خود اضافه کنید .gitignore
فایل.
متغیرهای پیکربندی خود را به این صورت در این فایل قرار دهید:
FIREBASE_API_KEY="YOUR_API_KEY"
FIREBASE_AUTH_DOMAIN='YOUR_AUTH_DOMAIN'
PROJECT_ID='YOUR_PROJECT_ID'
STORAGE_BUCKET='YOUR_STORAGE_BUCKET'
MESSAGING_SENDER_ID='YOUR_MESSAGING_SENDER_ID'
APP_ID='YOUR_APP_ID'
در src
پوشه، یک پوشه به نام اضافه کنید services
و یک فایل به نام اضافه کنید firebase.js
به آن این را در فایل قرار دهید.
import { initializeApp } from "firebase/app";
import {
getAuth,
signInWithPopup,
GoogleAuthProvider,
signOut,
} from "firebase/auth";
import {
getFirestore,
collection,
addDoc,
getDocs,
doc,
updateDoc,
query,
where,
} from "firebase/firestore";
import { TodoStore } from "./../stores";
let env = env_var;
env = env.env;
const firebaseConfig = {
apiKey: env.FIREBASE_API_KEY,
authDomain: env.FIREBASE_AUTH_DOMAIN,
projectId: env.PROJECT_ID,
storageBucket: env.STORAGE_BUCKET,
messagingSenderId: env.MESSAGING_SENDER_ID,
appId: env.APP_ID,
};
initializeApp(firebaseConfig);
export const provider = new GoogleAuthProvider();
provider.setCustomParameters({ prompt: "select_account" });
const auth = getAuth();
export const signInWithGoogle = () => signInWithPopup(auth, provider);
export const logOut = () => signOut(auth);
export const db = getFirestore();
export const addTodo = async (todo, uid) => {
try {
let newTodo = {
...todo,
user: uid,
};
const { id } = await addDoc(collection(db, "todos"), newTodo);
newTodo.id = id;
TodoStore.update((current) => [newTodo, ...current]);
} catch (e) {
console.error("Error adding document: ", e);
}
};
export const getTodos = async (uid) => {
if (!uid) return;
const q = query(collection(db, "todos"), where("user", "==", uid));
const querySnapshot = await getDocs(q);
const docs = [];
querySnapshot.forEach((doc) =>
docs.push({
id: doc.id,
...doc.data(),
})
);
TodoStore.set(docs);
};
export const markTodo = async (completed, id) => {
await updateDoc(doc(db, "todos", id), {
completed,
});
TodoStore.update((current) => {
let todoIdx = current.findIndex((todo) => todo.id === id);
current[todoIdx].completed = completed;
return [...current];
});
};
ما در این فایل 2 کار مهم انجام می دهیم:
- ما Firebase را با متغیرهای پیکربندی خود مقداردهی اولیه می کنیم.
- ما تمام توابع ابزاری را که در کامپوننت ها استفاده خواهیم کرد صادر می کنیم.
- را
signInWithGoogle
وlogOut
تابع دقیقاً همان چیزی را انجام می دهد که نام آنها نشان می دهد. - را
addTodo
تابع را ذخیره می کندtodo
آن را به عنوان یک آرگومان به Firestore دریافت می کند و آن را به فروشگاه محلی ما نیز اضافه می کند. - را
markTodo
تابع را به روز می کندcompleted
حالت الفtodo
به صورت محلی و در Firestor. - را
getTodos
تابع برای واکشی همه کارها از Firestore و ذخیره آن در ما فراخوانی شده استTodoStore
ما باید متغیرهای محیطی خود را به پروژه اضافه کنیم تا بتوانیم به آن دسترسی داشته باشیم. بسته هایی را نصب کنید که ما را قادر می سازد این کار را با اجرا انجام دهیم
yarn add -D @rollup/plugin-replace & yarn add dotenv
ما همچنین نیاز به دسترسی به Firebase SDK داریم. آن را با اجرا نصب کنید
yarn add firebase
سپس خود را به روز کنید rollup.config.js
به:
//کد برای اختصار حذف شده است
وارد كردن { svelteSVG } از جانب "rollup-plugin-svelte-svg";
//خط جدید اینجا
وارد كردن جایگزین کردن از جانب "@rollup/plugin-replace";
وارد كردن { پیکربندی } از جانب "dotenv";
//کد برای اختصار حذف شده است
پلاگین ها: [
//NEW LINE HERE
replace({
env_var: JSON.stringify({
env: {
isProd: production,
...config().parsed,
},
}),
}),
svelteSVG({
svgo: {},
}),
//CODE OMITTED FOR BREVITY
Firebase should be set up properly by now. We just have to add Firebase functionalities into the components.
<script>
import { signInWithGoogle } from "./../services/firebase";
import { UserStore } from "./../stores";
import page from "page";
$: if ($UserStore) page.redirect("/");
</script>
<div class="login container">
<button on:click={signInWithGoogle}>Log In</button>;
</div>
With this, we can now sign into the app with our Google Account.
<script>
export let todo = null;
import TickIcon from "./../assets/tick.svg";
import CancelIcon from "./../assets/cancel.svg";
import StarIcon from "./../assets/star.svg";
import { markTodo } from "./../services/firebase";
$: ratings = new Array(todo.priority);
</script>
<div class="todo">
<button on:click={() => markTodo(!todo.completed, todo.id)}>
{#if todo.completed}
<CancelIcon width="100%" fill="#fff" />
{:else}
<TickIcon width="100%" fill="#fff" />
{/if}
</button>
<div class="info">
<p class:cancel={todo.completed}>{todo.name}</p>
<div class="stars">
{#each ratings as _}
<StarIcon width="20px" />
{/each}
</div>
</div>
</div>
We’ve updated the button to call the markTodo function and pass the right arguments.
<script>
import { logOut } from "./../services/firebase";
import { UserStore } from "./../stores";
</script>
<header>
{#if $UserStore}
<img src={$UserStore.photoURL} alt="User" />
<p>Welcome, {$UserStore.displayName.split(" ")[0]}</پ>
{/اگر}
<دکمه بر:کلیک={خروج}> ورود به سیستم بیرون </دکمه>
</سرتیتر>
دکمه “خروج” اکنون می تواند کاربران را از سیستم خارج کند.
<script>
import { UserStore } from "./../stores";
import { addTodo } from "./../services/firebase";
let name = "";
let priority = "1";
const submitTodo = () => {
const newTodo = {
priority,
name,
completed: false,
};
name = ''
priority = '1'
addTodo(newTodo, $UserStore.uid);
};
</script>
<form>
<header>Add New Todo</header>
<label for="name">
Todo Name
<input bind:value={name} id="name" type="text" />
</label>
<label for="priority">
Todo Priority
<input
bind:value={priority}
id="priority"
min="1"
step="1"
max="5"
type="range"
/>
</label>
<button on:click|preventDefault={submitTodo}>Add Todo</button>
</form>
این دکمه را بهروزرسانی میکند تا بتواند اکنون تابع addTodo را فراخوانی کند.
<script>
import router from "page";
import Home from "./pages/home.svelte";
import Login from "./pages/login.svelte";
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { UserStore } from "./stores";
import { onMount } from "svelte";
import { getTodos } from "./services/firebase";
const auth = getAuth();
onMount(async () => {
onAuthStateChanged(auth, async (user) => {
if (user) {
UserStore.set(user);
getTodos(user.uid);
} else {
UserStore.set(user);
getTodos(user.uid);
console.log("User signed out");
}
});
});
let page;
router("/", (ctx, next) => (page = Home));
router("/login", (ctx, next) => (page = Login));
router.start();
</script>
<svelte:component this={page} />
ما به onMount
تابعی که به ما کمک می کند تا پس از نصب یک جزء، یک تابع را اجرا کنیم. را onAuthStateChanged
هر زمان که وضعیت کاربر تغییر کند، تابع فراخوانی خود را پاسخ می دهد. سپس به روز رسانی می کنیم UserStore
بر اساس ارزشی که دریافت می کنیم.
فروشگاه ها را در stores.js
فایل تا Firebase بتواند کنترل و !Voila! انجام شد.
نتیجه
در این مقاله با استفاده از Svelte و Firebase یک Todo List ایجاد کردیم. ما با برخی از اصول اولیه Svelte و نحوه ادغام آن با Firebase آشنا شدیم. پروژه تکمیل شده را می توانید اینجا و کد آن را اینجا ببینید. برای اطمینان از درک کامل مفاهیم مطرح شده در این آموزش، سعی کنید ویژگی های خود را به برنامه اضافه کنید مانند ساختن todos
قابل ویرایش یا حذف
با تشکر از خواندن مقاله ❤. تا زمانی که دوباره همدیگر را ببینیم، من می مانم @the_dro
_.