Nx یک چارچوب ساخت است که بهینه سازی، مقیاس بندی کارآمد برنامه ها و سایر ویژگی ها مانند کتابخانه ها و مؤلفه های مشترک را تسهیل می کند. در این مقاله، ما به این خواهیم پرداخت که چگونه میتوانیم به طور موثر برنامههای Next.js را با استفاده از Nx مقیاسبندی کنیم.
در این مقاله به نحوه بهینه سازی و ساخت اپلیکیشن Next.js با کارایی بالا با استفاده از آن می پردازیم Nx و ویژگی های غنی آن ما نحوه راه اندازی یک سرور Nx، نحوه افزودن یک پلاگین به سرور موجود و مفهوم monorepo را با تجسم عملی بررسی خواهیم کرد.
اگر توسعهدهندهای هستید که به دنبال بهینهسازی برنامهها و ایجاد مؤلفههای قابل استفاده مجدد در بین برنامهها به طور مؤثر هستید، این مقاله به شما نشان میدهد که چگونه به سرعت برنامههای خود را مقیاسبندی کنید، و چگونه با Nx کار کنید. برای ادامه، به دانش اولیه چارچوب Next.js و TypeScript نیاز دارید.
Nx چیست؟
Nx یک چارچوب ساخت منبع باز است که به شما کمک میکند تا در هر مقیاسی معمار، آزمایش و بسازید – به طور یکپارچه با فناوریها و کتابخانههای مدرن ادغام میشود، در حالی که یک رابط خط فرمان قوی (CLI)، حافظه پنهان و مدیریت وابستگی ارائه میدهد. Nx ابزارها و پلاگین های پیشرفته CLI را برای چارچوب ها، تست ها و ابزارهای مدرن به توسعه دهندگان ارائه می دهد.
برای این مقاله، ما بر نحوه کار Nx با برنامه های Next.js تمرکز خواهیم کرد. Nx ابزارهای استانداردی را برای آزمایش و استایل در برنامه های Next.js شما، مانند Cypress، Storybook و styled-components ارائه می دهد. Nx یک monorepo را برای برنامههای شما تسهیل میکند و فضای کاری ایجاد میکند که میتواند کد منبع و کتابخانههای چند برنامه کاربردی را در خود نگه دارد و به شما امکان میدهد منابع را بین برنامهها به اشتراک بگذارید.
چرا از Nx استفاده کنیم؟
Nx مقدار معقولی از عملکرد را در اختیار توسعه دهندگان قرار می دهد، از جمله صفحات دیگ بخار برای آزمایش سرتاسر (E2E) برنامه شما، یک کتابخانه استایل و یک monorepo.
استفاده از Nx مزایای زیادی دارد و ما در این بخش به چند مورد از آنها خواهیم پرداخت.
- اجرای کار مبتنی بر نمودار
Nx از اجرای کار مبتنی بر نمودار توزیع شده و ذخیره محاسباتی برای سرعت بخشیدن به وظایف استفاده می کند. سیستم وظایف و دستورات را با استفاده از یک سیستم گراف برنامه ریزی می کند تا تعیین کند که کدام گره (یعنی برنامه کاربردی) باید هر وظیفه را اجرا کند. این کار اجرای برنامه ها را مدیریت می کند و زمان اجرا را به طور موثر بهینه می کند. - آزمایش کردن
Nx ابزارهای آزمایشی از پیش تنظیم شده را برای تست واحد و تست های E2E فراهم می کند. - ذخیره سازی
Nx گراف پروژه کش شده را نیز ذخیره می کند. این به آن امکان می دهد فقط فایل های به روز شده را دوباره تجزیه و تحلیل کند. Nx فایلهای تغییر یافته از آخرین commit را ردیابی میکند و به شما امکان میدهد فقط آن فایلها را آزمایش، بسازید و اعمال کنید. این امکان بهینه سازی مناسب را در هنگام کار با یک پایه کد بزرگ فراهم می کند. - نمودار وابستگی
نمودار وابستگی بصری شما را قادر می سازد تا نحوه تعامل اجزا با یکدیگر را بررسی کنید. - فضای ذخیره ابری
Nx همچنین فضای ذخیره سازی ابری و ادغام GitHub را فراهم می کند، به طوری که می توانید پیوندها را با اعضای تیم برای بررسی گزارش های پروژه به اشتراک بگذارید. - به اشتراک گذاری کد
ایجاد یک کتابخانه مشترک جدید برای هر پروژه می تواند بسیار سخت باشد. Nx این عارضه را از بین می برد و شما را آزاد می کند تا روی عملکرد اصلی برنامه خود تمرکز کنید. با Nx میتوانید کتابخانهها و مؤلفهها را در بین برنامهها به اشتراک بگذارید. حتی می توانید کدهای قابل استفاده مجدد را بین برنامه های جلویی و بک اند خود به اشتراک بگذارید. - پشتیبانی از monorepos
Nx یک فضای کاری برای چندین برنامه فراهم می کند. با این تنظیمات، یک مخزن GitHub می تواند منبع کد برنامه های مختلف را در زیر فضای کاری شما قرار دهد.
Nx برای کتابخانه های قابل انتشار
Nx به شما امکان می دهد کتابخانه های قابل انتشار ایجاد کنید. این زمانی ضروری است که شما کتابخانه هایی دارید که خارج از monorepo از آنها استفاده خواهید کرد. در هر موردی که در حال توسعه مؤلفههای رابط کاربری سازمانی با ادغام Nx Storybook هستید، Nx مؤلفههای قابل انتشار را در کنار داستانهای شما ایجاد میکند. مؤلفه های قابل انتشار می توانند این مؤلفه ها را کامپایل کنند تا یک بسته کتابخانه ای ایجاد کنند که می توانید آن را در یک رجیستری خارجی مستقر کنید. شما از --publishable
گزینه هنگام تولید کتابخانه، بر خلاف --buildable
، که برای تولید کتابخانه هایی استفاده می شود که فقط در monorepo استفاده می شوند. Nx کتابخانه های قابل انتشار را به طور خودکار مستقر نمی کند. می توانید بیلد را از طریق دستوری مانند فراخوانی کنید nx build mylib
(جایی که mylib
نام کتابخانه است)، که سپس یک بسته نرم افزاری بهینه سازی شده در آن تولید می کند dist
/mylib
پوشه ای که می تواند در یک رجیستری خارجی مستقر شود.
Nx به شما این امکان را می دهد که یک فضای کاری جدید با Next.js به عنوان یک پیش تنظیم ایجاد کنید، یا Next.js را به یک فضای کاری موجود اضافه کنید.
برای ایجاد یک فضای کاری جدید با Next.js به عنوان یک پیش تنظیم، می توانید از دستور زیر استفاده کنید:
npx create-nx-workspace happynrwl
--preset=next
--style=styled-components
--appName=todo
این دستور یک فضای کاری Nx جدید با یک برنامه Next.js به نام “todo” و با ایجاد می کند styled-components
به عنوان کتابخانه یک ظاهر طراحی شده
سپس، میتوانیم برنامه Next.js را با دستور زیر به فضای کاری Nx موجود اضافه کنیم:
npx nx g @nrwl/next:app
ساختن یک برنامه Next.js و Nx
افزونه Nx برای Next.js شامل ابزارها و مجریان برای اجرا و بهینه سازی برنامه Next.js است. برای شروع، باید یک فضای کاری جدید با Nx ایجاد کنیم next
به عنوان پیش تنظیم:
npx create-nx-workspace happynrwl
--preset=next
--style=styled-components
--appName=todo
بلوک کد بالا یک فضای کاری جدید Nx و برنامه Next.js ایجاد می کند. ما یک اعلان برای استفاده از Nx Cloud دریافت خواهیم کرد. برای این آموزش، “نه” را انتخاب می کنیم و سپس منتظر می مانیم تا وابستگی های ما نصب شوند. پس از انجام این کار، باید یک درخت فایل مشابه این داشته باشیم:
📦happynrwl
┣ 📂apps
┃ ┣ 📂todo
┃ ┣ 📂todo-e2e
┃ ┗ 📜.gitkeep
┣ 📂libs
┣ 📂node_modules
┣ 📂tools
┣ 📜.editorconfig
┣ 📜.eslintrc.json
┣ 📜.gitignore
┣ 📜.prettierignore
┣ 📜.prettierrc
┣ 📜README.md
┣ 📜babel.config.json
┣ 📜jest.config.js
┣ 📜jest.preset.js
┣ 📜nx.json
┣ 📜package-lock.json
┣ 📜package.json
┣ 📜tsconfig.base.json
┗ 📜workspace.json
در 📂apps
پوشه، ما برنامه Next.js خود را “todo” خواهیم داشت، با تست E2E از پیش پیکربندی شده برای برنامه کارها. همه اینها به طور خودکار با ابزار قدرتمند Nx CLI تولید می شود.
برای اجرای برنامه ما، از npx nx serve todo
فرمان پس از اتمام سرویس برنامه، باید صفحه زیر را ببینید:
صفحه پیشفرض Nx برای یک برنامه جدید. (پیش نمایش بزرگ)
ساخت API
در این مرحله، ما فضای کاری را تنظیم کرده ایم. مرحله بعدی ساخت CRUD API است که در برنامه Next.js استفاده خواهیم کرد. برای انجام این کار، ما از Express استفاده خواهیم کرد. برای نشان دادن پشتیبانی monorepo، ما سرور خود را به عنوان یک برنامه کاربردی در فضای کاری می سازیم. ابتدا باید با اجرای این دستور افزونه Express را برای Nx نصب کنیم:
npm install --save-dev @nrwl/express
پس از انجام این کار، ما آماده هستیم تا برنامه Express خود را در فضای کاری ارائه شده راه اندازی کنیم. برای ایجاد یک برنامه Express، دستور زیر را اجرا کنید:
npx nx g @nrwl/express:application --name=todo-api --frontendProject=todo
فرمان nx g @nrwl/express:application
یک برنامه Express ایجاد می کند که می توانیم پارامترهای مشخصات اضافی را به آن منتقل کنیم. برای تعیین نام برنامه، از --name
پرچم؛ برای نشان دادن برنامه جلویی که از برنامه Express استفاده می کند، نام یک برنامه در فضای کاری ما را به آن ارسال کنید --frontendProject
. چند تا دیگه گزینه هایی برای یک برنامه Express در دسترس هستند. هنگامی که این کار انجام شد، ما یک ساختار فایل به روز شده در آن خواهیم داشت apps
پوشه با 📂todo-api
پوشه به آن اضافه شد
📦happynrwl
┣ 📂apps
┃ ┣ 📂todo
┃ ┣ 📂todo-api
┃ ┣ 📂todo-e2e
┃ ┗ 📜.gitkeep
…
را todo-api
پوشه یک دیگ بخار اکسپرس با یک main.ts
فایل ورودی
/**
* This is not a production server yet!
* This is only minimal back end to get started.
*/
import * as express from 'express';
import {v4 as uuidV4} from 'uuid';
const app = express();
app.use(express.json()); // used instead of body-parser
app.get('/api', (req, res) => {
res.send({ message: 'Welcome to todo-api!' });
});
const port = process.env.port || 3333;
const server = app.listen(port, () => {
console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);
ما مسیرهای خود را در داخل این برنامه ایجاد خواهیم کرد. برای شروع، آرایه ای از اشیاء را با دو جفت کلید-مقدار مقداردهی اولیه می کنیم. item
و id
، درست در زیر اعلان برنامه.
/**
* This is not a production server yet!
* This is only minimal back end to get started.
*/
import * as express from 'express';
import {v4 as uuidV4} from 'uuid';
const app = express();
app.use(express.json()); // used instead of body-parser
let todoArray: Array<{ item: string; id: string }> = [
{ item: 'default todo', id: uuidV4() },
];
…
در مرحله بعد، مسیری را برای واکشی لیست های همه کارها در زیر تنظیم می کنیم app.get()
:
…
app.get('/api', (req, res) => {
res.status(200).json({
data: todoArray,
});
});
…
بلوک کد بالا مقدار فعلی را برمی گرداند todoArray
. متعاقباً، مسیرهایی برای ایجاد، بهروزرسانی و حذف موارد انجام کار از آرایه خواهیم داشت.
…
app.post('/api', (req, res) => {
const item: string = req.body.item;
// Increment ID of item based on the ID of the last item in the array.
let id: string = uuidV4();
// Add the new object to the array
todoArray.push({ item, id });
res.status(200).json({
message: 'item added successfully',
});
});
app.patch('/api', (req, res) => {
// Value of the updated item
const updatedItem: string = req.body.updatedItem;
// ID of the position to update
const id: string = req.body.id;
// Find index of the ID
const arrayIndex = todoArray.findIndex((obj) => obj.id === id);
// Update item that matches the index
todoArray[arrayIndex].item = updatedItem
res.status(200).json({
message: 'item updated successfully',
});
});
app.delete('/api', (req, res) => {
// ID of the position to remove
const id: string = req.body.id;
// Update array and remove the object that matches the ID
todoArray = todoArray.filter((val) => val.id !== id);
res.status(200).json({
message: 'item removed successfully',
});
});
…
برای ایجاد یک مورد جدید، تنها چیزی که نیاز داریم مقدار مورد جدید به عنوان یک رشته است. با افزایش شناسه آخرین عنصر آرایه روی سرور، یک شناسه ایجاد می کنیم. برای بهروزرسانی یک آیتم موجود، مقدار جدید مورد و شناسه شی مورد را که قرار است بهروزرسانی شود، وارد میکنیم. در سرور، هر آیتم را با حلقه حلقه می کنیم forEach
روش، و مورد را در محلی که شناسه با شناسه ارسال شده با درخواست مطابقت دارد، به روز کنید. در نهایت، برای حذف یک آیتم از آرایه، شناسه مورد را ارسال می کنیم تا با درخواست حذف شود. سپس، از طریق آرایه فیلتر می کنیم، و یک آرایه جدید از همه موارد که با شناسه ارسال شده با درخواست مطابقت ندارند، برمی گردانیم و آرایه جدید را به todoArray
متغیر.
توجه داشته باشید: اگر به پوشه برنامه Next.js نگاه کنید، باید یک را ببینید proxy.conf.json
فایل با پیکربندی زیر:
{
"/api": {
"target": "http://localhost:3333",
"secure": false
}
}
این یک پروکسی ایجاد میکند و به همه تماسهای API اجازه میدهد تا مسیرها مطابقت داشته باشند /api
برای هدف قرار دادن todo-api
سرور
ایجاد صفحات Next.js با Nx
در برنامه Next.js ما یک صفحه جدید ایجاد خواهیم کرد، home
و یک جزء آیتم. Nx یک ابزار CLI را برای ما فراهم می کند تا به راحتی یک صفحه ایجاد کنیم:
npx nx g @nrwl/next:page home
پس از اجرای این دستور، درخواستی برای انتخاب کتابخانه استایلی که میخواهیم برای صفحه استفاده کنیم، دریافت میکنیم. برای این مقاله، ما انتخاب خواهیم کرد styled-components
. Voilà! صفحه ما ایجاد شد برای ایجاد کامپوننت، اجرا کنید npx nx g @nrwl/next:component todo-item
; این یک ایجاد خواهد کرد component
پوشه با todo-item
جزء.
مصرف API در برنامه Next.js
در هر مورد، دو دکمه خواهیم داشت، برای ویرایش و حذف مورد. توابع ناهمزمان که این اقدامات را انجام می دهند به عنوان ابزار از صفحه اصلی ارسال می شوند.
…
export interface TodoItemProps {
updateItem(id: string, updatedItem: string): Promise<void>;
deleteItem(id: string): Promise<void>;
fetchItems(): Promise<any>;
item: string;
id: string;
}
export const FlexWrapper = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
padding-bottom: 10px;
margin-top: 20px;
@media all and (max-width: 470px) {
flex-direction: column;
input {
width: 100%;
}
button {
width: 100%;
}
}
`;
export function TodoItem(props: TodoItemProps) {
const [isEditingItem, setIsEditingItem] = useState<boolean>(false);
const [item, setNewItem] = useState<string | null>(null);
return (
<FlexWrapper>
<Input
disabled={!isEditingItem}
defaultValue={props.item}
isEditing={isEditingItem}
onChange={({ target }) => setNewItem(target.value)}
/>
{!isEditingItem && <Button
onClick={() => setIsEditingItem(true)}
>
Edit
</Button>}
{isEditingItem && <Button onClick={async () => {
await props.updateItem(props.id, item);
//fetch updated items
await props.fetchItems();
setIsEditingItem(false)
}}>
Update
</Button>}
<Button
danger
onClick={async () => {
await props.deleteItem(props.id);
//fetch updated items
await await props.fetchItems();
}}
>
Delete
</Button>
</FlexWrapper>
);
}
برای عملکرد به روز رسانی، یک ورودی داریم که زمانی غیرفعال می شود isEditingItem
دولت است false
. پس از کلیک بر روی دکمه “ویرایش”، آن را تغییر می دهد isEditingItem
حالت به true
و دکمه “Update” را نمایش می دهد. در اینجا مؤلفه ورودی فعال می شود و کاربر می تواند مقدار جدیدی را وارد کند. هنگامی که دکمه “به روز رسانی” کلیک می شود، آن را فرا می خواند updateItem
تابع با پارامترهای ارسال شده، و تغییر می کند isEditingItem
بازگشت به false
.
در home
جزء صفحه، ما توابع ناهمزمان را داریم که عملیات CRUD را انجام می دهند.
…
const [items, setItems] = useState<Array<{ item: string; id: string }>>([]);
const [newItem, setNewItem] = useState<string>('');
const fetchItems = async () => {
try {
const data = await fetch('/api/fetch');
const res = await data.json();
setItems(res.data);
} catch (error) {
console.log(error);
}
};
const createItem = async (item: string) => {
try {
const data = await fetch('/api', {
method: 'POST',
body: JSON.stringify({ item }),
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.log(error);
}
};
const deleteItem = async (id: string) => {
try {
const data = await fetch('/api', {
method: 'DELETE',
body: JSON.stringify({ id }),
headers: {
'Content-Type': 'application/json',
},
});
const res = await data.json();
alert(res.message);
} catch (error) {
console.log(error);
}
};
const updateItem = async (id: string, updatedItem: string) => {
try {
const data = await fetch('/api', {
method: 'PATCH',
body: JSON.stringify({ id, updatedItem }),
headers: {
'Content-Type': 'application/json',
},
});
const res = await data.json();
alert(res.message);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
fetchItems();
}, []);
…
در بلوک کد بالا، ما داریم fetchItems
، که برمی گردد todoArray
از سرور سپس، ما باید createItem
تابع، که یک رشته می گیرد. پارامتر مقدار مورد جدید برای انجام است. را updateItem
تابع دو پارامتر می گیرد، شناسه موردی که باید به روز شود و updatedItem
ارزش. و deleteItem
تابع مورد منطبق با شناسه ارسال شده را حذف می کند.
برای رندر مورد کار، نقشه را از طریق items
دولت:
…
return (
<StyledHome>
<h1>Welcome to Home!</h1>
<TodoWrapper>
{items.length > 0 &&
items.map((val) => (
<TodoItem
key={val.id}
item={val.item}
id={val.id}
deleteItem={deleteItem}
updateItem={updateItem}
fetchItems={fetchItems}
/>
))}
</TodoWrapper>
<form
onSubmit={async(e) => {
e.preventDefault();
await createItem(newItem);
//Clean up new item
setNewItem('');
await fetchItems();
}}
>
<FlexWrapper>
<Input
value={newItem}
onChange={({ target }) => setNewItem(target.value)}
placeholder="Add new item…"
/>
<Button success type="submit">
Add +
</Button>
</FlexWrapper>
</form>
</StyledHome>
);
…
سرور و فرانت اند ما اکنون راه اندازی شده اند. ما می توانیم برنامه API را با اجرا ارائه دهیم npx nx serve todo-api
و برای برنامه Next.js اجرا می کنیم npx nx serve todo
. روی دکمه «ادامه» کلیک کنید، صفحهای را مشاهده میکنید که مورد پیشفرض باید نمایش داده شود.
صفحه موارد انجام کار برنامه. (پیش نمایش بزرگ)
ما اکنون یک برنامه Next.js و Express داریم که با هم در یک فضای کاری کار می کنند.
Nx ابزار CLI دیگری دارد که به ما امکان می دهد نمودار وابستگی برنامه خود را در اجرای ترمینال خود مشاهده کنیم. اجرا کن npx nx dep-graph
، و باید صفحه ای مشابه تصویر زیر ببینیم که نمودار وابستگی برنامه ما را نشان می دهد.
نمودار عمق برنامه (پیش نمایش بزرگ)
سایر دستورات CLI برای Nx
nx list
افزونههای Nx نصبشده را فهرست میکند.nx migrate latest
بسته های موجود را به روز می کندpackage.json
به آخرین نسخهnx affected
این عمل را فقط روی برنامههای تحت تأثیر یا اصلاح شده انجام میدهد.nx run-many --target serve --projects todo-api,todo
دستور target را در تمام پروژه های فهرست شده اجرا می کند.
نتیجه
به عنوان یک نمای کلی از Nx، این مقاله به آنچه Nx ارائه می دهد و چگونه کار را برای ما آسان می کند، پرداخته است. ما همچنین راهاندازی یک برنامه Next.js را در فضای کاری Nx، اضافه کردن یک افزونه Express به یک فضای کاری موجود و استفاده از ویژگی monorepo برای قرار دادن بیش از یک برنامه در فضای کاری خود انجام دادیم.
کد منبع کامل را در قسمت پیدا خواهید کرد مخزن GitHub. برای اطلاعات بیشتر در مورد Nx، به ادامه مطلب مراجعه کنید مستندات یا Nx مستندات برای Next.js.
