پنج شیوه رایج در React، که دیگر مجبور نیستید استفاده کنید

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 27 تیر 1397
دسته بندی ها : جاوا اسکریپت

در حال حاضر، به جرعت می‌توان گفت که React یکی از محبوب‌ترین کتابخانه‌های موجود است. مقدار زیادی قابلیت در React وجود دارد و توسعه ‌دهندگان جدید، به علت رابط کاربری خوب آن،‌ به سمت آن کشیده می‌شوند. در حالیکه هم خود کتابخانه و هم اکوسیستم React در طی سال‌های اخیر پیشرفت زیادی کرده‌اند، موارد خاصی پیدا می‌شوند که در مواجهه با آن‌ها از خود می‌پرسید: «روش صحیح انجام دادن آن دقیقا چیست؟»

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

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

بهینهسازی کردن React از ابتدا

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

چرا؟

React در مقایسه با دیگر پلتفرم‌های Front-end آسان‌تر است، زیرا مجبور نیستید برای سریع‌تر کردن فرایند، تمام ماژول‌ها را بازنویسی کنید. مقصر اصلی که مشکلاتی در کارایی ایجاد می‌کند، فرایند اصلاحی است که React برای بروزرسانی DOM مجازی استفاده می‌کند.

بیایید نگاهی به این که React دقیقا چگونه با آن‌ها برخورد می‌کند،‌ داشته باشیم. React بر روی هر render()، یک Tree که از عناصر رابط کاربری تشکیل شده است را ایجاد می‌کند. وقتی که state یا propها بروزرسانی می‌شوند، React مجبور می‌شود که یک Tree جدید با کمترین میزان تغییرات ایجاد کند و همه چیز را ساده‌تر نگه دارد.

فرض کنید یک Tree دارید که به این صورت است:

حال فرض کنید که برنامه داده‌های جدیدی دریافت می‌کند و Node‌های زیر باید بروزرسانی شوند:

معمولا React در آخر به جای این که فقط Nodeهای مربوطه را رندر کند، کل زیرشاخه ‌ها را به این صورت رندر می‌کند:

وقتی که state در کامپوننت‌های بالایی تغییر می‌کند، تمام کامپوننت‌های زیر آن مجددا رندر می‌شوند. این رفتار پیشفرض Raect‌ است و برای برنامه‌های کم حجم مناسب است. همینطور که برنامه رشد می‌کند، بهتر است اندازه‌گیری کارای واقعی با استفاده از Chrome Profiling Tools را در نظر داشته باشید. این ابزار جزئیات دقیقی درباره زمان هدر رفته برای رندر‌های ناخواسته را به شما می‌دهد. اگر عدد به دست آمده بزرگ است، سپس می‌توانید فرایند رندر کردن را با اضافه کردن shouldComponentUpdate به کامپوننت‌های خود، بهینه‌سازی کنید.

این فرایند قبل از این که فرایند رندر مجدد آغاز شود، فعال شده، و به طور پیشفرض مقدار True را بر می‌گرداند:

shouldComponentUpdate(nextProps, nextState) {
	return true;
}

وقتی که مقدار True برگردانده می‌شود، الگوریتم React جایگزین شده و تمام زیرشاخه‌ها را مجددا رندر می‌کند. می‌توانید با اضافه کردن منطق مقایسه به shouldComponentUpdate، از این کار جلوگیری کنید و فقط وقتی که propهای مرتبط تغییر کرده‌اند، فرایند رندر مجدد را آغاز کنید.

shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

در این حالت، کامپوننت مورد نظر اگر هر prop یا state دیگری به جز color یا count تغییر کرده باشد، بروزرسانی نمی‌شود.

جدا از این، برخی بهینه‌سازی‌های خارج از React وجود دارند که توسعه دهندگان معمولا از دست می‌دهند، اما تاثیر قابل توجهی بر روی کارایی برنامه دارند.

برخی از عادت‌ها و راه حل‌های قابل جلوگیری را در زیر لیست کرده‌ام:

  1. عکسهای بهینهسازی نشده - اگر در حال ساخت تصاویر دینامیک هستید، باید وقتی که در حال رسیدگی به تصاویر هستید، گزینه‌های خود را در نظر بگیرید. تصاویری که حجم زیادی دارند، می‌توانند باعث شوند کاربر فکر کند که برنامه کند است. قبل از این که تصاویر را به سرور ارسال کنید،‌ آن‌ها را فشرده کنید یا از یک روش دستکاری تصویر دینامیک استفاده کنید. من به شخصه Cloudinary را برای بهینه‌سازی تصاویر React قبول دارم، زیرا کتابخانه مخصوص خود را نیز دارد. همچنین می‌توانید از Amazon S3 یا Firebase نیز استفاده کنید.
  2. فایلهای Build فشردهسازی نشده - Gzip کردن فایل‌های Build (bundle.js) می‌توانند حجم فایل‌ها را به مقدار قابل قبولی کاهش دهند. برای انجام این کار، باید یک سری تغییرات بر روی Webserver نیز اعمال کنید. Webpack یک پلاگین فشرده‌سازی به صورت Gzip، به نام compression-webpack-plugin دارد. می‌توانید از این روش برای ساخت فایل‌های با فرمت gz، مانند bundle.js.gz در هنگام تولید استفاده کنید.

