نکات تکمیلی بهینه‌سازی + نحوه‌ی کار Virtual DOM

Additional Optimization Tips + How Virtual DOM Works

23 بهمن 1399
نکات تکمیلی بهینه سازی + نحوه ی کار Virtual DOM

در چه مواقعی بهینه سازی کنیم؟

حالا که با memo و shouldComponentUpdate آشنا شدیم، منطقی است که بخواهیم تک تک کامپوننت هایمان را با آن ها بهینه سازی کنیم، درست است؟ آیا بهتر نیست تمام کامپوننت هایمان را بهینه سازی کنیم تا سرعت برنامه مان را افزایش دهیم؟

شاید در نظر اول این حرف منطقی به نظر بیاید اما اصلا درست نیست! کامپوننت هایی وجود دارند که ورودی هایشان را از کامپوننت پدر خود می گیرند، در این صورت اگر کامپوننت پدر بروزرسانی شود و داده هایش تغییر کند، کامپوننت فرزند هم حتما باید بروزرسانی شود. بنابراین این کامپوننت های فرزند همیشه باید بروزرسانی شوند. در چنین حالتی اگر از متدهای shouldComponentUpdate یا memo استفاده کنید، این متدها چک می کنند که آیا ورودی تغییر کرده است؟ و جواب مثل همیشه بله خواهد بود و کامپوننت شما بروزرسانی می شود. آیا متوجه مشکل شدید؟ ما یک چندین خط اضافه کد نوشتیم تا چیزی را چک کنیم که جوابش همیشه «بله» خواهد بود و با این کار تنها سرعت برنامه خود را پایین آورده ایم...

بنابراین از خودتان این سوال را بپرسید: آیا این کامپوننت فرزند یک کامپوننت بزرگتر است که تغییر در آن ربطی به فرزندش نخواهد داشت؟ اگر جواب بله بود (مانند Persons.js) باید کدها را چک کنیم، در غیر این صورت از چک کردن های بیهوده دوری کنید.

pureComponent چیست؟

نکته دیگری که باید در مبحث بهینه سازی به خاطر بسپارید pureComponent ها هستند. اگر در یک کامپوننت کلاس-محور هستید و می خواهید از shouldComponentUpdate برای مقایسه تمام prop های مربوط استفاده کنید، بهتر است دست نگه دارید! چرا که یک روش بهتر وجود دارد.

برای درک بهتر یک مثال برایتان میزنم؛ به فایل Persons.js بروید:

    shouldComponentUpdate(nextProps, nextState) {
        console.log('[Persons.js] shouldComponentUpdate');
        if (nextProps.persons !== this.props.person) {
            return true;
        } else {
            return false;
        }
    }

در این قسمت از کدها Persons مقایسه می شود. حالا اگر به قسمت های پایین تر نگاه کنید:

    render() {
        console.log('[Persons.js] rendering...');

        return this.props.persons.map((person, index) => {
            return (
                <Person
                    click={() => this.props.clicked(index)}
                    name={person.name}
                    age={person.age}
                    key={person.id}
                    changed={(event) => this.props.changed(event, person.id)} />
            );
        });
    }

متوجه می شوید که علاوه بر prop های استفاده شده (مربوط به persons) دو تابع changed و clicked را نیز داریم. فرض کنید ممکن است که این تابع ها تغییر کنند (مثلا از خارج از برنامه به ما ارسال شوند). در چنین حالتی کامپوننت ما باید خبردار شده و بروزرسانی شود بنابراین باید کد را بدین شکل بنویسیم:

    shouldComponentUpdate(nextProps, nextState) {
        console.log('[Persons.js] shouldComponentUpdate');
        if (nextProps.persons !== this.props.person ||
            nextProps.changed !== this.props.changed ||
            nextProps.clicked !== this.props.clicked) {
            return true;
        } else {
            return false;
        }
    }

توجه کنید که این کد تنها یک مثال است. ممکن است تعداد prop های ما بسیار بیشتر از این سه مورد باشد.

