<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمدجواد سیدی</title>
        <link>https://virgool.io/feed/@mjavad.seyyedi</link>
        <description>Frontend team leader @snappfood</description>
        <language>fa</language>
        <pubDate>2026-06-16 12:56:14</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/124373/avatar/nL8gEh.png?height=120&amp;width=120</url>
            <title>محمدجواد سیدی</title>
            <link>https://virgool.io/@mjavad.seyyedi</link>
        </image>

                    <item>
                <title>استراتژی های assets name hashing در وب پک</title>
                <link>https://virgool.io/coderlife/%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%DA%98%DB%8C-%D9%87%D8%A7%DB%8C-assets-name-hashing-%D8%AF%D8%B1-%D9%88%D8%A8-%D9%BE%DA%A9-gmcyasem8ej0</link>
                <description>assets name hashing قدیم ندیما، برای درست تنظیم کردن ورژن ها و درست آپدیت شدن اپلیکیشن های وب، بعد از هر ریلیز سعی میکردن به صورت دستی و استاتیک، به فایل های asset شون یه ورژن میزدن. مثلاhttps://temp.com/main.chunk.js?v=0xasa0sddخب، مثل همه ی کارهای دستی دیگه، آدما دوس دارن یه چیز automatic بسازن. حالا نمیریم سراغ سیری که طی شد برای این کار. اما اتفاقی که این روزها میوفته و ابزاری که در دسترسمون هست، Module bundler هایی هستن که میتونید به وسیله اونها، این کار رو انجام بدین. من میخوام اینجا،‌ از وب پک استفاده کنم و کارهایی که میشه در وب پک انجام داد رو بگم.کاری که این روزها داره ازش استفاده میشه، اضافه شدن یک hash به صورت اتوماتیک به آدرس فایل های asset هست به شکل زیر:https://temp.com/main.chunk.0xasa0sdd.jsبرای اینکار، روی وب پک کار خاصی نیاز نیست انجام بدید، کافیه برای حالت پروداکشن، در قسمت output از مثال زیر استفاده کنید:output: {
  filename: &#039;app.bundle.[hash].js&#039;,
  chunkFilename:  &#039;[id].[hash].js&#039;
},این کار باعث میشه، بعد از build شدن، به انتهای همه ی فایل های js شما، یک hash یکتا الصاق بشه. یعنی خروجی اسم فایل هاتون یه همچین چیزی میشه:0.d0fb27baffad1379579c.js.gz
1.d0fb27baffad1379579c.js.gz
.
.
.
app.bundle.d0fb27baffad1379579c.js.gzقبل از اینکه انواع hash هایی که میتونید بزنید رو بگم،‌ بگم این hash ها چگونه کار میکنن؟ بعد از هر build یه dependency گرافی، بین فایل های asset شما ایجاد میشه. یعنی، index شما که لود میشه، درخواست دریافت dependency های خودش که اصطلاحا بهشون گفته میشه Entrypoints. این فایل ها خودشون به فایل های دیگه dependency دارن و الی آخر (در صورتی که split chunks و lazy loading فعال کرده باشید).اینجای کار،‌اگر همه ی dependency ها درست ست شده باشن، اینجا شما به هیچ مشکلی از نظر کش نمی خورید و به محض دیپلوی شدن ورژن جدید، فایل های جدید در اختیار کاربر قرار میگیره، مگر در یک حالت اونم اینکه document اصلی (یا فایل index.html) کش شده باشه. پس لازمه ی اینکه همه ی آپدیت شدن شما درست صورت بگیره اینه که از کش شدن Document جلوگیری کنید ( در nginx و service worker). البته اگر بخواید Offline mode رو برای pwa فعال کنید، باید document رو کش کنید که اونجا برای آپدیت کردن نیاز به کارهای دیگه ای دارید که اگر نیاز بود، بپرسید همین پایین، میگم!خب، حالا بیاین در مورد این صحبت کنیم که چند حالت hash داریم:Hash:وابسته به بیلد هست، با هر بیلدی، یک hash یکتا تولید شده و به همه ی asset ها الصاق میشه. اگر فقط یک تغییر کوچک هم بدین، hash کل بیلدتون عوض میشه. این نوع hashing به درد زمانی میخوره که باندلتون کوچیکه و یا هر نوع مدیریتی ، سمت کلاینت، روی asset هاتون بخواین اعمال کنین.output: { 
  filename: &#039;app.bundle.[hash].js&#039;,  
 chunkFilename:  &#039;[id].[hash].js&#039;
 },Chunkhashاین فرمول، وابسته به entry point ها در webpack هست. هر entry دارای hash اختصاصی خودش هست. بالاتر گفتم منظور از entry چی هست. هرچیزی در یک entry خاص، تغییر کنه، hash اون entry تغییر میکنه. بهترین حالت برای استفاده از حداکثر ظرفیت caching در browser، در این فرمول نهفته هست. با اولین بار لود شدن یک asset، اون فایل در کلاینت شما cache میشه ( چه بهتر اگر سرویس ورکر داشته باشید). فرض کنید شما یه هاتفیکس یه خطی زدید، اگر از استراتژی اول استفاده کنید، hash همه ی فایل هاتون تغییر کرده و کش فعلی بروزر رو باید بریزید دور و همه ی فایل هاتون رو از اول بگیرید. اما با این استراتژی فقط فایل های entry خاصی که تغییر کرده دوباره از سرور دریافت میشن.output: { 
   filename: &#039;app.bundle.[chunkhash].js&#039;,
   chunkFilename:  &#039;[id].[chunkhash].js&#039;
 },البته این داستان بحث زیادی داره، من سعی کردم خیلی خلاصه در موردش بگم. اگر سوالی در مورد hashing، caching، service worker و ... داشتید، در خدمتتون هستم!</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Mon, 31 Aug 2020 19:09:54 +0430</pubDate>
            </item>
                    <item>
                <title>حذف Rerender های اضافی در کامپوننت های React</title>
                <link>https://virgool.io/iran-react-community/%D8%AD%D8%B0%D9%81-rerender-%D9%87%D8%A7%DB%8C-%D8%A7%D8%B6%D8%A7%D9%81%DB%8C-%D8%AF%D8%B1-%DA%A9%D8%A7%D9%85%D9%BE%D9%88%D9%86%D9%86%D8%AA-%D9%87%D8%A7%DB%8C-react-o66d7yeoubni</link>
                <description>من با Angular.js شروع کردم (version 1). اصلا اون موقع هنوز ری اکتی با این هیبت وجود نداشت. ورژن های جدید angular هم زاده نشده بودن. Vue و امثالهم هم بعدها به بازار اضافه شدن. بنابراین، اون سالها شاخ ترین Framework برای Frontend همین angular ورژن یک بود. توسط گوگل هم پشتیبانی شده بود و خیال همه راحت که بهترین انتخاب ممکن هست. تنها رقبایی که براش وجود داشتن، Ember و Backbone بودن. این دو تا هم طرفدارای خاص خودشون رو داشتن ولی واقعا در حد Angular نبود. یکی از مشکلاتی که خود من با Angular داشتم، این بود که وقتی یه پروژه ی با Scale بالا رو باهاش میزدی، از یه جایی به بعد، کندی رو میدیدی. اصلا مشخص بود دیگه داره به مرحله ی زایش میرسه. یه مدت که گذشت، React اومد تو بورس. نقطه ی قوتش هم چیزی نبود جز Virtual Dom. آقا ما که با این مشکلات Angular آشنا بودیم، ندیده و نشناخته عاشق Virtual DOM و ری اکت شدیم. همین موقع ها هم بود که Angular V2 به بالا وارد شدن. اما به خاطر وجود نداشتن این ویژگی، واقعا چشم و دل ما براش نرفت. حتی تا وقتی Angular V4 معرفی نشد من در مورد سینتکس هاشم نخوندم. اما بنا بر تقدیر، ما اول با Angular نسل جدید شروع کردیم. یه Framework کامل و غول که همه چیزی داشت. اصلا هرکاری بخوای باهاش بکنی میشه. منتهی یکمی زیادی کامله و واقعا برای اپلیکیشن هایی که برای یه بیزنس با تعداد مشتری های بالا و اپلیکیشن large Scale هست، خیلی انتخاب جالبی نیست. چون بحث اصلی تاپیک نیست زیاد وارد جزییات نمیشم، همینقد بگم من با Angular و React اپ های خیلی بزرگی زدم، پس بعد از یه تجربه ی بزرگ دارم همچین جمله ای میگم. بگذریم!با وجود React و قابلیت مهمش، دیگه با خیال راحت میشد رفت سراغ پیاده سازی. خب وقتی ندیده و نشناخته، میری سراغ یه اپلیکیشن Large Scale و خیالت راحته که قراره یه Performance عالی تحویل بدی، یهویی میرسی به یه نقطه ای که میبینی کندترین اپلیکیشن ممکن رو ساختی! حالا ببینیم چرا؟این قابلیت (Virtual DOM) برای خودش یه سری قواعد و قوانین داره. به هر حال مقایسه هایی که بین آپدیت های مختلف صورت میده، همه شون به یک شکل نیست. یه جاهایی این مقایسه ها روی Reference صورت میگیره، یه جاهایی روی Value. ممکنه شما به Debugger تون نگاه کنید و ببینید، آپدیتی روی Value ها ندارید ولی مثل ضربان قلب گنجشنک، کامپوننتت داره update میشه. این آپدیت شدن ها، هرکدوم ممکنه دلایل خاص خودش رو داشته باشه، اما من امروز میخوام در مورد Memo کردن یه کامپوننت صحبت کنم. یکی از قابلیت هایی که با هوک ها معرفی شد، useMemo هست. این قابلیت به شما این امکان رو میده که کامپوننتتون رو داخل یه wrapper به نام React.memo رپ کنید، یه فانکشن کاستوم بهش بدید و براش تعیین کنید که در چه شرایطی میخواین کامپوننتتون آپدیت بشه. بریم ببینیم چجوری:اینجا شما یه نتیجه از Profiler در کروم میبینید. یکی از صفحات اپلیکیشن من هست که وقتی درشون اکشن هایی رو انجام میدید، این مقدار ریرندر و time consuming وجود دارهکامپوننت Header کارت من، بیشترین میزان Rerender رو داشته در حالی که در این تستی که من گرفتم، هیچ کاری باهاش نداشتم. یه اکشنی رو روی یه کامپوننت دیگه توی Page انجام دادم و چون فقط یه state در Parent این کامپوننت ست شد، و کامپوننت پدر، Rerender شد، این کامپوننت و تمام بچه هاش، یه بار دیگه رندر شدن. خب دلیلش چیه؟یکی از دلایلی که وجود داره اینه که ممکنه رفرنس آبجکت یا آرایه ای که به این کامپوننت پاس دادین، عوض شده باشه. برای همین میتونیم از React.memo توی این کامپوننت استفاده کنیم. به این صورت :function HeaderCard(props){
     ...
     return &lt;div&gt;
                  ...
             &lt;/div&gt;
}function areEqual(prevProps, currProps) {
  return prevProps.title === currProps.title
}export default React.memo(HeaderCard, areEqual)توی این wrapper ای که برای کامپوننت میزارم، یه فانکشن پاس میدم به اسم areEqual. این تابع، بعد از هر بار درخواست رندر جدیدی که میاد، یه بار کال میشه و مقدار قبلی Prop ها و مقدار فعلی رو در اختیارتون میزاره. شما میتونید در بدنه ی این تابع، یه سری شروط بنویسید که فقط در صورت true شدن اون شرط ها، کاپوننتتون آپدیت و در نهایت Rerender بشه. مثلا در این کامپوننت من میخوام فقط وقتی title آپدیت شد، Rerender صورت بگیره. برای همین شرطم به این شکله که تا زمانی که مقادیر قبل و بعد title با هم برابر هستن، یعنی Prop ها با هم برابرن و نباید ریرندر انجام بشه.این از مرحله ی اول. اگر کالبکی به کامپوننت خودتون پاس نداده باشین، همین کار کافیه و همین عمل باعث میشه که ریرندر اضافی از دوش کامپوننت برداشته بشه.اما خیلی از دفعات هست که شما به instance کامپوننتون، یه کالبک پاس میدید. React.memo فقط میتونه جلوی آپدیت های بی مورد Prop ها رو بگیره. اما با هر بار آپدیت شدن Parent یه instance جدید از کالبکی که به کامپوننت دادید ساخته میشه و باعث کال شدن مجدد کامپوننت میشه. برای حل این مشکل، این بار باید از useCallback استفاده کنید:&lt;HeaderCard
         ...
        onFaveClick={memoizedAddToFave}
        Comment={memoizedClickComment}
        Information={memoizedClickInformation}
