# نمایش widgetها و layout

# React

برای پروژه می‌خوایم از کتابخونه React استفاده کنیم که یک کتابخونه است برای ساخت روابط کاربری (User Interfaces)

برای آشنایی با React این لینک رو مطالعه کن. نمی‌خواد که مراحل tutorial رو رو سیستم خودت بری جلو، اما انتظار میره که بعد خوندن اون صفحه، با مفاهیم React آشنا باشی. به طور خاص، موارد زیر:

  • آشنایی کلی با React components
  • props رو چجوری به component میدن و component چجوری ازشون استفاده می‌کنه؟
  • تابع render کامپوننت‌های react کی صدا زده میشن.
  • JSX چیه؟
  • کد JSX نهایتا چجوری تو runtime اجرا میشه؟
  • نحوه استفاده از تابع onClick
  • state در کمپوننت‌ها
  • چرا نباید this.state رو مستقیم عوض کرد؟
  • چرا عوض کردن مقادیر به طور مستقیم یا اصطلاحا mutate کردن خوب نیست؟
  • functional کمپوننت‌ها کی به کار میان و مزیتشون چیه؟

در فصل قبل دستور create-react-app ساختار اولیه پروژه رو تولید کردی. همون دایرکتوری blog رو با ادیتور باز کن و لیست فایل‌هایی که ساخته شدند رو ببین. توضیح در مورد structure پروژه رو اینجا بخون. تو همون صفحه بخش philosophy و What's included می‌تونن کمکت کنند.

# تعریف پروژه

پروژه‌ای که قراره بنویسی یه بخشی از دیوار به صورت خیلی ساده‌ست که نتیجه نهایی‌ش اینه که با استفاده از مفهوم widget در دیوار صفحه‌های مختلف با UI متفاوت ساخته می‌شند.

# پیش‌نیازها

اول باید در مورد کارکرد React و JSX بیشتر یاد بگیری. موارد زیر رو مطالعه کن:

# ساخت یه widget

ما در دیوار یه مفهوم به نام widget داریم. widget توصیفی برای ساخت یه بخش از UI به همراه action اونه. این توصیف از سمت back-end فرستاده می‌شه و بر اساس آن UI ساخته خواهد شد. به این ساختار دقت کن:

{
  "widget_type": "FEATURE_ROW",
  "data": {
    "title": "بررسی فنی بیش از <b>۴۰۰</b> نقطه‌ خودرو",
    "image_url": "https://s100.divarcdn.com/static/icons/4/ic_checked.png",
    "image_color": "SUCCESS_PRIMARY",
    "has_divider": true,
    "icon": {
      "image_url_dark": "https://s100.divarcdn.com/static/imgs/widget-icons/dark/success_primary/checked.png",
      "image_url_light": "https://s100.divarcdn.com/static/imgs/widget-icons/light/success_primary/checked.png",
      "icon_name": "CHECKED",
      "icon_color": "SUCCESS_PRIMARY"
    }
  }
}

دو بخش مهم این‌هاست:

  • widget_type نوع این widget را مشخص می‌کنند.
  • data اطلاعات لازم برای نمایش widget را مشخص می‌کند.

با توجه به این داده‌ها می‌توان UI زیر را درست کرد:

Result

می‌بینی که هر کدام از مقدارها چگونه استفاده شده‌ند.

# تمرین

۱. فایل src/App.js رو باز کن. می‌خوایم با استفاده از JSON بالا UI توی عکس رو درست کنی. برای راحتی JSON رو توی یه متغیر بذار. این widget رو می‌خوایم توی پاراگرافی که کلاس App-intro داره نشون بدیم. پس متن فعلی داخل این tag رو پاک کن.

بالای فایل، قبل از شروع کلاس App، متغیر widget رو مثل کد بالا تعریف کن.

۲. تحقیق کن که چرا React، نمی‌ذاره تگ b که داخل title بود render بشه و در قدم بعد تحقیق کن که چه‌جوری این مشکل حل می‌شه.

# ساخت widget با action

بعضی از widgetها علاوه بر نمایش یه بخش از صفحه باید یه کاری هم انجام بدند(action). این action هم همراه با widget فرستاده می‌شه. فرض ما اینه که هر widget فقط یه action داره که همه‌ی اون رو دربرمی‌گیره.

