Lazy Load کردن Route ها

22 بهمن 1399
Lazy Load کردن Route ها

به جلسات آخر این فصل نزدیک شده ایم و تنها یک مبحث دیگر از این فصل باقی مانده است؛ Lazy Load کردن Route ها.

مبحث Lazy Load کردن Route ها از مباحث پیشرفته است اما پیاده سازی آن اصلا مشکل نیست. برای شروع ابتدا به فایل Blog.js رفته و auth درون state را روی true بگذارید تا بتوانیم به صفحه ی New Post برویم. حالا dev tools مرورگرتان را باز کرده و سربرگ network را باز کنید. در نهایت به صفحه ی new-post بروید و حواستان به قسمت network باشد:

دانلود شدن تمام کد های ما (فایل bundle.js) - مبحث Lazy Load کردن
دانلود شدن تمام کدهای ما (فایل bundle.js)

همانطور که می بینید یک فایل به نام bundle.js وجود دارد که در این صفحه دانلود شده است. این فایل تمام سورس کد ما را درون خود قرار داده است و فعلا سایز بزرگی دارد چرا که در حالت توسعه (development build) هستیم نه حالت تولید (production build). اگر برنامه ی ما بسیار بزرگ بوده و قسمت های کاملا مستقلی هم داشته باشد چرا باید تمام سورس کد را یکجا دانلود کنیم؟ شاید کاربری هیچ گاه به قسمت خاصی از برنامه ی ما سر نزند؛ به طور مثال اگر در برنامه ی ما کاربر هیچ وقت روی new-post کلیک نکند چرا باید کدهای آن دانلود و بارگذاری شوند؟

واقعیت این است که برای برنامه های بسیار کوچکی مانند برنامه ی ما، چنین تغییری عقلانی نیست چرا که برنامه بسیار بسیار کوچک است و جدا سازی قسمت های مختلف آن برای lazy loading هیچ تفاوت معناداری در عملکرد آن نخواهد داشت (بالعکس تعداد درخواست ها به سرور افزایش پیدا می کند) اما در برنامه های بسیار بزرگ اینطور نیست. این قابلیت که با نام lazy loading یا code splitting شناخته می شود تنها با create-react-app و react-router 4 کار می کند چرا که شدیدا وابسته به پیکربندی webpack است و مبحث پیشرفته ای است.

برای شروع درون پوشه ی src یک پوشه ی دیگر به نام hoc ایجاد کنید که دارای فایلی به نام asyncComponent.js باشد. در این فایل ابتدا باید React را وارد کنیم:

import React, { Component } from 'react';

سپس یک کامپوننت کاربردی (functional) می سازم که یک کلاس را return می کند. این کلاس مثل همیشه یک متد render می خواهد و دارای state است. همچنین به عنوان پارامتر یک تابع را دریافت می کند (importComponent) که بعدا در مورد آن بیشتر توضیح می دهم. این پارامتر قرار است مقدار state را تغییر دهد و این تغییر درون componentDidMount انجام خواهد شد. بنابراین محتویات این فایل تا الان باید به شکل زیر باشد:

import React, { Component } from 'react';

const asyncComponent = (importComponent) => {
    return class extends Component {

        state = {
            component: null
        }

        componentDidMount() {

        }

        render() {

        }
    }
}

همانطور که قبلا هم گفتم importComponent قرار است یک تابع باشد یا به شکل دقیق تر یک promise است (promise ها را در جلسات قبل بررسی کردیم). من می خواهم این تابع را درون componentDidMount اجرا کنم بنابراین می گویم:

componentDidMount() {
    importComponent().then();
}

حالا در قسمت then آن یک آرگومان دریافت می کنیم که یک خصوصیت خواهد داشت. نام این خصوصیت default است و همان کامپوننتی است که قرار است بارگذاری شود! بنابراین می گویم:

componentDidMount() {
    importComponent().then(cmp => {
        this.setState({ component: cmp.default });
    });
}

آرگومانی که دریافت کرده ایم (cmp که مخفف component است) می تواند هر نام دیگری داشته باشد. مهم ترین نکته این است که ما می خواهیم خصوصیتی به نام default را روی state خودمان تنظیم کنیم (دستور SetState). یعنی بالاخره state.component ما دارای مقداری غیر از null خواهد شد، یعنی همان کامپوننتی که قرار است بارگذاری شود، بنابراین باید آن را درون JSX بارگذاری کنیم:

render() {
    const C = this.state.component;

    return C ? <C {...this.props} /> : null;
}