/&gt;اینجا، بعد از پاس دادن Prop ها به کامپوننت، سعی میکنیم، Function ها رو هم Memoize کنیم. حالا ببنیم منظور از این چیه؟const memoizedClickInformation = useCallback((x) =&gt; {
  callFunctionA(x)
}, [y])ما میخوایم وقتی داخل کامپوننت، تابع Information کال شد، تابع callFunctionA رو با ورودی x که از کامپوننت بهش داده میشه، کال کنیم. این کار با استفاده از useCallback به شکل بالا انجام میشه. اون مقداری که در آرایه به عنوان آرگومان دوم به useCallback پاس داده شده، یعنی [y] به این معنی هست که این تابع من، به مقدار y ، وابسته است یا اصطلاحا dependency داره. پس هر وقت مقدار y آپدیت شد، این کالبک هم باید آپدیت بشه و یه instance جدید ازش ساخته بشه. مثلا فرض کنید x یکی از attr های آبجکت y هست. اگر این dependency رو انجام ندید، ممکنه مقدار x در آبجکت y تغییر کنه اما وقتی کالبک صدا زده میشه، مقدار قبلی x رو دریافت کنید.خب حالا ببینیم با این کار، نتیجه چه تغییری میکنه:میبینید که در شرایط مشابه، نسب به حالت قبل، تمامی ریرندرهای اضافی حذف شد و به صفر رسید.در پایان اینو بگم که ، این کار در حالت کلی توصیه نمیشه. چون باعث Memory usage زیادی میشه و فقط در مواقع Critical باید ازش استفاده بشه. از طرفی اگر Component Composition درستی وجود داشته باشه و معماری اپتون از اول درست باشه، این اتفاق نمی افته. اما یه زمانایی هم اجتناب ناپذیره. موفق باشید.</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Fri, 12 Jun 2020 19:48:09 +0430</pubDate>
            </item>
                    <item>
                <title>بروزرسانی پروژه با CI/CD - قسمت دوم</title>
                <link>https://virgool.io/apieco/%D8%A8%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-cicd-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-ndhsuedzkqik</link>
                <description>از اونجایی که بنا دارم CI/CD رو با Docker و docker compose توضیح بدم، در صورتی که با داکر آشنایی ندارید، حتما قبلش قسمت اول رو بخونید.در CI/CD ، هدف اینه که، توسعه دهنده بتونه بدون دسترسی مستقیم به سرور، پروژه ی خودش بروزرسانی بکنه. برای این منظور، اول از همه باید ببینیم چه کاری قراره دقیقا صورت بگیره. ما اینجا یک نماینده از repository خودمون روی سرور ثبت میکنیم. برای مثال فرض میکنیم پروژه مون روی gitlab قرار داره. پس باید یه gitlab-runner روی سرور خودمون ثبت کنیم. این رانر وظیفه داره که از جانب شما پروژه تون رو بیلد و ران کنه. برای نصب رانر گیت لب روی سرور لینوکسیتون از این لینک استفاده کنید:https://docs.gitlab.com/runner/install/linux-manually.htmlبعد از اینکه رانر رو نصب کردید، باید روی سرور یه رانر بسازید یا اصطلاحا، Register کنید. برای اینکار به آدرس زیر برید:https://docs.gitlab.com/runner/register/در مراحل ثبت نام از شما آدرس و token درخواست میکنه. برای اینکه این مقادیر رو بدید به runner، در پروژه ی گیت لبتون برید به این آدرس:در این صفحه، قسمت runners رو expand کنید. و مطابق شکل زیر، آدرس و توکن مد نظر خودتون رو پیدا می کنید:فقط در حواستون باشه، در مرحله ی آخر، وقتی ازتون میخواد executor رو مشخص کنید، حتما shell رو انتخاب کنید.اگر مراحلی که در دو لینک بالا آمده رو درست انجام بدید، gitlab runner شما رجیستر و فعال میشه. از اینجا به بعد باید بریم سراغ پروژه و ببینیم در پروژه چه تنظیماتی رو باید انجام بدیم. اول از همه شما به یه فایل با نام gitlab-ci.yml. نیاز دارید. این فایل به صورت پیش فرض توسط گیت لب به عنوان فایل اجرایی Pipeline شناسایی میشه. مطابق قطعه کد زیر، این فایل رو استفاده کنید:image: docker:latest
stages:
  - deploy

services:
  - docker:dind