در چنین حالتی کدهای ما بسیار طولانی می شوند. راه حل چیست؟

راه حل استفاده از PureComponent ها است. ابتدا کد مربوط به shouldComponentUpdate را کامنت یا حذف کنید. باید PureComponent را وارد فایل خود کنیم. بنابراین کد زیر:

import React, { Component } from 'react';

تبدیل به این کد می شود:

import React, { PureComponent } from 'react';

حالا کلاس کامپوننت ما نیز به جای extend کردن component باید PureComponent را extend کند:

class Persons extends PureComponent {

حالا اگر به مرورگر بروید می بینید که با حذف Cockpit هیچ چیزی از سمت Persons اجرا نمی شود. به این دلیل که PureComponent در واقع یک کامپوننت عادی است که خودش به صورت خودکار shouldComponentUpdate را پیاده سازی کرده و تمام prop ها را نیز چک می کند بنابراین این گزینه هم یک گزینه عالی برای بهینه سازی است.

React چگونه DOM اصلی را بروزرسانی می کند؟

همانطور که می دانید react یک virtual DOM دارد که از DOM مرورگر جدا است. بنابراین برای جداسازی این دو از این به بعد به DOM مرورگر، DOM واقعی یا اصلی می گوییم. حالا سوال اینجاست: react چطور DOM واقعی را بروزرسانی می کند؟

زمانی که متد render فراخوانی می شود به معنی ارسال مستقیم کدها به DOM اصلی و render شدن آنها نیست (چه کامپوننت های کلاس-محور و چه کامپوننت های کاربردی). متد render بیشتر شبیه یک پیشنهاد است که می گوید HTML باید بدین شکل در بیاید. ممکن است متد render صدا زده شود و نتیجه اش همان HTML قبلی باشد؛ ما هم به همین دلیل از shouldComponentUpdate استفاده می کردیم تا از فراخوانی render به صورت بیهوده جلوگیری کنیم... اما فرض کنید که این کار را هم نکرده باشیم، یا به طور مثال props تغییر کند اما به هر دلیل نتیجه باز هم همان HTML قبلی بشود. در چنین حالتی نباید تصور کنید که کدهای ما سریعا در DOM اصلی render می شوند.

react دو نسخه virtual DOM دارد؛ یک virtual DOM old (به معنی DOM مجازی قدیمی - آخرین نسخه render شده در DOM اصلی مرورگر همین نسخه است) و یک virtual DOM جدید که به آن re-rendered virtual DOM هم می گوییم. دلیل استفاده از virtual DOM این است که virtual DOM بسیار سریع تر از DOM واقعی در مرورگر است. این virtual DOM یک نسخه شبیه به DOM واقعی در جاوا اسکریپت است بنابراین بدون اینکه چیزی را درون مرورگر نمایش دهیم می توانیم یک نسخه DOM را درون جاوا اسکریپت که سریع تر است داشته باشیم.

re-rendered virtual DOM همان DOM ای است که هنگام فراخوانی متد render ساخته می شود (حواستان باشد که هنوز به DOM اصلی دست نزده ایم). react این دو نسخه DOM (re-rendered virtual DO و old virtual DOM) را مقایسه می کند تا ببیند آیا تفاوتی موجود است یا خیر. اگر تفاوتی بین این دو نسخه را پیدا کند، به DOM اصلی در مرورگر رفته و فقط قسمت هایی را تغییر می دهد که تفاوت دارند بنابراین کل DOM اصلی را re-render نمی کند بلکه فقط قسمت های متفاوت بروزرسانی می شوند؛ به طور مثال اگر متن یک دکمه تغییر کند، کل دکمه re-render نمی شود، بلکه فقط متن آن بروزرسانی می شود.

اگر هم تفاوتی بین این دو نسخه پیدا نکند، با اینکه render فراخوانی شده است و مقایسه انجام می شود، اصلا به DOM واقعی دست نمی زند.

امیدوارم به طور کامل با فرآیندهای پشت صحنه react آشنا شده باشید.

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

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