در اینجا متغیری به نام C ایجاد کرده ایم که مقدار state.component را دریافت خواهد کرد. سپس بررسی می کنیم که آیا C تعریف شده است یا خیر (تا زمانی که state.component برابر null باشد این شرط true نخواهد بود) و اگر تعریف شده بود آن را به صورت یک کامپوننت عادی برمی گردانیم در غیر این صورت همان مقدار null را برایش می گذارم. من از اپراتور spread نیز استفاده کرده ام تا prop های احتمالی را به این کامپوننت پاس بدهم. در نهایت باید این کامپوننت را export کنیم:

export default asyncComponent;

فایل را ذخیره کرده و به Blog.js برگردید. اگر به ابتدای این فایل نگاه کنید دستور import برای NewPost را خواهید دید:

import NewPost from './NewPost/NewPost';

اما ما می خواهیم از این به بعد این قسمت را به صورت پویا بارگذاری کنیم. در چنین حالتی مثل حالت بالا که می گوییم فلان مقدار را از فلان جا import کن (مثلا import x from ‘x’) به webpack اعلام کرده ایم که مقدار مورد نظر از وابستگی های پروژه است بنابراین webpack این مقدار یا دستور import را درون فایل bundle.js قرار خواهد داد. مسئله اینجاست که این موضوع دقیقا برخلاف چیزی است که ما می خواهیم! ما می خواهیم lazy load انجام دهیم یعنی NewPost درون bundle.js نباشد و تنها زمانی بارگذاری شود که به آن احتیاج داشته باشیم. بنابراین ابتدا این دستور import را کامنت کنید:

// import NewPost from './NewPost/NewPost';

حالا باید یک متغیر جدید بسازیم که از تابع موجود در فایل asyncComponent.js استفاده خواهد کرد پس ابتدا این فایل را import می کنیم:

import asyncComponent from '../../hoc/asyncComponent';
// import NewPost from './NewPost/NewPost';

سپس می گویم:

import asyncComponent from '../../hoc/asyncComponent';
// import NewPost from './NewPost/NewPost';

const AsyncNewPost = asyncComponent();

همانطور که قبلا گفتم این تابع یک آرگومان می گیرد که همان importComponent بود. importComponent باید یک تابع anonymous باشد که دستور import را به صورت یک تابع برمی گرداند!

const AsyncNewPost = asyncComponent(() => {
    return import();
});

نوشتن دستور import به صورت یک تابع که احتمالا عجیب به نظر برسد یک syntax خاص است. در این حالت هر چیزی که به عنوان آرگومان ()import پاس داده شود (بین پرانتز هایش قرار بگیرد) فقط زمانی import می شود که تابع anonymous ما اجرا شود. از طرفی می دانیم که این تابع anonymous تنها زمانی اجرا خواهد شد که ما متغیر AsyncNewPost را درون صفحه render کنیم. به همین سادگی می توان گفت:

const AsyncNewPost = asyncComponent(() => {
    return import('./NewPost/NewPost');
});

حالا تنها زمانی NewPost را بارگذاری و import می کنیم که متغیر AsyncNewPost در جایی render شود. به قسمت JSX همین فایل (Blog.js) بروید و در Route ای که مسئول بارگذاری کامپوننت NewPost است تغییر زیر را ایجاد کنید:

{this.state.auth ? <Route path="/new-post" component={AsyncNewPost} /> : null}

یعنی به جای NewPost، کامپوننت AsyncNewPost را بارگذاری کنید. فایل ها را ذخیره کرده و به مرورگر بروید. در مرورگر در سربرگ network می توانید شاهد بارگذاری NewPost باشید؛ باید یک بار روی NewPost کلیک کنید تا به این صفحه بروید و سپس مشاهده خواهید کرد که فایلی به نام 1.chunk.js سریعا بارگذاری خواهد شد:

پیدا شدن فایل chunkjs - Lazy Load کردن
پیدا شدن فایل chunkjs

همانطور که می بینید فایل bundle.js هنوز هم بارگذاری می شود که طبیعی است. فایل bundle.js همیشه بارگذاری خواهد شد چرا که قسمت اصلی برنامه است اما تفاوت اینجاست که دیگر شامل کدهای NewPost نخواهد شد. در قسمت بعدی روش دیگری را برای lazy load کردن کامپوننت ها آموزش خواهیم داد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

سجاد
24 بهمن 1398
سلام و خسته نباشید، برای Vue هم همچین دوره جامعی در نظر دارید؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
28 بهمن 1398
سلام دوست عزیز، بله حتما در نظر دارم اما فعلا منتظر موندم تا ببینم نسخه ی جدید Vue (نسخه ی 3) کی قرار هست بیاد. اگر زیاد به تاخیر بیفته (که احتمال زیاد میوفته) احتمالا دوره ی vue 2 رو برگزار کنیم و بعدا وقتی vue 3 اومد بریم سراغش. حتما دوره اش منتشر میشه ان شاء الله

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.