deploy_production:
  stage: deploy
  script:
    - docker-compose -f docker-compose-production.yml up -d --build
  only:
    - master
  when: manual
  tags:
    - runnerدر خط اول، معرفی میکنید که از آخرین ورژن داکر برای دیپلوی استفاده بشه. در قسمت stages ، بیان میکنید که قراره چه کاری صورت بگیره که خب ما میخوایم ورژن جدید رو دیپلوی کنیم. و در سرویس هم معرفی میکنیم که ابزار مورد استفاده ی ما docker هست.هر قسمت از این فایل، باید یه نام داشته باشه. مثلا ما برای دیپلوی کردن پروژه ی جدیدمون، اسم deploy_production رو انتخاب می کنیم. هر اسمی رو انتخاب کنید، در قسمت Pipeline گیت بهتون نمایش داده میشه. در خط بعد، stage این قسمت رو مشخص میکنید، که برای کار ما میشه همون deploy.خط بعد از شما میخواد، script هایی که قراره به ترتیب صورت بگیره رو بیان کنید. ما برای ران کردن پروژه و کمتر دردسر کشیدن، به جای استفاده ی مستقیم از docker، سعی می کنیم از docker-compose استفاده کنیم. وقتی اینکار رو میکنید، نیاز نیست ورژن قبلی رو stop کنید و به سرور بگید ورژن جدید رو run کن. خود رانر ، ورژن قبلی که با همین docker-compose نوشته شده رو براتون، با image جدید restart می کنه. پس در این قسمت بهش میگیم که فایل docker-compose ما رو که اسمش هست docker-compose-production.yml رو ران کنه. در نظر داشته باشید که حتما flag هایی که می بینید وجود داشته باشه. --build به docker-compose میگه که حتما یه بیلد جدید از پروژه بگیر.قسمت بعدی only هست. این قسمت به رانر میگیم فقط روی برنچ هایی که لیست شدن حساس باش، اگر کامیت جدیدی روی این برنچ ها دیدی، یه رکورد جدید برای پایپلاین در نظر بگیر. ما اینجا حساسیت رو میزاریم روی master چون هدفمون بروزرسانی پروداکشن هست. اما میتونید با همین روش، روی develop هم Pipeline ست کنید.قسمت when به رانر میگه که به صورتی دستی ، پایپلاین رو استارت کنه یا اتوماتیک. بدون هیچ سوالی، این رو بزارید روی manual. آفرین، سوال نپرسید :))مهم ترین قسمت، همین tags هست. شما در این قسمت، tag ای که برای رانرتون روی سرور انتخاب کردین رو میدین و میگین من میخوام این رانر نماینده ی دیپلوی من باشه. خب این قسمت تنظیمات ابتدایی فایل ci انجام میشه. در مرحله ی بعد باید بریم سراغ docker-compose و ببینیم اونجا چه تنظیماتی نیاز داره. به قسمت زیر دقت کنید:version: &#039;3.7&#039;
services:
  production-test:
    build: .
    container_name: production-test
    restart: always
    environment:
      - NODE_ENV=production
    ports:
      - 8080:80ورژن که مشخصه، توضیح نمی خواد.در بخش services شما میتونید چندتا container رو یه جا معرفی کنید. اینکار باعث میشه بتونید با یه بار ران کردن ci، چندتا image مختلف رو ران کنید. این کار برای مواقعی که اپ شما روی پورت های مختلف و با ورژن های مختلف ران میشه مناسبه. ما فعلا همین یه ورژن رو میخوایم بسازیم. خط بعد اسم container ما رو تعیین می کنه. ما اینجا اسمش رو میزاریم production-testقسمت restart یک flag برای docker ست میکنه، به این صورت که اگر به هر دلیلی، پروژه به خطا خورد و استاپ شد، شما همینجوری الکی دوباره استارتش کن. در واقع ریستارتش کن.در environment هر variable ای که نیازه در build تایم معرفی بشه، می تونید ست کنید. مثل همین NODE_ENVقسمت آخر هم همون port ای هست که مقاله ی قبلی گفتم. اینجا یعنی پورت ۸۰ داخل داکر رو به پورت ۸۰۸۰ روی سرور مپ کن.خب این هم از docker-compose. فقط در نظر بگیرید که داکر و داکر کامپوز، هر دو باید روی سرور نصب باشن.وقتی این دو تا فایل رو به فایل های پروژه اضافه کنید، Dockerfile هم که از قبل موجوده. ترکیب تنظیمات مورد نیاز برای ران شدن Pipeline کامل میشه. حالا با هر کامیتی که میکنید و هر پوشی که روی مستر صورت میگه یه رکورد برای Play شدن در قسمت CI/CD =&gt; Pipelines اضافه میشه. مثل شکل زیر:طریقه ی کار کردن این قسمت رو خودتون می تونید بخونید. وقتی Pipeline رو ران کنید و با موفقیت تا انتها بره، همچین لاگی رو میبینید:این یعنی image شما ساخته شد و روی پورتی که تنظیم کردین ران شده. اگر تنظیمات وب سرور روی سرور اصلی، درست ست شده باشه، باید بتونید ورژن جدید رو روی آدرس دامنه دریافت کنید.موفق باشید...</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Sun, 07 Jun 2020 18:15:07 +0430</pubDate>
            </item>
                    <item>
                <title>بروزرسانی پروژه با CI/CD - قسمت اول</title>
                <link>https://virgool.io/apieco/%D8%A8%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%A8%D8%A7-cicd-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-zgdr9yhrs86t</link>
                <description>یکی از مشکلاتی که خیلی از ماها، مخصوصا frontend Developer ها باهاش درگیریم، زمانی هست که میخوایم یه ورژن جدیدی از اپلیکیشن رو ریلیز کنیم. روال متداول و سنتی قضیه، اینه که شما در تنظیمات وب سرورتون (Node, Nginx, Apache, ...) یه فولدر رو اختصاص میدید به پروژه ی فرانت اندتون، هر زمانی که خواستید ورژن جدیدی رو ریلیز کنید، پروژه تون رو روی سرور آپلود میکنید و فایل های بیلد شده رو منتقل می کنید روی اون فولدر. این مساله چند تا عیب داره. اولیش اینه که شما به عنوان دولوپر سمت Client شاید دسترسی مستقیم به سرور نداشته باشید و هر سری بروزرسانی، باید توسط SysAdmin یا امثالهم صورت بگیره. مساله دیگه باز نبودن دستتون برای اعمال تغییرات روی وب سرور هست، مثلا شما اجازه ندارید تنظیمات جدیدی برای Caching روی Nginx انجام بدید، هرچند که فقط مربوط به پروژه ی خودتونه، ولی دسترسی میخواد که شما ندارید. و یک سری مشکلات روتینی که همه تون بهش برخوردید.خب، ببینیم چجوری میشه از شر این قضیه راحت شد. گام اول برای رهایی از این داستان، Dockerize کردن پروژه تون هست. اما قبلش بگم، من نمی خوام داکر توضیح بدم یا از اول بگم داکر چطوری کار میکنه. فقط در حدی که یه Frontend Developer باید آشنا باشه، توضیح میدم.حالا بزارین خیلی ساده بگم Docker چیکار میکنه. شما وقتی پروژه تون رو با استفاده از Docker بیلد و ران میکنید، دارید یه محیط ایزوله ی جدید که مثل یه ماشین کامل عمل میکنه، میسازید. فرض کنید روی nginx سرور، پورت ۸۰۸۰ به پروژه ی شما اختصاص پیدا کرده. مدیر سیستم، روی دامنه (ساب دامنه ای) که اپ شما روی اون Serve میشه، یک Proxy Path مینویسه که همه ی درخواست ها ارسال میشه به پورت ۸۰۸۰. از اینجا به بعد کافیه شما به این پورت Listen کنید و پروژه تون رو براش Serve کنید. وقتی دارین از Docker استفاده می کنید، کافیه پروژه تون رو بیلد کنید و روی پورت ۸۰۸۰ ران کنید. از این به بعد تمام درخواست هایی که به این پورت میرسه، توسط داکر قابل دریافته.خب بعدش چی میشه؟از اینجا به بعد داکر کاملا مثل یه ماشین مجازی با پروژه ی شما برخورد میکنه. هر Image داکر، یه ماشین مجازیه که می تونید به صورت مستقل برای تنظیمات ست کنید. مثلا میتونید برای خودتون Nginx مستقل بنویسید، می تونید روی این ماشین Module هایی که می خواید رو نصب کنید. خلاصه آزادی عمل دارین.حالا بزارین یه نمونه از یه Dockerfile رو با هم ببینیم و خط به خط با هم بریم جلو:FROM node:10.16.0 as build

WORKDIR /app
COPY ./ /app/

RUN yarn install
RUN yarn build