{
  "widget_type": "SCORE_ROW",
  "data": {
    "title": "بدنه",
    "percentage_score": 88,
    "score_color": "SUCCESS_PRIMARY",
    "image_url": "https://s100.divarcdn.com/static/icons/4/ic_vehicles.png",
    "image_color": "ICON_SECONDARY",
    "action": {
      "type": "SHOW_INSPECTION_ITEM",
      "payload": {
        "type": "type1",
        "slug": "YmFkYW5l"
      }
    },
    "has_divider": true,
    "icon": {
      "image_url_dark": "https://s100.divarcdn.com/static/imgs/widget-icons/dark/icon_secondary/car_body.png",
      "image_url_light": "https://s100.divarcdn.com/static/imgs/widget-icons/light/icon_secondary/car_body.png",
      "icon_name": "CAR_BODY",
      "icon_color": "ICON_SECONDARY"
    }
  }
}

با این JSON می‌شه این widget رو ساخت:

Result

برای پیاده‌سازی action این‌ها مهمه:

  • type مشخص می‌کنه چه actionی قراره اجرا بشه.
  • payload اطلاعاتی که برای فرستادن request اون action نیازه.

این typeها قراردادی بین front-end و back-end هستند. ما می‌دونیم هر type قراره چه کار کنه. ممکنه لازم باشه به یه endpoint درخواست بفرسته یا به یه صفحه‌ی دیگه بره یا…

مثلاً برای SHOW_INSPECTION_ITEM با استفاده از slug به یه endpoint درخواست AJAX می‌فرستیم و پاسخ رو زیرش نشون می‌دیم.

/car-inspection/YmFkYW5l/type1

# تمرین

۱. با توجه به JSON ازت می‌خوایم که widget رو مثل عکس درست کنی.

۲. در ادامه action رو هم به‌ش اضافه کن. برای شبیه‌سازی درخواست action فرض کن JSON زیر در پاسخ می‌آد:

{
  "widget_type": "DESCRIPTION_ROW",
  "data": {
    "title": "بدنه",
    "text": "کاپوت سنگ خوردگی دارد. به دلیل وجود فلاپ امکان بررسی رکاب ها وجود ندارد.",
    "has_divider": true
  }
}

این خودش یه widgetه و برای نمایشش از همون روش گفته‌شده باید استفاده کنیم. نمایشش این‌جوریه:

Result

# لیست widgetها

علاوه بر نمایش تکی widgetها ما نیاز داریم که لیستی از اون‌ها رو نمایش بدیم.

[
  {
    "widget_type": "TITLE_ROW",
    "data": {
      "text": "کیا اسپورتیج 2400cc",
      "text_color": "TEXT_PRIMARY"
    }
  },
  {
    "widget_type": "UNEXPANDABLE_ROW",
    "data": {
      "title": "تاریخ انجام کارشناسی",
      "value": "۹۹/۰۵/۱۲"
    }
  },
  {
    "widget_type": "SECTION_DIVIDER_ROW"
  },
  {
    "widget_type": "SCORE_ROW",
    "data": {
      "action": {
        "type": "SHOW_INSPECTION_ITEM",
        "payload": {
          "slug": "YmFkYW5l"
        }
      },
      "title": "بدنه",
      "percentage_score": 88,
      "score_color": "SUCCESS_PRIMARY",
      "image_url": "https://s100.divarcdn.com/static/icons/4/ic_vehicles.png",
      "image_color": "ICON_SECONDARY",
      "has_divider": true,
      "icon": {
        "image_url_dark": "https://s100.divarcdn.com/static/imgs/widget-icons/dark/icon_secondary/car_body.png",
        "image_url_light": "https://s100.divarcdn.com/static/imgs/widget-icons/light/icon_secondary/car_body.png",
        "icon_name": "CAR_BODY",
        "icon_color": "ICON_SECONDARY"
      }
    }
  },
  {
    "widget_type": "SCORE_ROW",
    "data": {
      "action": {
        "type": "LOAD_PAGE",
        "payload": {
          "slug": "Esdqwe232="
        }
      },
      "title": "لاستیک و رینگ‌ها",
      "percentage_score": 60,
      "score_color": "WARNING_SECONDARY",
      "image_url": "https://s100.divarcdn.com/static/icons/4/ic_car_tires.png",
      "image_color": "ICON_SECONDARY",
      "has_divider": true,
      "icon": {
        "image_url_dark": "https://s100.divarcdn.com/static/imgs/widget-icons/dark/icon_secondary/car_tires.png",
        "image_url_light": "https://s100.divarcdn.com/static/imgs/widget-icons/light/icon_secondary/car_tires.png",
        "icon_name": "CAR_TIRES",
        "icon_color": "ICON_SECONDARY"
      }
    }
  }
]