رندر سمت سرور برای SEO

گرچه برنامه‌های تک صفحه‌ای عالی هستند، اما دو مشکل وجود دارند که به آن‌ها مربوط می‌شوند.

  1. وقتی که برنامه بارگذاری می‌شود، هیچ cache مربوط به JavaScript در مرورگر وجود ندارد. اگر برنامه بزرگ باشد، زمان مورد نیاز برای بارگذاری برنامه نیز طولانی‌تر خواهد بود.
  2. از آنجایی که برنامه در سمت کاربر رندر می‌شود، Web crawlerهایی که موتورهای جستجو استفاده می‌کنند، نمی‌توانند محتویات تولید شده توسط JavaScript را وارد کنند.

تکنیک رندر سمت سرور،‌ در اینجا کاربرد دارد. در SSR، محتویات JavaScript در ابتدا بر روی سرور رندر می‌شوند. پس از رندر اولیه، اسکریپت سمت کاربر جایگزین شده و مانند یک SPA معمولی کار می‌کند. راه‌اندازی SSR به روش سنتی بسیار پیچیده‌تر است؛ زیرا شما مجبور به استفاده از یک سرور Node یا Express هستید.

اگر مشتاق SEO هستید، خبر خوبی در آن وجود دارد. در این صورت، گوگل بدون هیچ مشکلی محتویات JavaScript را وارد می‌کند. گوگل در سال ۲۰۱۶ شروع به استفاده از این الگوریتم کرد،‌ که امروزه کاملا بی نقص کار می‌کند.

اگر از رندر سمت سرور فقط به این دلیل استفاده می‌کنید که نگران رتبه گوگل خود هستید، دیگر به استفاده از SSR نیاز ندارید.

گرچه،‌ اگر این کار را برای ارتقای سرعت رندر اولیه انجام می‌دهید، بهتر است یک پیاده‌سازی ساده‌تر SSR با استفاده از Next.js را امتحان کنید. Next از هدر دادن زمان بر روی راه‌اندازی سرور Node یا Express جلوگیری می‌کند.

استایل‌های خطی و ورودی‌های CSS

به شخصه در هنگام کار با React، به دنبال ایده‌های مختلفی برای معرفی استایل به کامپوننت‌های React بودم. روش سنتی CSS-in-CSS که مدت‌هاست استفاده می‌شود، با کامپوننت‌های React نیز کار می‌کند. تمام stylesheetهای شما در یک شاخه stylesheet قرار می‌گیرند و می‌توانید CSS مورد نیاز را به کامپوننت‌های خود وارد کنید.

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

روش‌های دیگر زیادی برای ادغام کدهای CSS و JS در یک فایل استفاده می‌شوند. احتملا استایل خطی معروف‌ترین میان آن‌هاست.

import React from 'react';

const divStyle = {
  margin: '40px',
  border: '5px solid pink'
};
const pStyle = {
  fontSize: '15px',
  textAlign: 'center'
};

const TextBox = () => (
  <div style={divStyle}>
    <p style={pStyle}>Yeah!</p>
  </div>
);

export default TextBox;

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

در اینجا، روش CSS-in-JSS به کار می‌آید. کد زیر، نحوه انجام این کار را نشان می‌دهد:

import styled from 'styled-components';

const Text = styled.div`
  color: white,
  background: black
`
<Text>This is CSS-in-JS</Text>

چیزی که مرورگر می‌بیند، چنین چیزی است:

<style>
.hash234dd2 {
  background-color: black;
  color: white;
}
</style>

<p class="hash234dd3">This is CSS-in-JS</p>

یک تگ <style> جدید به بالای DOM اضافه شده است و بر خلاف استایل‌های خطی، در اینجا استایل‌های CSS واقعی ایجاد می‌شوند. پس هر چیزی که در CSS کار کند، در کامپوننت‌های استایل‌بندی شده نیز کار می‌کند. به علاوه، این تکنیک CSS را ارتقا می‌دهد، خوانایی را بهتر می‌کند و با معماری کامپوننت‌ها سازگار است.

اپراتور سه‌گانه تو در تو

اپراتور‌های سه‌گانه در React معروف هستند. این اپراتورها، اپراتورهایی هستند که من در Statementهای شرطی استفاده می‌کنم و داخل متد render() نیز به خوبی کار می‌کنند. برای مثال، آن‌ها در رندر کردن عناصر به صورت خطی مانند مثال زیر، کمک می‌کنند. من از آن‌ها برای نمایش وضعیت ورود استفاده کرده‌ام.

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}

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

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