FROM nginx:alpine
COPY --from=build /app/build/ /usr/share/nginx/html/
ADD ./deploy/nginx-default.conf /etc/nginx/conf.d/default.confابتدای ماجرا، Node ورژن 10.16.0 رو به عنوان سورس برای نصب Node Module ها معرفی می کنیم. در خط بعد،  فولدر /app رو به عنوان فولدر اصلی، ساخت پروژه در Docker معرفی میکنیم و میگیم میخوایم کارامون رو توی این فولدر انجام بدیم. دستور COPY ، هر فایلی رو که بگید، از فایل های لوکال روی سیستمتون، به Docker منتقل میکنه. مثلا ما اینجا نوشتیم که تمام فایل های روت پروژه رو به فولدر /app/ که برای خودمون ساخته بودیم، منتقل کنه.حالا که فایل های خام پروژه رو منتقل کردیم، برای راه اندازی پروژه ، باید Dependency ها رو راه بندازیم. برای این کار،‌ باید yarn install بزنیم. بعد از اینکه ماژول هامون نصب شد، دستور build رو میزنیم که از پروژه مون build production گرفته بشه.وقتی کار بیلد گرفتن پروژه تموم بشه، اگر در محیط داکر هم نباشیم، فایل های بیلد شده رو به آدرسی که در nginx تعریف کردیم، منتقل میکنیم، اینجا هم به همین صورت:FROM nginx:alpineیعنی می خوایم از یه ورژن light برای nginx استفاده کنیم. در خط بعد،‌ میگیم فایل های بیلد شده که الان در فولدر /app/build/ روی داکر هستن رو به فولدر  /usr/share/nginx/html/ که همون روت nginx هست منتقل بشه. یک مساله ی اساسی که باقی میمونه اینه که هر nginx ای نیاز به یه سری تنظیمات کاستوم داره که شما باید در داکر اون رو ست کنید. این تنظیمات در لینوکس در فایل /etc/nginx/conf.d/default.conf قرار داره. خب شما میتونید یه فایل conf به صورت کاستوم در پروژه ی خودتون درست کنید. مثل:server {
    listen 80 default_server;
    root /usr/share/nginx/html;
    server_name  _;
    server_tokens off;
    charset utf-8;
    index index.html index.htm;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    client_max_body_size 20M;


    location / {
        index index.html index.htm;
        try_files $uri $uri/ /index.html =404;
        include mime.types;
        default_type application/octet-stream;

        gzip_static on;
        gzip on;
        gzip_vary on;
        gzip_min_length 0;
        gzip_proxied expired no-cache no-store private auth;
        gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
        gzip_disable &amp;quotMSIE [1-6]\.&amp;quot
    }

    location ~ /assets/fonts/ {
        root /usr/share/nginx/html/assets/fonts/;
    }

    location ~ /\. {
        deny all;
    }


    location ~* \.(?:html)$ {
        sendfile off;
        add_header Last-Modified $date_gmt;
        add_header Cache-Control &#039;no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0&#039;;
        if_modified_since off;
        expires off;
        etag off;
    }

    location ~ \.html$ {
        sendfile off;
        add_header Last-Modified $date_gmt;
        add_header Cache-Control &#039;no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0&#039;;
        if_modified_since off;
        expires off;
        etag off;
    }

    location ~* \.(?:ico|gif|jpg|jpeg|png|woff|woff2|ttf|js)$ {
        if_modified_since off;
        gzip_static on;
        expires 365d;
        add_header Pragma public;
        add_header Cache-Control &amp;quotpublic&amp;quot
        access_log off;
    }
}
و در یه فولدری در پروژه تون به آدرس/deploy/nginx-default.conf ذخیره کنید. در پایان می تونید همین تنظیمات رو با دستور ADD ./deploy/nginx-default.conf /etc/nginx/conf.d/default.conf به عنوان تنظیمات nginx در داکر ست کنید.در نظر داشته باشید که اگر محتوایی که برای docker توضیح دادم رو در فایل ای با اسم Dockerfile ذخیره کنید، به عنوان فایل پیشفرض شناخته میشه و در دستور بیلدتون نیاز نیست اسم فایل رو ذکر کنید.خب حالا که محتوای Dockerfile آماده شد، باید دستور بیلدتون رو وارد کنید:docker build . -t &lt;image_name&gt;به جای &lt;image_name&gt;  اسمی که برای image داکر خودتون میخواید بزارید رو بنویسید. مثلا:docker build . -t javad_productionبعد از اینکه این دستور رو زدین، بیلد شما شروع میشه. در نظر داشته باشید که تحریم باعث میشه همون اول نتونید یه سری ماژول ها رو دانلود کنید، پس حتما از یه چیزی مثل shecan استفاده کنید.بعد از اینکه بیلدتون تموم شد دستور docker images رو وارد کنید. یه لیست از ایمیج هایی که ساخته شده رو بهتون نشون میده که اگه بیلدتون موفقیت آمیز باشه، javad_production هم درش هست.حالا برای اجرا کردن این image، دستور docker run -it -p 8080:80 javad_productionرو وارد کنید. با -p پورتی که میخواید image روی اون ران بشه رو معرفی میکنید. اول پورتی که در سرور برای پروژه ی شما تعریف شده (۸۰۸۰) و بعد از : ، پورتی که در داخل داکر پروژه ی خودتون رو ران کردین که اینجا فرض کردیم اون پورت ۸۰ هست. در انتها هم اسم image.این یه مقدمه برای اینکه بدونیم CI/CD با استفاده از داکر قراره چطور کار کنه. در قسمت بعد اون رو کامل توضیح میدمشب خوش... </description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Sat, 06 Jun 2020 01:14:18 +0430</pubDate>
            </item>
                    <item>
                <title>هنر به پوچی رسیدن - قسمت دوم</title>
                <link>https://virgool.io/@mjavad.seyyedi/%D9%87%D9%86%D8%B1-%D8%A8%D9%87-%D9%BE%D9%88%DA%86%DB%8C-%D8%B1%D8%B3%DB%8C%D8%AF%D9%86-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-recobf0pzbfi</link>
                <description>قبل از هرچیز، قسمت اول رو میتونید از اینجا بخونید!رسیدیم به اینجا که چی شد من از js بدم میومد! خب باید براش یه خاطره تعریف کنم. سال سوم دانشگاه بودم که کار آموزی ورداشتم. توی شرکت یکی از بچه ها قرار شد برم کارآموزی. تابستون بود و من خوابگاه اتاق گرفتم. همون روزای اولی که خوابگاه تابستون رو گرفتم، ماه رمضون هم شروع شده بود، تو اون اتاق ساس افتاده بود. بچه هایی که ساس رو تجربه کرده باشن میفهمن، چه چیز ترسناک و عجیبیه این موجود. عموما هم توی خوابگاه ها و آسایشگاه های پادگان ها زیاده. هرچی سمپاشی هم بکنی، نمیره. مگه اینکه سم خیلی خیلی قوی ای بزنی. بماند که کل تنم زخم شده بود، بعد از سم پاشی ، عجیب مریض شدم. پنی سیلین و اینا، حالا توی اون مریضی و زخمای ساس، باید میرفتم کارآموزی و js یاد میگرفتم. خب شما جای من، چه اتفاقی میوفته. توی اون وضع عجیب، شدیدا home sick هم شده بودم و دلم میخواست برگردم خونه. خلاصه با یه وضع خیلی بدی کارآموزی رو حذف کردم و برگشتم خونه. از اون موقع شرطی شدم و از js نفرت داشتم. حالا همه ی اینا به کنار، سعی کردم یه بار دیگه تلاشم رو بکنم. برم تو دلش ببینم چیه این موجود. برای همین پیشنهاد مهران رو قبول کردم و رفتم همون فلان جایی که قسمت قبل تعریف کردم. در مورد جزییاتی که در مسیر کاری من پیش اومده، یه رشتو طولانی در توییتر نوشتم، اگه علاقه داشتین اون رو از لینک زیر بخونید: https://twitter.com/hunter1371/status/1190688999175868416?s=20 خب ، بافر کار در حوزه ی وب که پر شده بود و من باید خالیش میکردم. میدونستم باید در نهایت به اون حس پوچی برسم، اما مساله این بود که این سری میخواستم ازش نهایت استفاده رو بکنم، حالا که انگیزه ی خالی شدن بافرم رو داشتم، باید یه سودی هم براش داشته باشم. این شد که این سری چسبیدم بهش . من خیلی بلد نبودم که چطوری باید کار رو برم جلو اما اون شرکت یه برنامه خودآموز محور داشت که یواش یواش راه رو به من نشون داد. خیلی از جزییاتش نمی گم اما به همون بهانه سعی میکنم بگم چه مسیری، از نظر من بهترین مسیر برای رسیدن به مهارت کافی در حوزه ی وب هست:اولین و اساسی ترین نکته اینه که باید خودآموز برید جلو. اصلا و سر سوزنی به کلاس های آموزشی اعتقاد ندارم. من این مسیر رو قبلا رفتم و هییییچ تاثیر مثبتی نداشت برای همین میگم وقت خودتون رو سر کلاس های آموزشی هدر ندید. به جاش از آموزش های به روز جهان، که خیلی هاش هم به صورت رایگان در شبکه هایی مثل یوتیوب وجود دارن استفاده کنید. وقتی این ویدئوها رو میبینید، بهتون یه دید کلی میده، اصلا برای شروع کافی نیست ولی لازمه. ویدئو ها عموما همراه با تمریناتی میرن جلو، شما هم سعی کنید با مدرس، تمرینات رو انجام بدین. وقتی یکی از دوره ها رو باهاش برین جلو و تمرینات رو انجام دادین، میرسین سر خطی که حالا باید تمرینات ریز و کوچیک برای خودتون تعریف کنین. اگر مسیر رو درست اومده باشین و تمرینات مناسبی برای خودتون تعریف کنین، عمدتا با این فکر شروع به انجام اون تمرینات میکنید که &quot;خدایا، اینجا کجاست؟ من کی ام؟ دارم چی کار میکنم؟ باید از کجا شروع کنم؟&quot; . پس trust me، این نقطه اصلا معنی بدی نداره، همه ی ماها یه روزی اینجا بودیم. اما وقتی میتونید از این نقطه به خوبی عبور کنید که از کسی کمک نگیرید. اصلا از هیچ کسی سوال نکنید، فقط و فقط باید با سرچ کارتون رو شروع کنید و جلو ببرید، حتی اگه یک ماه زمان ببره. اگر به غیر تکیه کنید، تا ابد تکیه میکنید و هیچ وقت کارتون جلو نمیره. اونقد باید تلاش کنید و مسیرتون رو با سرچ باز کنید تا ترستون بریزه و به یه دید کلی از کارایی که میتونید انجام بدید برسید. انتهای مسیر، یعنی جایی که تونستید پروژه های کوچکتون رو به سرانجام برسونید، وقت اونه که برید سراغ یک پروژه ی بزرگتر. اما در نظر بگیرید، پروژه های بزرگ اول و دومی که انجام میدین، معمولا اونقدر شلخته و کثیف انجام میشن، که فقط به درد دور ریختن میخورن، پس گول نخورین و پروژه های دیگران رو دست نگیرین. این شما فقط دارین تجربه تون رو بالا میبرید. در طول انجام این پروژه ها، نکته اصل و اساسی خوندن مقالات زیاد و یاد گرفتن تجربه های کسانی هست که مسیر رو قبل از شما رفتن. این یه تفاوت اساسی با پرسیدن از دیگران داره، وقتی شما مقاله میخونید، یا سوالتون رو جایی مثل stackoverflow میپرسید، دارین درون اکو سیستمی که در آینده قراره درش زندگی کنید، تمرین زندگی میکنید. اما پرسش از دانایان به صورت حضوری و مثل شاگرد و معلم، اصلا همچین خاصیتی نداره. پس خودتون رو گول نزنید و برید سراغ همین کاری که گفتم. اگر این مسیر رو درست انجام دادین و رسیدین به نقطه ای که گفتم، تماااام تمریناتی که تا الان انجام دادین رو با خیال راحت بریزید دور و مسیر جدیدتون یعنی، تلاش برای تثبیت مهارت هاتون رو شروع کنید. از اینجا به بعد دیگه دست خودتونه، هرکسی یه جوری خودش رو به روز نگه میداره، یکی ویدئوهای بروز رو میبینه، یکی هر روز مدیوم رو چک میکنه، یکی stackoverflow رو بالا پایین میکنه و ...پس اگه یه مسیر که جواب داده نیاز دارید، یه بار دیگه این مقاله رو از اول بخونید.شب بخیر ....</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Thu, 21 May 2020 22:48:03 +0430</pubDate>
            </item>
                    <item>
                <title>هنر به پوچی رسیدن - قسمت اول</title>
                <link>https://virgool.io/@mjavad.seyyedi/%D9%87%D9%86%D8%B1-%D8%A8%D9%87-%D9%BE%D9%88%DA%86%DB%8C-%D8%B1%D8%B3%DB%8C%D8%AF%D9%86-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-pszupbz0pavo</link>
                <description>من محمدجواد هستم! یک ۲۷ ساله، در قله ی پوچی ...خیلی ها ازم در مورد مسیری که باید طی کنن میپرسن. اینکه از کجا شروع کنن، چه مسیری رو برن و چه سبکی آموزش ببینن، تا بشه در گروه یه برنامه نویس حساب بشن. من این ادعا رو برای خودم ندارم، ولی از اونجایی که در معرض خیلی از این سوالات هستم، خواستم یه بار برای همیشه بگم که چی شد که اینجوری شد...زندگی من، از اواخر دوران دبیرستان، یهویی رنگ و بوش عوض شد. وارد یه سری مشکلاتی شدم که هنوزم بعد از ۱۰ ۱۱ سال، ممکنه باعث لرزیدنم بشه وقتی بهش فک میکنم. افسردگی، اضطراب ، پریشونی. همه ی اینها باعث شد یه مدت خیلی طولانی، از ایده آل هام دور بشم. دیگه خیلی به آرزوهام فکر نمی کردم و صرفا دنبال این بودم که بتونم اضطراب نداشته باشم. دو سه سالی تو این وضعیت بودم تا به حالت پایدار برسم. اما شما میدونید دو سه سال اضطراب دائم برای کسی که تازه از دوران نوجوونی گذشته و وارد دانشگاه شده، کسی که باید تمام آرزوهاش رو شکل بده و شخصیت آینده ش رو بسازه، زمان زیاد و مهمیه!؟کلی مشاوره و دارو، خرج این شد که فقط برگردم به روزای قبل از اضطراب، راستش آرزوی زیر لبم شده بود، &quot;زندگی عادی&quot;. چیزی که داریم و قدرش رو نمی دونیم ...بگذریم...وقتی به یه آرامش نسبی رسیدم و دیگه میتونستم فکر کنم، دیدم پسر، من چقد عقبم! بیست و یکی دوساله م شده بود در حالی که عملا هیچی بلد نبودم، هیچی. صفر مطلق. هی تو ذهنم این سوال بود که خب میخوای چی کار کنی ، میخوای به کجا برسی ، میخوای چی بشی؟ اصلا میخوای وقتی &quot;بزرگ&quot; شدی چی کاره بشی!؟؟؟؟شما فک میکنی نشستم با خود فک کردم، جواب پیدا کردم؟؟؟؟ نه، ابدا، اما یه سری دغدغه ها برام ایجاد شد. اینکه یواش یواش داشت یه سری چیزا برام مهم میشد، درسا، مهارت ها، تیپ و قیافه ی نداشته م، ...از همون سال ها، دیگه Buffer ذهنم داشت پر میشد، هی در مورد یه چیزی وسواسی میشدم، میرفتم سراغش تا این بافر لعنتی رو خالی کنم، وقتی پر میشد، شبا خوابم نمیبرد، اما وقتی خالی بود، درسته که خوشحال نبودم، اما حداقل آزار نمی دیدم! خب بزار بگم چیا میومد توی این بافر.مثلا تو سال های دانشگاه، مسابقات ACM برگزار میشد، اما حاجی تون حتی سینتکس C رو هم بلد نبود که بخواد بره همچین مسابقاتی شرکت کنه، وقتی میدیدم همه ی همکلاسی ها میرن شرکت میکنن، من میموندم و یه عقده و یه بافر پر از آرزوهایی که باید جایگزین این عقده میشد. اما کاری هم از دستم بر نمیومد، فقط و فقط حرص میخوردم و حسادت میکردم.آقا دری به تخته خورد، یکی گفت بیاین بریم آزمایشگاه Robotics ، فلانی یه دوره برگزار میکنه، نفری ۵۰ تومن، یاد میگیریم بعدش تیم تشکیل میدیم! خب ما هم بی کار و بی عار پاشدیم رفتیم. سال ۹۱ ۹۲ ، ۵۰ تومن خیلی بود، کلا پول تو جیبی منی که تو یه شهر دیگه تو خوابگاه زندگی میکردم، ماهی ۵۰ تومن بود. ولی برای خالی کردن بافر هم که شده، رفتیم. دوره رو هم رفتم ولی اونقد که تمرکز نداشتم و برام مهم نبود، اصلا هیچی یاد نگرفتم، هیچکس هم تو تیمش راهم نداد. هیچی، رباتیک هم تعطیل شد. اما حداقل بافرش خالی شد و من از حالت اضطراب، رسیدم به پوچی مجدد!یه مدت گذشت، یللی تللی ادامه داشت، یکی گفت بریم پیش فلان استاد، مباحث امنیت شبکه رو بریم جلو، رفتیم، یه گروه ساختیم، یه مدت در موردش خودآموز خوندیم، تعطیل شد! پوچی!یه استادی گفت بیاین اینجا بهتون کار با سخت افزار و برنامه نویسی سخت افزار یاد بدم، رفتیم، یه ماه بودیم، اومدیم بیرون، تعطیل! پوچی!یکی از دانشجوهای دکترا رو پیدا کردم، گفت بیا، بهت سرفصل میدم برو در موردش بخون، یواش یواش در مورد یکی از موضوعات امنیتی سایبری مقاله بده. منم گفتم بافر اپلای کردن با این میتونه خالی بشه، رفتم ، مقاله خوندم، استارت زدم، گاز دادم، دنده کم کردم، خاموش کردم، پوچی!آقا این روند ۲ ۳ سال ادامه داشت. ما هرجا رفتیم ، بازم برگشتیم سر خونه ی اولمون. یه بار نشستم با خودم فک کردم ، گفتم ببین جوون، تو باید همیشه این بافر رو خالی کنی، به پوچی برسی، ولی بیا هدفمندش کن، یه بار سفت و سخت خالیش کن!این شد که با یکی از بچه ها زدیم تو کار استارتاپ، پناه و مامن همه ی بچه های کامپیوتری که هدفی برای آینده ندارن! آقا گفتیم تو حوزه گردشگری فلان چیز رو بزنیم، استارت زدیم، ول کردیم! بعد از یه مدت این شد اعتیاد جدید، کلا رفتیم قاطی این استارتاپیا! هی این ور برو اونور برو، تو این جلسه تو اون گردهمایی. همیشه داشتیم پول همینا رو میدادیم. ( بزار اینجا این رو بگم! اگه دانشجویی یا سنت کمه و کم تجربه ای و به فکر اینی که استارتاپ خودت رو بزنی و کار کاسبی خودت رو تو سن کم راه بندازی، رک اینی که میگم بشنو! همه ی اون چیزایی که تو همایش ها و گردهمایی ها بهتون میگن خزعبلاته! همه ش! برای چاپیدن جیب شماهاست، برادر من، خواهر من، استارتاپ زدن و موفق شدن، اونم تو ایران سه تا چیز اساسی نیاز داره: تیم، تجربه، دانش فنی! اگه داری بسم الله، وگرنه الکی خودتو معطل نکن، هدفگذاری کن برای رسیدن به این سه تا، و تا وقتی میشه برای بقیه کار کن تا یاد بگیری)آقا سرتون رو درد نیارم، استارتاپ هم بافرش داشت خالی میشد و دوباره به پوچی میرسیدم و بازم چیزی نشده بودم. اما یهویی یه شبی، مهران (مهم نیست کیه :))) ) یه حرفی زد که به قول نقی منِ به فکر فرو برد! داستان از این قرار بود که گفت بیا بریم فلان جا من معرفیت میکنم، کار آموز میخوان، برو اونجا js رو یاد بگیر بعدش هم شروع به کار کن. گفتم بابا من از js بدم میاد، گفت بدم میاد یعنی چی، فردا پس فردا نمی خوای زن و بچه نون بدی؟ این چه بهونه ایه آخه! خلاصه که خیلی حرفش سنگین بود. اما بزار بگم چرا از js بدم میومد!بگم؟ ولش کن، تو قسمت بعدی میگم، سرتون درد نیاد، فقط همینقد بدونید، بافر js داشت پر میشد :))شب خوش ...</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Sun, 17 May 2020 22:25:02 +0430</pubDate>
            </item>
                    <item>
                <title>Responsive Design یا Separate Views</title>
                <link>https://virgool.io/@mjavad.seyyedi/responsive-design-%DB%8C%D8%A7-separate-views-plr3f7ztj8rp</link>
                <description>یکی از چالش هایی که من باهاش مواجه بودم، این بود که گهگاه پیش میومد برای یه پروژه ی یکسان مجبور بشم چندتا   view متفاوت بزنم، در حالی که همه شون تا حد زیادی، Functionality های یکسان داشتن. اما خب این اتفاق برای خیلی ها نمی افته که مجبور باشن این کار رو بکنن، بنابر این به یه شکل دیگه ای مساله رو مطرح میکنم.خیلی مواقع پیش اومده که شما باید چیزی که پیاده سازی می کنید، به نسبت display view های مختلف، ریسپانسیو بوده و دیزاین مناسب رو نشون میده. اما خیلی از زمان ها، دیزاین هایی که طراح برای Mobile view و Desktop View میده جوریه که خب پدر developer در میاد تا بخواد پیاده سازیش کنه. خب بزارین ببینیم چه alternative هایی داریم.یه حالت خیلی ساده ش اینه که Mobile View و Desktop View شما تا حد بسیار خوبی شبیه به هم هستن و با استفاده از یه سری CSS Tricks and Tools می تونید اون رو پیاده کنید، این جور موقع ها برای آرامش جان و مال و ناموستون، برید سراغ Flexbox یا CSS Grid و هرچی بیشتر تلاش کنید از یه چیزی مثل bootstrap دوری کنید. ( نظر شخصیمه، کلا اصراری بهش نیست). اما خب این موضوع بحث ما نیست.موضوع اساسی و چالش بر انگیز زمانیه که شما تا یه حد خوبی تفاوت UI و Functionality بین دو تا Layout خودتون دارید (خیلی وقتا اینا میشن سه تا، تبلت هم ساز جدایی خودش رو میزنه). خب بزارید ببینیم من چی کار میکنم. یکی از کارایی که همون اول میرم سراغش جدا کردن view ها هست. اصلا هم لازم نیست دنبال آدرس دهی متفاوت باشید و تفاوت رو از اینجا شروع کنید، از خیلی جاهای راحت تری میشه جدایی رو به وجود آورد. بزارید یه مثال بزنم:من یه آدرس دارم برای هوم پیج مثل localhost:3000/home، خب توی آدرس دهی در router، یه فایل ایندکس رو بهش اساین میکنم که برام سرو بشه به شکل زیر:{
  component: loadable(() =&gt; import(&#039;components/pages/Home&#039;)),
  path: &#039;/home&#039;,
  exact: true,
  fetching: import(&#039;components/pages/Home/fetching&#039;),
},خب این نوع آدرس دهی، میره و از پوشه ی Home برام فایل index رو load میکنه. حالا میرسیم به مرحله ای که باید محتوای فایل index.js رو تعیین کنیم. فایل های index در پروژه هایی که من میزنم به شکل زیر هست:import React from &#039;react&#039;;
import { useSelector, useDispatch } from &#039;react-redux&#039;
import { createStructuredSelector } from &#039;reselect&#039;;

import { useInjectReducer } from &#039;utils/injectReducer&#039;;
import { useInjectSaga } from &#039;utils/injectSaga&#039;;
import reducer from &#039;redux/Vendors/reducer&#039;;
import saga from &#039;redux/Vendors/saga&#039;;

import {loadUser} from &#039;redux/Global/actions&#039;
import {getVendors} from &#039;redux/Vendors/actions&#039;
import {makeSelectVendorsList} from &#039;redux/Vendors/selectors&#039;

import View from &#039;./view&#039;

const key = &#039;vendors&#039;;

export default function VendorsPageIndex(props){
  useInjectReducer({ key, reducer });
  useInjectSaga({ key, saga });

  const mapStateToProps = useSelector(createStructuredSelector({
    vendors: makeSelectVendorsList()
  }));

  const dispatch = useDispatch();

  const mapDispatchToProps = {
    getVendors: data =&gt; dispatch(getVendors(data)),
    loadUser : () =&gt; dispatch(loadUser()),
  }

  const finalProps = Object.assign({}, mapStateToProps, mapDispatchToProps, props)

  return &lt;View {...finalProps}/&gt;
}برای فهم مساله یه توضیح کوتاه بدم اینجا داره چه اتفاقی میوفته. من هر نوع connection ای که با ریداکس نیاز داشته باشم رو اینجا انجام میدم. کلا هر ارتباطی با خارج از این component نیاز باشه، وظیفه ی index هست که اون رو هندل کنه. فایل های Redux من، یعنی Reducer و Saga همه شون lazy load هستن، برای همین وقتی import میشن، باید توسط یک manager ای که اینجا دو هوک useInjectReducer و useInjectSaga هستن، به ساختار store اصلی inject میشن. در مرحله ی بعد، selector هام رو مینویسم و در نهایت هر dispatch ای که نیاز باشه رو اینجا map میکنم و یه funcation به props ام اضافه میکنم. در مرحله ی آخر، تمامی این data رو در یه Object به نام finalProps الصاق میکنم و props های نهاییم رو میسازم.خب تا اینجای کار ، اتفاق خاصی نمی افته. اما در مرحله ی بعد هست که سعی میکنم کار جدا سازی view ها رو انجام بدم. چطور؟ ببینیم:فرض میکنیم دو تا فایل برای دو تا view نوشتیم که component های موبایلی و دسکتاپی ما رو هندل میکنن، این دو تا در دوفایل به نام view.mobile.js و view.desktop.js قرار گرفتن. خب این دو تا رو import میکنیمimport MobileView from &#039;./view.mobile&#039;
import DesktopView from &#039;./view.desktop&#039;بعد از اینکه این دو تا رو import کردیم، باید تصمیم بگیریم از کدوم استفاده کنیم. برای این کار از یه library استفاده میکنیم به نام react-device-detect. شاید بگید میشه از user agent ریکویست ها هم دیتایی که نیازه رو بدست آورد، منم حق رو به شما میدم ولی درد این library خیلی کمتره. به علاوه اینکه کلی دیتای دیگه هم بهتون میده که ممکنه بهش نیاز داشته باشید.خب مقایسه رو انجام میدیم، به شکل زیر:import MobileView from &#039;./view.mobile&#039; 
import DesktopView from &#039;./view.desktop&#039;
import { isMobile } from &#039;react-device-detect&#039;

const View = isMobile ? MobileView : DesktopViewاین کار باعث میشه ویو ها توی هر حالتی که بودید، جدا بشه. اما به نظرتون این کار، درسته؟ جواب مشخصه، خیر. چرا، چون وقتی دارید دو تا ویو رو import میکنید، عملا دارید هر دو رو به باندلتون اضافه می کنید، خب چرا وقتی قراره با browser موبایلم بیام، باید js هایی که برای دسکتاپ نوشتم رو ببینم؟پس بریم سراغ یه راه حل منطقی تر که چیزی نیست جز Dynamic Import. شما میتونید از فانکشن import استفاده کنید ولی من توصیه نمی کنم، یه مقدار دردسر داره، همون روش قدیمی و سنتی CommonJS رو پیش بگیریم بهتره. پس از require استفاده میکنیم. بریم ببینیم چجوری میشه:یکی از کارایی که میتونید بکنید اینه که موقع import کردن هرچیزی به وسیله require بهش متغیر بدید، همین خاصیت باعث میشه کلی جلو بیفتین، یعنی اینکه یه جوری موقع بیلد شدن پروژه بهش بگید که میخوام این آدرس رو برای لود کنی: const View = require(`./views/view.${process.env.WEB_ENV}.js`)این کار میاد هر متغیری که شما به WEB_ENV موقع بیلد شدن پاس میدین رو جایگزین میکنه. با فرض اینکه فایل های view شما توی فولدر sibling فایل index.js به نام views هستن،‌ میتونید از سینتکس بالا استفاده کنید. موقع بیلد شدن هم باید توجه داشته باشید که این مقدار رو به دستورتون پاس بدین، به شکل زیر:cross-env NODE_ENV=production cross-env WEB_ENV=desktopوقتی مقدار desktop رو به WEB_ENV پاس بدین، اون وقت در تمامی import های داینامیکی که نوشین این مقدار جایگزین process.env.WEB_ENV میشه.اما نکته ی اساسی اینه که در صورتی میتونید اینکار رو بکنید که بخواید دو تا domain متفاوت داشته باشید. مثلا example.com برای دسکتاپو m.example.com برای موبایل. دلیلش هم اینه که باید هر کدوم از این ها به صورت جداگانه فایل هایی که بیلد کردین رو براتون سرو کنن. اگر داکر داشته باشید هم میتونید دو تا image متفاوت بسازین و هرکدوم رو روی یه پورت خاص run کنید که به اون دامنه ها وصل شدن. نمونه دستوری build داکرش هم اینجوری میشه:docker build --rm --build-arg NODE_ENV=production --build-arg WEB_ENV=mobile -t production .خب این از جداسازی فایل ها. حالا بریم سراغ لاجیک. یه مساله ای که ممکنه پیش بیاد، قسمت های مشترکه. مطمئنا تا الان این سوال براتون پیش اومده که خیلی از کارایی که من میخوام توی دو تا layout انجام بدم، مشترکه، اما اگه این کاری که شما میگی رو بکنم، Duplication دارم. جونم برات بگه که شاید اگه با وجود HOOKS در دنیای React هنوز یه جا باشه که HOC به درد میخوره همینجاست.من برای اینکه کدهای مشابه رو حذف کنم، یه لایه دیگه بین index و view هام اضافه کردم به نام view.hocاین لایه کارش اینه که تمامی functionality های مشترک، توی این قسمت پیاده سازی میشن و به صورت props در اختیار view های نهایی قرار میگیرن. برای همین شما در index.js ابتدا view.hoc رو import میکنید و بعد از اون داخل view.hoc کدهای dynamic import رو مینویسید:import React from &#039;react&#039;; ...import View from &#039;./view.hoc&#039;;
....
 return &lt;View {...finalProps}/&gt;و فایل view.hoc:import React from &#039;react&#039;;
const View = require(`./views/view.${process.env.WEB_ENV}.js`)
export default function HomePageHOC(props){
       exampleFunction = (a, b) =&gt; {
              return a * b
        }
     return  &lt;View
                 {...props}
                 exampleFunction={exampleFunction}
     /&gt;
}</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Sat, 16 May 2020 22:38:18 +0430</pubDate>
            </item>
                    <item>
                <title>State Manager ای به نام URL</title>
                <link>https://virgool.io/@mjavad.seyyedi/state-manager-%D8%A7%DB%8C-%D8%A8%D9%87-%D9%86%D8%A7%D9%85-url-ifgiudv90jyp</link>
                <description>یکی از مهم ترین و کارآمد ترین state manager هایی که وجود داره و در عین حال همه مون ازش غفلت میکنیم، آدرس صفحه و یا همون url هست. این به این معنی نیست که کلا باید استیت منیجر های قدرتمندی مثل Redux رو بزاریم کنار. حضور ریداکس و ContextAPI در یک پروژه ی Large Scale با React اجتناب ناپذیره. پس بیاین بریم تو بحرش ببینیم منظور از url as a state manager چیه؟سعی میکنم یه تجربه ی عملی ای که داشتم رو بگم تا با مثال واقعی بشه معنی و مفهوم رو برسونم.ما تو اسنپ فود، صفحاتی داریم که در اون گهگاه، شما فیلترهای مختلفی اعمال میکنید. مثل این صفحهخب این صفحه دسته های مختلفی از فیلتر رو داخل خودش داره، مثلوخب، اینجا یه اتفاقی می افته. اونم اینه که علاوه بر اینکه باید انواع مختلف فیلترها رو توی این صفحه هندل کنید، دیپ لینک هایی که به این صفحه میرسن و فیلترهای خاصی درشون وجود داره هم باید درست مدیریت بشن و داده های متناسب با خودشون نشون بدن. بیاین فرض کنیم بخوایم از لوکال استیت و ریداکس استفاده کنیم برای این کار ببینیم. چی میشه:تو مرحله ی اول شما یه سری local variable  و local state تعریف میکنید، با هر تغییر توی مقادیر این فیتلرها، این استیت ها رو آپدیت میکنید، بعد از اینکه این کارها تموم شد، و کاربر روی دکمه ی &quot;اعمال&quot; کلیک کرد شما به عنوان استیت کلی این صفحه، مقادیر فیلتر ها رو توی ریداکس ست میکنین و بعد ریکویست و ادامه ی ماجرا.خب میدونین فاجعه کی رخ میده؟ آفرین وقتی صفحه رو رفرش کنید، یا رفت و برگشتی به این صفحه داشته باشین، همه ی فیلترهای قبلیتون که اعمال شده بود میپره. شاید بگین خبالا، persist ش میکنیم. آفرین منم همینجوری فک میکردم. از وسطای ورژن قبلی بود که ما persistence رو به اپ اضافه کردیم و خب جواب هم داد، اما تا حالا به این دقت کردین که دارین کجا این کار رو میکنین؟ به نظرم جواب همه تون local storage باشه اما هیچ وقت از خودتون پرسیدین که لوکال استوریج چقد استیت منیجر پر هزینه ایه؟اگه به روال معمول، ریداکستون رو توی لوکال استوریج پرسیست کنید، تقریبا با هر آپدیت ریداکس، دارین یه transaction با لوکال استوریج انجام میدین، یعنی یه بار آپدیت لوکال استوریج! به نظرتون برای یه اپ large scale یکم سنگین به نظر نمیاد؟ باعث کند شدن Run Time نمیشه؟ به والله که میشه! اما یه مساله ای ، پرسیست شدن یه وب اپلیکیشن از اوجب واجباته، پس چیکار کنیم که این مساله حل بشه؟؟؟اینجاست که باید بگیم باید تا جایی که میشه transaction ها رو کم کنیم. برای همین یه سری میزنیم به urlحالا فرض کنیم شما میخواین فیلترهایی که گفتم رو ببرین توی query parameter های صفحه و اونجا استیتشون رو نگه دارین. ترتیب کار چجوری میشه؟!این دفعه باز هم مجبورین یه سری local state تعریف کنید، تغییرات کاربر رو توی اون ذخیره کنید و بعد از اینکه روی &quot;اعمال&quot; کلیک کرد، دیگه سراغ ریداکس نمیریم! حتی سراغ ContextAPI هم نمیریم، چون یه چیز منطقی تر به نام url وجود داره. بعد از کلیک روی اعمال ، استیت صفحه رو توی url آپدیت میکنیم!این یعنی چی؟ این مساله یعنی اینکه شما key value های فیلترهاتون رو میریزین توی query parameter و آدرس جدید تولید شده رو با مقدار قبلی replace میکنید، تنها کاری که باید بکنید اینه که یه useEffect روی آدرس داشته باشین و هر وقت آدرستون آپدیت شد، درخواست جدید رو به سرور بدین. این کار باعث میشه هر رفرش و تغییر مسیری هم شما رو برمیگردونه به همین استیت، بدون نیاز به persist شدن.آمّااااقضیه اینه که این کار به این سادگی ها هم نیست. چرا؟ چون که مجبورین کلی util یا custom hook بنویسین که این کار رو براتون مدیریت کنه. ولی یه راه فرار بهتون نشون میدم که همه چی رو آسون میکنه اونم این package زیباست: https://www.npmjs.com/package/use-query-params این پکیج همه ی چیزی که شما برای این مدل استیت منیجینگ نیاز دارین رو بهتون میده به سادگی. تنها کاری که باید بکنید اینه که object ای که در query param قراره ساخته بشه رو بدین بهش. به این صورت:const [query, setQuery] = useQueryParams({
x: NumberParam,
q: StringParam,
filters: withDefault(ArrayParam, []),
});این کار یعنی من قراره توی query parameter هام، یه متغیر x داشته باشم از نوع number، یه متغیر q از جنس string و یه filters که به صورت پیشفرض قراره arrayParams باشه. این هوک به شما دو تا مقدار بر میگردونه. یه Object به نام query و یه تابع به نام setQueryهر وقت خواستین یکی از این مقادیر رو آپدیت کنید، کافیه فقط روی همون مقدار، setQuery بزنید، خودش براتون آدرس رو آپدیت میکنه. مثلا:setQuery({ 
x: Math.random
 },&#039;replaceIn&#039;)این کار به q و filters دست نمیزنه و مقادیر قبلیشون رو نگه میداره و فقط مقدار x رو آپدیت میکنه. حالا تنها چیزی که باید رعایت کنید، useEffect روی query هست. هر وقت setQuery کال بشه، این useEffect هم کال شده و آپدیت جدید رو بهتون میده اونوقت با مقادیر جدید فیلترهاتون میتونید حال کنید!یه نکته ی خیلی خیلی مهم هم اینه که میتونید Custom تایپ تعریف کنید. یعنی بگین متغیر y من از جنس مثلا CustomObject هست و برای CustomObject یه تابع بنویسید که آبجکت فیلتر شما رو میگیره و تبدیل میکنه به مقداری که باید توی y بشینهمثلا:const MyParam = {
      encode(value) 
             return `${value * 10000}`;
       },
decode(strValue) {
      return parseFloat(strValue) / 10000;
}}
const [query, setQuery] = useQueryParams({ foo: MyParam });
setQuery({ foo: 99 })این تایپ Custom عدد ورودی رو در 10000 ضرب کرده و در پارامتر foo جایگذاری میکنهحالا برای اینکه دیپ لینک های خارجی درست مپ بشین به این صفحه، تنها کاری که باید بکنین اینه که translator بنویسید که این ساختار کاستوم خودتون رو بهتون تحویل بده. همین و تمام!</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Tue, 12 May 2020 00:26:09 +0430</pubDate>
            </item>
                    <item>
                <title>مهم ترین پلاگین های وب پک (webpack)</title>
                <link>https://virgool.io/wptips/%D9%85%D9%87%D9%85-%D8%AA%D8%B1%DB%8C%D9%86-%D9%BE%D9%84%D8%A7%DA%AF%DB%8C%D9%86-%D9%87%D8%A7%DB%8C-%D9%88%D8%A8-%D9%BE%DA%A9-webpack-pfkpzeygs824</link>
                <description>این چند ساله به دلیل پروژه های مختلفی که درگیرشون بودم، با پلاگین های مختلفی از وب پک سر و کله زدم. معمولا اگه سرچ کنید برای یه کار خاص، خیلی راحت و مستقیم به ابزارهاش دسترسی پیدا میکنید. ولی من اینجا واسه دلم، سعی میکنم به صورت تجمیعی از ابزارهای مختلفی که باهاشون کار کردم اسم ببرم. تقریبا همه شون رو میگم، حتی ساده و اساسی ترینش رو  1- mini-css-extract-plugin : یک پلاگین ساده برای استخراج css به فایل های جدا از هم. این پلاگین به ازای هر فایل js که شامل css باشه، یه فایل css می سازه.    2- html-webpack-plugin : پلاگینی برای ساده سازی ساخت فایلهای html ای که قراره باندل webpack شما را serve کنه.  این پلاگین به شدت برای زمانی که از hash برای نام فایل هاتون استفاده می کنید کاربردیه. به صورت اتوماتیک تمامی dependency ها رو به فایل html شما متصل می کنه.3- circular-dependency-plugin: مواردی که دارای وابستگی چرخشی باشن مخصوصا در پروژه های بزرگ و پیچیده، اجتناب ناپذیر هستن. این ماژول در وب پک، تا حد خیلی زیادی مشکلات این مساله رو حل میکنه.4- webpack-pwa-manifest: یکی از نیازمندی های اصلی تو ساخت PWA، وجود فایل manifest.json هست. اطلاعاتی که برای معرفی اپلیکیشن مثل نام، آیکن ، ورژن و ... هست در این فایل قرار میگیرند. این پلاگین به شما کمک میکنه تا این فایل به صورت اتوماتیک تولید شده و در باندل قرار بگیره. (یک سری تفاوت ها بین android و ios وجود داره که اگه از این پلاگین استفاده کنید دردش کمتره)5- offline-plugin: یکی دیگه نیازمندی های PWA ، وجود فایلservice worker هست. طبق این تجربه ی چندساله، شما دلتون نمی خواد سرویس ورکر رو خودتون بنویسید، باور کنید دلتون نمیخواد. پس بسپریدش به یه پلاگین تا براتون تولیدش کنه. دو تا انتخاب خیلی خوب وجود داره.الف: workbox ب: offline-plugin . گزینه اول به صورت original توسط خود گوگل معرفی شده و خب قطعا می تونه انتخاب اول همه باشه، اما گزینه ی دوم هم در خود گوگل Recommend شده و طرفدارای زیادی داره. هر دوشون خوب و قدرتمند هستن. برای همین زیاد فرقی نمی کنه از کدوم استفاده کنید اما اگه خیلی بخوابین ریز بشین و کارای اساسی ازش بخواین، به نظرم با workbox برید جلو. اما خودم تو این سالا همه ش با Offline-plugin کار کردم ، خب باهاش راحت تر بودم.    6- terser-webpack-plugin : در یه جمله، minify your JavaScript . تمامی کارهای مربوط به uglification و minimize کردن رو براتون هندل می کنه.7- compression-webpack-plugin: بعد از &quot;زشت سازی&quot; و minimize کردن پروژه تون، برای کاهش سایز فایل های js و سرعت بیشتر دانلودشون، &quot;باید&quot; فایل هاتون رو فشرده کنید. این پلاگین فایل های شماره با فرمت .gz فشرده می کنه. ( می تونین از brotli-webpack-plugin هم استفاده کنید، این پلاگین براتون فشرده سازی با فرمت .br انجام میده. فشرده سازی بهینه تری داره، گویا!)8- preload-webpack-plugin: اگه لود تایم و نمره ی lighthouse براتون مهمه، یکی از مهم ترین پلاگینا همینه. لود asynchronous  فایل های js و chunk هایی که ساختین. این پلاگین به شما امکان لود همه ی فایل ها یا فقط فایل هایی که در لحظه نیاز دارین میده.تجربه ی شخصی من میگه اگه حجم پروژه تون کوچیکه، از سرویس ورکر استفاده میکنید و لودتایم خیلی براتون مهم نیست، همه فایل ها رو همون اول لود کنید. اما اگه سایز پروژه بالاست و لود تایم براتون مهمه از ویژگی lazyLoad این پلاگین استفاده کنین. خیلی راحت و بدون خون ریزی خودشون هندلش میکنن.9- webpack-bundle-analyzer: بهترین و زیباترین پلاگینی که دیدم. نصبش کنید از پروژه تون بیلد پروداکشن بگیرین. اون وقت روی پورت 8888 تمامی chunk هاتون رو در سه سایز عادی، minify و gz شده ببنید. محتوای هر فایل و اینکه شامل چه قسمت هایی هستن، حجم هر قسمت چیه و چه ماژول های خارجی درشون وجود دارن دیده میشه. بهترین ابزار برای ارتقا پرفورمنس و کاهش حجم پروژه10- webpack-manifest-plugin: اگه لیستی از تمامی asset های تولید شده در باندلتون نیاز دارین، به این پلاگین مراجعه کنید. یه لیست میسازه و یه فایل json بهتون تحویل میده.11- copy-webpack-plugin: خیلی وقتا پیش میاد که نیاز دارین یه سری فایل هاتون رو در طول بیلد شدن پروژه کپی کنید و در فولدر نهایی قرار بدین. از همین پلاگین استفاده کرده و عشقشو ببرید.</description>
                <category>محمدجواد سیدی</category>
                <author>محمدجواد سیدی</author>
                <pubDate>Mon, 11 May 2020 11:46:08 +0430</pubDate>
            </item>
            </channel>
</rss>