همون‌جور که می‌بینی هر کدوم از این widgetها می‌تونند typeها و actionهای مختلف داشته باشند.

با این JSON می‌شه این UI رو درست کرد:

Result

Result

قدم بعدی اینه که این لیست رو از یه promise بگیری. اول در مورد promiseها که تو ES6 به خود JavaScript اضافه شدند، بخون. سپس در مورد state کامپوننت‌ها تو React بخون. برای درک بیشتر lifecycle methodها توی ری‌اکت این لینک رو هم بخون تا ترتیب و زمان دقیق فراخوانی متدهای کامپوننت‌ها رو بدونی و اینکه هدف هر کدوم چیه و کجا میشه ازشون استفاده کرد. همچنین برای مقداردهی اولیه به state و متدهایی که باید bind بشن تا بتونن با state کار کنن این لینک رو بخون. همچنین درباره‌ی hookها می‌تونی این‌جا رو بخونی(در ادامه هر جا که خواستی می‌تونی به جای class componentها از functional componentها و hookها استفاده کنی).

بعد، فایل widget-service.js رو مثل کد زیر تعریف کن:

import { widgets } from './widgets'; 

export const WidgetService = () => Promise.resolve(widget);

توی تابع lifecycle مناسب، widgetها رو از این سرویس بگیر و جای مناسب بریز تا داخل تابع render بتونی نشونشون بدی. قبل‌تر خوندیم که برای request زدن به سرور از xhr میشه استفاده کرد. برای این‌کار بهتره با fetch آشنا بشی و از این به بعد از اون استفاده کنی. این لینک هم شیوه‌ی استفاده از fetch رو توضیح داده.

# فرستادن درخواست

برای ادامه لازمه یه server آزمایشی داشته باشیم که widgetها رو از اون بگیریم.

git clone https://github.com/divar-ir/frontend-course-server.git
cd frontend-course-server
yarn
yarn start

بعد از اجرای کد بالا server روی https://localhost:3000 بالا می‌آد.

حالا تابع WidgetService رو جوری تغییر بده که widgetها رو از آدرس زیر بگیره و تو صفحه نشون بده:

https://localhost:3000/api/v1/widgets

فعلاً نیازی نیست actionهاشون رو پیاده کنی.

وقتی کاربر صفحه رو load کرده، اما هنوز لیست widgetها نیومده، باید loading مناسب به کاربر نشون بدیم. اول اینجا رو که در مورد conditional renderingه بخون. حالا کاری کن که تا وقتی لیست widgetها نیومده، کاربر به جای صفحه خالی، متن «در حال بارگذاری...» رو ببینه.

وقتی مستقیماً درخواست به server می‌زنی با پیغام خطای CORS مواجه خواهی شد. درباره CORS این لینک رو بخون تا علت وجود چنین مکانیزمی رو در مرورگرها بدونی. برای اینکه پروژه رو بخوای ادامه بدی و ریکوئست بزنی ۳ راه داری

  1. اجرای کروم با فلگ disable-web-security

  2. نصب پلاگین کروم

  3. استفاده از امکان پراکسی در webpack dev server

درباره هر سه روش مطالعه کن و با هر کدوم راحت‌تر بودی (روش سوم رو بهت پیشنهاد میکنم) کار رو ادامه بده.

# ساخت layout

رسیدیم به بخشی که باید ظاهر سایت‌مون رو کمی مرتب‌تر کنیم.

بدین منظور، اول باید در مورد تگ‌های معنایی وب semantic elements اطلاعات بیشتری کسب کنی. می‌خوایم برای ایجاد صفحه پست‌ها از المنت‌ها استفاده کنیم. اطلاعات بیشتر استفاده از این تگ‌های جدید برای ساختن وبسایت رو می‌تونید در MDN جستجو کنید.

