برنامه نویسی

ساخت TodoList با Svelte & Firebase

Svelte فریم ورک جدید و درخشان برای توسعه دهندگان فرانت اند است. به نظر می رسد که عملکرد بسیار متفاوتی نسبت به سه چارچوب بزرگ دارد. ما در این مقاله به بررسی عمیق آن خواهیم پرداخت.

فهرست مطالب

  1. پیش نیازها

  2. معرفی

  3. راه اندازی Firebase

  4. ساخت Frontend با Svelte

  5. نتیجه

پیش نیازها

  • درک صحیح جاوا اسکریپت و اکوسیستم آن
  • Node v14 یا بالاتر
  • یک Node Package Manaager (npm یا yarn)

معرفی

Svelte یک چارچوب جدید جاوا اسکریپت (در واقع یک کامپایلر) است که توسط Rich Harris ایجاد شده است. این یک تغییر اساسی در نحوه ویرایش DOM (ماژول شیء سند) ایجاد کرده است.

Svelte در زمان ساخت اجرا می شود و کدهای ضروری را که DOM را به روز می کند، بیرون می زند. این امر نیاز به DOM مجازی مورد استفاده در سایر فریمورک ها را از بین می برد. همچنین دارای ویژگی‌هایی مانند «طراحی محدوده» و «تخصیص واکنشی» است. علاوه بر این، Svelte همچنین دارای “فروشگاه” برای کمک به ذخیره وضعیت است.

راه اندازی Firebase

ایجاد پروژه

  • اگر حساب Google ندارید، می‌توانید برای ایجاد آن و ورود به سیستم ادامه دهید.

  • به صفحه اصلی Firebase بروید و روی دکمه “Go To Console” کلیک کنید

صفحه اصلی Firebase

در کنسول، با کلیک کردن روی نماد ‘+’ یک پروژه جدید ایجاد کنید و هر نامی که می خواهید به آن بدهید.

اضافه کردن پروژه در Firebase

من مال خود را ‘svelte-todo’ می نامم و تیک Google Analytics را برداریم زیرا به آن نیازی نداریم.

ایجاد پروژه در Firebase

راه اندازی Google OAuth

ما باید یک اپلیکیشن برای پلتفرمی که می سازیم بسازیم. روی نماد وب کلیک کنید.

برنامه وب را در Firebase اضافه کنید

پس از ثبت نام برنامه با نام مستعار انتخابی خود، این را مشاهده خواهید کرد:

متغیرهای Firebase SDK

شی firebaseConfig را کپی کنید زیرا بعداً از آن استفاده خواهیم کرد. سعی نکنید از متغیرهای موجود در تصویر استفاده کنید زیرا حذف شده بودند.

برگردید به کنسول، روی Authentication در نوار کناری کلیک کنید

لینک احراز هویت Firebase

روی «شروع به کار» کلیک کنید و Google را به عنوان ارائه دهنده تأیید اعتبار خود انتخاب کنید.

انتخاب Google به عنوان ارائه دهنده تأییدیه

ارائه دهنده را فعال کنید، ایمیل پشتیبانی خود را انتخاب کنید و روی «ذخیره» کلیک کنید.

فعال کردن Google به عنوان ارائه دهنده تأییدیه

راه اندازی Firestore

برای راه اندازی Firestore، به صفحه اصلی کنسول برگردید و روی “Cloud Firestore” کلیک کنید.

باز کردن کنسول Firestore

روی “ایجاد پایگاه داده” کلیک کنید، آن را روی “حالت تست” تنظیم کنید و مکان را همانطور که هست رها کنید.

ایجاد فروشگاه

در این مرحله، 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


Enter fullscreen mode

Exit fullscreen mode

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>

Enter fullscreen mode

Exit fullscreen mode

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 to scss.

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
Enter fullscreen mode

Exit fullscreen mode

and you should see this:

Svelte Default Page with blue header

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:

Folder Structure

We’re going to use a package called page. Run

yarn add page
Enter fullscreen mode

Exit fullscreen mode

to install it and open package.json when it’s done installing. Change the start script to

"start": "sirv public --single"
Enter fullscreen mode

Exit fullscreen mode

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} />
Enter fullscreen mode

Exit fullscreen mode

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.jsfile 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

Enter fullscreen mode

Exit fullscreen mode

Running

  yarn dev
Enter fullscreen mode

Exit fullscreen mode

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>
Enter fullscreen mode

Exit fullscreen mode

  <?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>
Enter fullscreen mode

Exit fullscreen mode

  <?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>
Enter fullscreen mode

Exit fullscreen mode

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;
}
Enter fullscreen mode

Exit fullscreen mode

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 کار مهم انجام می دهیم:

  1. ما Firebase را با متغیرهای پیکربندی خود مقداردهی اولیه می کنیم.
  2. ما تمام توابع ابزاری را که در کامپوننت ها استفاده خواهیم کرد صادر می کنیم.
  • را 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

Enter fullscreen mode

Exit fullscreen mode

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>
Enter fullscreen mode

Exit fullscreen mode

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>
Enter fullscreen mode

Exit fullscreen mode

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
_
.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا