مقالات

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


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

پس از خواندن این پست وبلاگ،
بروکس لیبراند این ترفند را به کار گرفت و
این نتیجه شد:

هیجان زده؟ بیایید آن را با یک مثال تخیلی ساده تجزیه کنیم و سپس در مورد آن صحبت کنیم
این چه کاربردی برای شما در برنامه های روزانه شما دارد.

// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-g9mt5

import * as React from 'react'
import ReactDOM from 'react-dom'

function Logger(props) {
	console.log(`${props.label} rendered`)
	return null // what is returned here is irrelevant...
}

function Counter() {
	const [count, setCount] = React.useState(0)
	const increment = () => setCount((c) => c + 1)
	return (
		div>
			button onClick={increment}>The count is {count}button>
			Logger label="counter" />
		div>
	)
}

ReactDOM.render(Counter />, document.getElementById('root'))

هنگامی که این اجرا می شود، “counter rendered” در ابتدا به کنسول وارد می شود و
هر بار که شمارنده افزایش می یابد، یک “شمارگر تصویری” در آن ثبت می شود
کنسول این به این دلیل اتفاق می افتد که وقتی دکمه کلیک می شود، وضعیت تغییر می کند و
React باید باعث شود عناصر React جدید بر اساس این تغییر حالت رندر شوند.
هنگامی که این عناصر جدید را دریافت می کند، آنها را رندر می کند و به DOM ارسال می کند.

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

// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-o9e9f

import * as React from 'react'
import ReactDOM from 'react-dom'

function Logger(props) {
	console.log(`${props.label} rendered`)
	return null // what is returned here is irrelevant...
}

function Counter(props) {
	const [count, setCount] = React.useState(0)
	const increment = () => setCount((c) => c + 1)
	return (
		div>
			button onClick={increment}>The count is {count}button>
			{props.logger}
		div>
	)
}

ReactDOM.render(
	Counter logger={Logger label="counter" />} />,
	document.getElementById('root'),
)

آیا متوجه تغییر شده اید؟ بله ما گزارش اولیه را دریافت می کنیم اما پس از آن نمی توانیم
سیاهههای مربوط به زمانی که ما روی دکمه در حال حاضر کلیک کنید! AAAAH!؟

اگر می خواهید تمام جزئیات فنی عمیق را نادیده بگیرید و به «چی» برسید
آیا این برای من به این معنی است که حالا برو و خودت را به آنجا بیانداز

پس دلیل این تفاوت چیست؟ خوب، این به عناصر React مربوط می شود. چرا
زود استراحت نکنید و پست وبلاگ من را بخوانید
“JSX چیست؟” برای به‌روزرسانی سریع عناصر React
و رابطه آنها با JSX.

هنگامی که React تابع شمارنده را فراخوانی می‌کند، چیزی که کوچک به نظر می‌رسد را برمی‌گرداند
مانند این:

// some things removed for clarity
const counterElement = {
	type: 'div',
	props: {
		children: [
			{
				type: 'button',
				props: {
					onClick: increment, // this is the click handler function
					children: 'The count is 0',
				},
			},
			{
				type: Logger, // this is our logger component function
				props: {
					label: 'counter',
				},
			},
		],
	},
}

به این موارد، اشیاء توصیفگر UI می گویند. آنها رابط کاربری را که React باید داشته باشد توصیف می کنند
در DOM ایجاد کنید (یا از طریق مؤلفه های بومی برای react native). بیایید کلیک کنیم
و تغییرات را ببینید:

const counterElement = {
	type: 'div',
	props: {
		children: [
			{
				type: 'button',
				props: {
					onClick: increment,
					children: 'The count is 1',
				},
			},
			{
				type: Logger,
				props: {
					label: 'counter',
				},
			},
		],
	},
}

تا آنجا که ما می توانیم بگوییم، تنها تغییرات هستند onClick و children پشتیبانی
در button عنصر با این حال، همه چیز کاملاً جدید است! از آنجایی کهدر طلوع زمان با استفاده از React، شما این اشیاء را کاملاً جدید برای هر یک ایجاد کرده اید
به تصویر کشیدن (خوشبختانه، حتی مرورگرهای تلفن همراه نیز در این کار بسیار سریع عمل می کنند، بنابراین آن چیز وجود دارد
هرگز یک مشکل عملکرد قابل توجه نبوده است).

در واقع، ممکن است کاوش بخش‌های این درخت React آسان‌تر باشد
موارد بین پیش نمایش ها یکسان است، بنابراین مواردی که تغییر نکرده اند در اینجا آمده است
بین این دو رندر:

const counterElement = {
	type: 'div',
	props: {
		children: [
			{
				type: 'button',
				props: {
					onClick: increment,
					children: 'The count is 1',
				},
			},
			{
				type: Logger,
				props: {
					label: 'counter',
				},
			},
		],
	},
}

همه انواع عناصر یکسان هستند (این معمولی است)، a label پشتیبانی برای
عنصر Logger بدون تغییر است. با این حال، شی prop خود هر بار تغییر می کند
render، حتی اگر ویژگی های این شی مانند مورد قبلی باشد
شیء نگهدارنده

خوب، این ضربه زننده همینجاست. چون شیء Logger props دارد
تغییر کرد، React باید تابع Logger را مجددا راه اندازی کند تا مطمئن شود که این کار را نمی کند
هر JSX جدید را بر اساس شیء props جدید دریافت کنید (علاوه بر افکت هایی که می توانند
باید بر اساس تغییر پایه ها شروع شود). اما چه می شد اگر می توانستیم پیشگیری کنیم
از تغییر بین پیش نمایش ها؟
اگر پایه ها تغییر نکردند، واکنش نشان دهید
می داند که افکت های ما نیازی به اجرای مجدد ندارند و JSX ما نیازی به تغییر ندارد
(زیرا React به این واقعیت متکی است که متدهای رندر ما باید فاقد قدرت باشند).
این دقیقاً همان چیزی است که React برای انجام آن کدگذاری شده است
همینجا
و از زمانی که React شروع به کار کرد اینطور بوده است!

باشه ولی مشکل همینه
react یک نام تجاری جدید ایجاد می کند props هر بار که یک عنصر React ایجاد می کنیم، شیء کنید،
بنابراین چگونه اطمینان حاصل کنیم که شی props بین رندرها تغییر نمی کند؟
امیدواریم اکنون آن را دریافت کرده باشید و دلیل مثال دوم بالا را بفهمید
Logger را رندر نکرد. اگر یک بار عنصر JSX را ایجاد کرده و دوباره از آن استفاده کنیم
همان، سپس ما هر بار همان JSX را دریافت خواهیم کرد!

در اینجا مثال دوم دوباره (بنابراین لازم نیست به عقب بروید):

// play with this on codesandbox: https://codesandbox.io/s/react-codesandbox-o9e9f

import * as React from 'react'
import ReactDOM from 'react-dom'

function Logger(props) {
	console.log(`${props.label} rendered`)
	return null // what is returned here is irrelevant...
}

function Counter(props) {
	const [count, setCount] = React.useState(0)
	const increment = () => setCount((c) => c + 1)
	return (
		div>
			button onClick={increment}>The count is {count}button>
			{props.logger}
		div>
	)
}

ReactDOM.render(
	Counter logger={Logger label="counter" />} />,
	document.getElementById('root'),
)

پس بیایید بررسی کنیم چیزهایی که یکسان هستند بین رندرها:

const counterElement = {
	type: 'div',
	props: {
		children: [
			{
				type: 'button',
				props: {
					onClick: increment,
					children: 'The count is 1',
				},
			},
			{
				type: Logger,
				props: {
					label: 'counter',
				},
			},
		],
	},
}

از آنجایی که عنصر Logger کاملاً بدون تغییر است (و بنابراین props هستند
همچنین بدون تغییر)، React می تواند به طور خودکار این بهینه سازی را برای ما و
برای رندر کردن مجدد عنصر Logger به خود زحمت ندهید زیرا لازم نیست
به هر حال دوباره تصویر شده این در اصل شبیه است React.memo جز به جایبا بررسی هر یک از props به صورت جداگانه، React شی props را بررسی می کند
به طور کل نگر

به طور خلاصه، اگر مشکلات عملکردی دارید، این را امتحان کنید:

  1. “بالا بردن” جزء گران قیمت به پدر و مادر که در آن کمتر ارائه می شود
    اغلب
  2. سپس جزء گران قیمت را به عنوان پشتیبان عبور دهید.

ممکن است متوجه شوید که این مشکل عملکرد شما را بدون نیاز به توزیع مجدد حل می کند
React.memo کل پایه کد شما مانند یک نوار چسب غول پیکر مزاحم 🤕😉

ایجاد یک نسخه نمایشی کاربردی از یک برنامه آهسته در React دشوار است زیرا تا حدودی
نیاز به ساخت یک برنامه کامل دارد، اما من یک برنامه نمونه ساخته شده که دارای یک است
قبل / بعد از آن می توانید بررسی کنید و با آن بازی کنید:

خطا در جاسازی https://codesandbox.io/s/react-codesandbox-qtdob

یکی از چیزهایی که می خواهم اضافه کنم این است که اگرچه اینطور است بهتر برای استفاده از سریعتر
نسخه این کد، هنوز در هنگام رندر بسیار ضعیف عمل می کند
در ابتدا و اگر واقعاً مجبور به انجام این کار شود، عملکرد بسیار بدی خواهد داشت
رندر دیگری از بالا به پایین (یا زمانی که سطرها/ستون ها را به روز می کنید). این یک است
یک مشکل عملکرد که احتمالاً باید خودش حل شود
(صرف نظر از اینکه چند رندر مورد نیاز است). همچنین، لطفا این را به خاطر بسپارید
codesandbox از نسخه توسعه React استفاده می کند که واقعی را در اختیار شما قرار می دهد
تجربه خوبی در توسعه دارد اما بسیار کندتر از تولید است
نسخه React

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

موفق باشید!

می‌خواهم یادداشتی اضافه کنم که اگر از یک زمینه قدیمی استفاده می‌کنید، نمی‌توانید
این بهینه سازی را در نظر بگیرید که React یک مورد خاص برای آن دارد
اینجابنابراین افرادی که نگران عملکرد هستند احتمالا باید از میراث مهاجرت کنند
زمینه


Source link

نوشته های مشابه

دیدگاهتان را بنویسید

همچنین ببینید
بستن
دکمه بازگشت به بالا