تکه تکه کردن کد
بستهبندی کردن (bundling)
فایلهای بیشتر برنامههای ریاکت با کمک ابزارهایی مانند Webpack، Rollup یا Browserify بستهبندی (bundle) میشود. فرآیند بسته بندی کردن فایلها به پیدا کردن فایلهای ایمپورت شده و قرار دادن محتوای همه آنها در یک فایل “بسته” گفته میشود. میتوان این بسته را در یک صفحه وب بارگذاری کرد که تمام برنامه یکجا بارگذاری شود.
پروسهی بستهبندی کردن به این شکل است که فایل های import شده دنبال میشود و همهی آنها در یک فایل با نام “bundle” ادغام میشوند. این فایل bundle میتواند در صفحهی وب بارگذاری شود تا کل برنامه را به یکباره اجرا کند.
مثال
App:
// app.js
import { add } from './math.js';
console.log(add(16, 26)); // 42
// math.js
export function add(a, b) {
return a + b;
}
Bundle:
function add(a, b) {
return a + b;
}
console.log(add(16, 26)); // 42
نکته:
بستههای شما در نهایت با این نمونه تفاوت زیادی خواهد داشت.
اگر شما از Gatsby ،Next.js ،Create React App یا ابزار های مشابه استفاده میکنید، بهطور پیشفرض Webpack تنظیم شدهاست تا برنامهی شما را بستهبندی کند.
در غیر این صورت، لازم است که پروسهی بستهبندی را خودتان تنظیم و راهاندازی کنید. برای نمونه راهنماییهای نصب و شروع به کار را از مستندات Webpack مشاهده کنید.
تکهتکه کردن کد
بستهبندی بسیار خوب است، اما همواره با رشد و بزرگ شدن برنامهتان، فایل bundle شما نیز بزرگ میشود. به ویژه اگر شما از کتابخانههای جانبی استفاده کنید. لازم است که همیشه چشمتان به کدهایی باشد که به فایل بسته اضافه میکنید. اگر به صورت تصادفی حجم فایل بسته زیاد شود، درنتیجه زمان بارگذاری اولیه برنامه شما طولانی میشود.
برای جلوگیری از درگیر شدن با مشکلات بستهی بزرگ، بهتر است که قبل از ایجاد مشکل شروع به “تکهتکه” کردن فایل بستهتان کنید. تکهتکه کردن کد امکانی است که توسط کتابخانههای بستهبندی کننده مثل Webpack و Browserify (توسط factor-bundle) پشتیبانی میشود به اینصورت که میتوانند چندین فایل bundle ایجاد کنند که هنگام اجرای برنامه بصورت پویا بارگذاری شوند.
تکهتکه کردن برنامهتان به شما کمک میکند که تنها چیزهایی که در حال حاضر کاربر نیاز دارد را به روش “lazy-load” بارگیری کنید که به صورت جشمگیری کارایی برنامه ی شما را افزایش میدهد. با وجود اینکه شما مقدار کلی کد برنامه خود را کاهش ندادهاید، حجم کد موردنیاز برای بارگذاری اولیه کاهش پیدا میکند. زیرا از بارگیری کدی که ممکن است کاربر اصلا نیازی به آن نداشته باشد، جلوگیری کردهاید.
import()
بهترین راه برای شروع استفاده از تکهتکه کردن کد در برنامهتان استفاده از سینتکس import()
پویا است.
قبل:
import { add } from './math';
console.log(add(16, 26));
بعد:
import("./math").then(math => {
console.log(math.add(16, 26));
});
هنگامی که Webpack با این سینکتس برخورد می کند، بصورت خودکار شروع به تکهتکه کردن کد برنامه ی شما میکند. اگر شما از Create React App استفاده میکنید، این در حال حاضر برای شما تنظیم شدهاست و شما میتوانید همین حالا از اینجا شروع به استفاده از آن کنید. همچنین بهصورت پیشفرض توسط Next.js نیز پشتیبانی میشود.
اگر شما Webpack را خودتان تنظیم و راهاندازی کردهاید، ممکن است بخواهید راهنمای تکهتکه کردن کد Webpack را بخوانید. پیکربندی Webpack شما احتمالا چیزی شبیه به این باشد.
هنگام استفاده از Babel، شما باید اطمینان حاصل کنید که Babel میتواند سینتکس import پویا را parse کند ولی آن را تغییر ندهد. در این راستا شما به babel-plugin-syntax-dynamic-import نیاز خواهید داشت.
React.lazy
نکته:
React.lazy
و Suspense هنوز برای رندر کردن در سمت سرور در دسترس نیست. اگر شما می خواهید کد تان را در برنامهای که سمت سرور رندر میشود تکه تکه کنید، ما به شما Loadable Components را پیشنهاد میکنیم که راهنمای خوبی برای تکهتکه کردن bundle در رندر سمت سرور دارد.
تابع React.lazy
به شما اجازه میدهد که یک import پویا را به عنوان یک component معمولی رندر کنید.
قبل:
import OtherComponent from './OtherComponent';
بعد:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
هنگامی که کامپوننت فوق رندر می شود، بصورت خودکار فایل bundle ای که حاوی OtherComponent
است را بارگیری میکند.
React.lazy
یک تابع به عنوان ورودی میگیرد که یک import() پویا را صدا میکند. که یک promise برمیگرداند که resolves آن یک ماژول با خروجی default ای است که حاوی یک کامپوننت ریاکت است.
کامپوننت lazy باید درون یک کامپوننت Suspense
رندر شود، که به ما امکان نمایش یک مجتوای جایگزین (fallback) هنگامی که کامپوننت در حال بارگذاری است، میدهد.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
prop fallback
هر المان ریاکتی که شما میخواهید در بازهی صبر کردن تا بارگیری کامپوننت اصلی رندر کنید را به عنوان ورودی میپذیرد. شما میتوانید هر جایی کامپوننت Suspense
را در بالای کامپوننت lazy قرار دهید. حتی شما میتوانید چندین کامپوننت lazy را داخل یک کامپوننت Suspense
قرار دهید.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
مرزهای خطا (Error Boundaries)
اگر یک ماژول در هنگام بارگیری با مشکل مواجه شود (برای مثال، به خاطر مشکلات شبکه)، خطا می دهد. شما می توانید خطاهای اینچنینی را مدیریت کنید که تجربه کاربری خوبی را نشان داده و بازیابی را با مرز های خطا مدیریت کنید. هنگامی که مرز خطایتان را ساختید، شما میتوانید از آن در هر جایی در بالای کامپوننت lazy تان برای نمایش حالت خطا هنگامی که مشکلی در شبکه وجود دارد، استفاده کنید.
import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);
تکهتکه کردن کد بر اساس مسیرها
تشخیص اینکه در کدام سطح از برنامهتان از تکهتکه کردن کد استفاده کنید میتواند کمی مشکل باشد. شما میخواهید اطمینان حاصل کنید که جاهایی را انتخاب کنید که bundle ها را بصورت مساوی تقسیم کنید، اما تجربهی کاربر را مختل نکنید.
مسیرها نقطه شروع خوبی هستند. بیشتر افراد در محیط وب به زمانبر بودن انتقال از صفحهای به صفحه دیگر عادت کردهاند. شما هم معمولا تمام صفحه را یکباره مجدد رندر میکنید، بنابراین دور از ذهن بهنظر میرسد که کاربران شما با دیگر المانهای صفحه در حال کار کردن باشند.
اینجا یک مثال از چگونگی راهاندازی تکهتکه کردن کد برپایهی مسیر (route-based code splitting) در داخل برنامهتان با استفاده از کتابخانههایی مثل React Router با React.lazy
را مشاهده میکنید.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
export های نامگذاری شده
در حال حاضر React.lazy
صرفا export های پیشفرض را پشتیبانی میکند، اگر ماژولی که شما میخواهید import کنید از export نامگذاری شده استفاده میکند، شما میتوانید یک ماژول میانجی ایجاد کنید که آن را بهصورت پیشفرض export میکند. این تضمین میکند که ساختار درختی برنامه همچنان بهصورت صحیح کار میکند و شما کامپوننت بدون استفادهای را درخواست نکردهاید.
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));