همانطور که می‌توانید ببینید، نشانه‌های کوتاه، فشرده‌تر هستند، اما باعث می‌شوند کد به هم ریخته شود. حال فرض کنید که ۱۰ اپراتور سه‌گانه تو در تو یا بیشتر در ساختار خود داشتید. این اتفاق، بسیار بیشتر از آنچه که فکر می‌کنید پیش می‌آید. وقتی که کار با اپراتورهای شرطی را شروع می‌کنید، در هم نویسی راحت‌تر می‌شود و در آخر، به نقطه‌ای می‌رسید که می‌فهمید تکنیک بهتری برای رسیدگی به رندر کردن شرطی نیاز دارید.اما نکته مثبت آن، این است که جایگزین‌های زیادی برای آن دارید. می‌توانید از یک پلاگین Babel مانند JSX Control Statement استفاده کنید، که JSX را گسترش می‌دهد تا کامپوننت‌هایی برای Statementها و حلقه‌های شرطی را شامل شود.

// before transformation
<If condition={ test }>
  <span>Truth</span>
</If>

// after transformation
{ test ? <span>Truth</span> : null }

تکنیک معروف دیگری به نام jiffy وجود دارد. این یک تابع ناشناس است که دقیقا پس از تعریف شدن، فراخوانده می‌شود.

(function() {
 // Do something​
 }
)()

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

در زیر، مثالی را می‌بینید که نشان می‌دهد ما چگونه می‌خواهیم از IFFE در React استفاده کنیم.

{
   (() => {
      if (this.props.status === 'PENDING') {
         return (<div className="loading" />);
      }
      else {
         return (<div className="container" />);

   })()
}

IIFE ها می‌توانند تاثیری بر روی کارایی داشته باشند، اما در اکثر موارد چیز مهمی نیست. متدهای بیشتری برای اجرای بیانیه‌های شرطی در React وجود دارند.

Closureها در React

Closureها توابع داخلی‌ای هستند که به متغیر‌ها و پارامتر‌های توابع دیگر دسترسی دارند. Closureها در همه جای JavaScript وجود دارند و ممکن است بدون این که بدانید، از آن‌ها استفاده کنید.

class SayHi extends Component {

render () {
 return () {
  <Button onClick={(e) => console.log('Say Hi', e)}>
    Click Me
  </Button>
 }
}
}

اما وقتی که از Closureها داخل متد render() استفاده می‌کنید، در واقع کار نامناسبی انجام می‌دهید. هر زمان که کامپوننت SayHi رندر می‌شود، یک تابع ناشناس جدید ساخته می‌شود و به کامپوننت دکمه‌ها منتقل می‌شود. گرچه propها تغییری نکرده‌اند، اما <Button/> مجبور به رندر مجدد می‌شود. همانطور که پیش‌تر اشاره شد، رندرهای بیهوده می‌توانند تاثیر مستقیمی بر روی کارایی داشته باشند.

در عوض، Closure مورد نظر را با یک متد کلاس جایگزین کنید. متدهای کلاس خواناتر بوده، و خطایابی آن‌ها ساده‌تر است.

class SayHi extends Component {

  showHiMessage = this.showMessage('Hi')
 
  render () {
   return () {
      <Button onClick={this.showHiMessage}>
            Click Me
      </Button>
   }
  }
}

نتیجه گیری:

وقتی که یک پلتفرم رشد می‌کند، هر روز الگو‌های جدیدی ظاهر می‌شوند. برخی الگوها در پیشرفت روند کاری به شما کمک می‌کنند، در حالیکه برخی دیگر اثرات جانبی قابل توجهی دارند. وقتی که اثرات جانبی بر روی کارایی برنامه شما تاثیر می‌گذارند یا خوانایی آن را تحت خطر قرار می‌دهند، بهتر است که به دنبال راه‌های جایگزین باشید.

منبع

مقالات پیشنهادی

آیا پروژه ای که روی آن کار می کنید دیگر برایتان جذاب نیست؟

الهام و شوق و هیجان نان روزانه طراحان است . بدون آنها ما طراحان فرو میریزیم ، پژمرده می شویم و کم کم از بین میرویم. ولی مشکل الهام و شوق این است که ا...

مهمترین تکنیک هایی که باید در طراحی وبسایت از آنها استفاده کنید

برای افرادی که در زمینه طراحی وب معلوماتی ندارند، درک آخرین تکنولوژی های طراحی وب و گزینه های متعددی که در یک طراحی خوب وجود دارد، سخت است. اما چیزی ک...

با استفاده از React، صفحه‌ بندی‌ سفارشی‌سازی شده بسازید

اکثر مواقع، در موقعیتی قرار می‌گیریم که باید یک وب‌اپلیکیشن بسازیم که نیاز است حجم زیادی داده را از یک سرور کنترل از راه دور، یک API یا یک دیتابیس دری...

از علاقه های شخصی‌تان در کارهای طراحی استفاده کنید

آیا اینجا افرادی هستند که علایق عجیب و غریب و نا آشنایی داشته باشند؟ احتمالا از آن دسته افرادی نیستید که دوست دارند سَر تمام بطری های دنیا را جمع کنند...