در ادامه این مقاله در مورد ساختار یک وبلاگ با این عناصر رو بخون.

ما می‌خوایم برای پیاده‌سازی layout از استایل‌های Twitter Bootstrap استفاده کنیم و نسخه Sass بوتسترپ رو در پروژه به کار ببریم.

# SASS

Sass یه پیش‌پردازنده برای CSS هست که توسعه‌ی استایل‌ صفحات وب رو به خصوص در اپلیکیشن‌های بزرگ، ساده‌تر می‌کنه. سینتکس Sass شبیه به CSS هست و تمام کدهای CSS برای Sass معتبر هستن. اما کد‌های Sass الزاما مطابق CSS نیستن و باید توسط یه کامپایلر به CSS تبدیل بشن. برای آشنایی با Sass اینجا و اینجا رو ببین و داکیومنت‌ Sass رو هم بخون.

# Twitter Bootstrap

Twitter Bootstrap یه فریم‌ورک برای ایجاد صفحات وب هست که شامل استایل‌ها و المنت‌های UI و… میشه. در این پروژه ما می‌خوایم بوتسترپ رو با npm نصب کنیم و محتویات داخل این فایل رو جایی داخل پروژه کپی کنیم و قسمت‌هایی که نمی‌خوایم رو کامنت کنیم تا در کد نهایی وجود نداشته‌باشن.

  • این کار کمک می‌کنه که حجم فایل نهایی که توسط کاربر گرفته میشه، کم‌تر باشه.
  • برای این که بتونی ماژول‌های داخل فایل bootstrap.scss رو داخل پروژه import کنی، باید آدرسی که فایل‌ها ازش import میشن رو عوض کنی.

برای آشنایی بیشتر با Twitter Bootstrap می‌تونی مستنداتش رو بخونی.

پروژه create-react-app که باهاش پروژه رو راه‌اندازی کردیم، به طور پیش‌فرض از پیش‌پردازنده‌های CSS استفاده نمی‌کنه و ما باید این قابلیت رو خودمون به پروژه اضافه کنیم. اینجا نوشته که چیکار باید کرد.

چیزی که ما در این مرحله خواهیم دید، همچین چیزی خواهد بود:

Result

هدر: قسمت بالا، شامل عنوان و توضیح کوتاهی در مورد بلاگ هست، که متن‌هاش سمت کلاینت هارد-کد شدند.

سایدبار: سمت چپ، یه ستون هست که فعلن فقط لینک‌های وبلاگ داخلش هستند. اما بعدا قراره چیز‌های بیشتری این تو بیاریم.

فوتر: در عکس بالا نمایش داده نشده اما یه فوتر انتهای بلاگ درنظر بگیر که فعلا نوشته مربوط به کپی‌رایت داخلش باشه. نکته مهم درباره فوتر اینه که همیشه باید در انتهای صفحه (نه پنجره مرورگر) نمایش داده بشه. (مثلن اگه هنوز متن ستون‌ها و ارتفاعشون کم هست، فوتر وسطِ صفحه نیاد و همیشه پایین صفحه نمایش داده بشه. اما اگه متن ستون‌ها زیاد شد و صفحه اسکرول عمودی داشت، فوتر هم به تناسب میره پایین و زیر دو ستون در انتهای صفحه قرار می‌گیره). این کار رو باید بدون استفاده از جاوااسکریپت انجام بدی.

مطالب: سمت راست، قسمت مطالب هست که شامل عنوان، نویسنده، تاریخ ارسال و متن هست.

سازگار با موبایل: و بعد layout بالا رو طوری تغییر بده که در حالت موبایل (مثلا عرض نمایشگر کمتر-مساوی ۷۶۷ پیکسل) ستون سمت چپ زیر ستون سمت راست نمایش داده بشه.

نکاتی که انتظار میره بهشون بها داده شده باشه، اینا هستن:

  1. از تگ‌های معنایی استفاده شده باشه. (مثل:‌<article> و <header> و <section> و <main> و …)

  2. از این تگ‌ها به درستی استفاده شده باشه.

  3. مطالب از نظر بصری خوانا باشند. (مثل line-height و فونت و…)

  4. مرز بین سه قسمتِ هدر، ساید‌بار و مطالب، به لحاظ بصری مشخص باشه.

آخرین بروزرسانی: 10/6/2020, 1:21:56 PM