Nx ile Next.js Uygulamalarını Optimize Etme
Yayınlanan: 2022-03-10Bu yazıda, Nx ve zengin özelliklerini kullanarak yüksek performanslı bir Next.js uygulamasının nasıl optimize edilip oluşturulacağını inceleyeceğiz. Nx sunucusu nasıl kurulur, mevcut bir sunucuya nasıl eklenti eklenir ve pratik bir görselleştirme ile monorepo kavramından bahsedeceğiz.
Uygulamaları optimize etmek ve uygulamalar arasında etkin bir şekilde yeniden kullanılabilir bileşenler oluşturmak isteyen bir geliştiriciyseniz, bu makale size uygulamalarınızı nasıl hızlı bir şekilde ölçeklendireceğinizi ve Nx ile nasıl çalışacağınızı gösterecektir. Devam etmek için Next.js çerçevesi ve TypeScript hakkında temel bilgilere ihtiyacınız olacak.
Nx Nedir?
Nx, güçlü bir komut satırı arabirimi (CLI), önbelleğe alma ve bağımlılık yönetimi sağlarken modern teknolojiler ve kitaplıklarla sorunsuz bir şekilde bütünleşerek her ölçekte mimari oluşturmanıza, test etmenize ve derlemenize yardımcı olan açık kaynaklı bir derleme çerçevesidir. Nx, geliştiricilere modern çerçeveler, testler ve araçlar için gelişmiş CLI araçları ve eklentileri sunar.
Bu makale için Nx'in Next.js uygulamalarıyla nasıl çalıştığına odaklanacağız. Nx, Cypress, Storybook ve stilli bileşenler gibi Next.js uygulamalarınızda test etme ve stil oluşturma için standart araçlar sağlar. Nx, uygulamalarınız için bir monorepo'yu kolaylaştırır, birden fazla uygulamanın kaynak kodunu ve kitaplıklarını tutabilen bir çalışma alanı oluşturarak uygulamalar arasında kaynakları paylaşmanıza olanak tanır.
Nx'i Neden Kullanmalı?
Nx, uygulamanızın uçtan uca (E2E) testi için standartlar, bir stil kitaplığı ve bir monorepo da dahil olmak üzere geliştiricilere kutudan çıktığı haliyle makul miktarda işlevsellik sağlar.
Nx kullanmanın birçok avantajı vardır ve bu bölümde bunlardan birkaçını inceleyeceğiz.
- Grafik tabanlı görev yürütme
Nx, görevleri hızlandırmak için dağıtılmış grafik tabanlı görev yürütme ve hesaplama önbelleğini kullanır. Sistem, her bir görevi hangi düğümün (yani uygulamanın) yürütmesi gerektiğini belirlemek için bir grafik sistemi kullanarak görevleri ve komutları zamanlayacaktır. Bu, uygulamaların yürütülmesini yönetir ve yürütme süresini verimli bir şekilde optimize eder. - Test yapmak
Nx, birim testi ve E2E testleri için önceden yapılandırılmış test araçları sağlar. - Önbelleğe almak
Nx ayrıca önbelleğe alınmış proje grafiğini de saklar. Bu, yalnızca güncellenen dosyaları yeniden analiz etmesini sağlar. Nx, son işlemden bu yana değişen dosyaların kaydını tutar ve yalnızca bu dosyalar üzerinde test etmenize, oluşturmanıza ve eylemler gerçekleştirmenize olanak tanır; bu, büyük bir kod tabanıyla çalışırken uygun optimizasyona izin verir. - Bağımlılık grafiği
Görsel bağımlılık grafiği, bileşenlerin birbirleriyle nasıl etkileşime girdiğini incelemenizi sağlar. - Bulut depolama
Nx ayrıca, proje günlüklerini gözden geçirmek için ekip üyeleriyle bağlantıları paylaşabilmeniz için bulut depolama ve GitHub entegrasyonu sağlar. - Kod paylaşımı
Her proje için yeni bir paylaşılan kitaplık oluşturmak oldukça zahmetli olabilir. Nx, bu komplikasyonu ortadan kaldırarak uygulamanızın temel işlevlerine odaklanmanızı sağlar. Nx ile, uygulamalar arasında kitaplıkları ve bileşenleri paylaşabilirsiniz. Hatta ön uç ve arka uç uygulamalarınız arasında yeniden kullanılabilir kodu paylaşabilirsiniz. - Monorepo desteği
Nx, birden çok uygulama için bir çalışma alanı sağlar. Bu kurulumla, bir GitHub deposu, çalışma alanınızın altındaki çeşitli uygulamalar için kod kaynağını barındırabilir.
Yayınlanabilir Kitaplıklar için Nx
Nx, yayınlanabilir kitaplıklar oluşturmanıza olanak tanır. Monorepo dışında kullanacağınız kitaplıklarınız varsa bu çok önemlidir. Nx Storybook entegrasyonu ile kurumsal UI bileşenleri geliştirdiğiniz herhangi bir durumda, Nx, hikayelerinizle birlikte yayınlanabilir bileşenler oluşturacaktır. Yayınlanabilir bileşenler, harici bir kayıt defterine dağıtabileceğiniz bir kitaplık paketi oluşturmak için bu bileşenleri derleyebilir. Yalnızca monorepoda kullanılan kitaplıkları oluşturmak için kullanılan --buildable
aksine, kitaplığı oluştururken --publishable
seçeneğini kullanırsınız. Nx, yayınlanabilir kitaplıkları otomatik olarak dağıtmaz; derlemeyi, nx build mylib
(burada mylib
, kitaplığın adıdır) gibi bir komut aracılığıyla çağırabilirsiniz; bu, daha sonra dist
/ mylib
klasöründe harici bir kayıt defterine dağıtılabilen optimize edilmiş bir paket oluşturur.
Nx, Next.js'yi hazır ayar olarak kullanarak yeni bir çalışma alanı oluşturma veya mevcut bir çalışma alanına Next.js ekleme seçeneği sunar.
Ön ayar olarak Next.js ile yeni bir çalışma alanı oluşturmak için aşağıdaki komutu kullanabilirsiniz:
npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo
Bu komut, "todo" adlı bir Next.js uygulaması ve stil kitaplığı olarak stil styled-components
içeren yeni bir Nx çalışma alanı yaratacaktır.
Ardından, Next.js uygulamasını mevcut bir Nx çalışma alanına aşağıdaki komutla ekleyebiliriz:
npx nx g @nrwl/next:app
Next.js ve Nx Uygulaması Oluşturma
Next.js için Nx eklentisi, bir Next.js uygulamasını çalıştırmak ve optimize etmek için araçlar ve yürütücüler içerir. Başlamak için, ön ayar olarak next
ile yeni bir Nx çalışma alanı oluşturmamız gerekiyor:
npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo
Yukarıdaki kod bloğu, yeni bir Nx çalışma alanı ve Next.js uygulaması oluşturacaktır. Nx Cloud'u kullanmak için bir istem alacağız. Bu eğitim için “Hayır”ı seçeceğiz ve ardından bağımlılıklarımızın yüklenmesini bekleyeceğiz. Bu yapıldıktan sonra, buna benzer bir dosya ağacımız olmalıdır:
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
klasöründe, yapılacaklar uygulaması için önceden yapılandırılmış E2E testiyle Next.js uygulamamız “yapılacaklar” olacak. Tüm bunlar, güçlü Nx CLI aracıyla otomatik olarak oluşturulur.
Uygulamamızı çalıştırmak için npx nx serve todo
komutunu kullanın. Uygulamayı sunmayı bitirdikten sonra aşağıdaki ekranı görmelisiniz:
API'yi Oluşturma
Bu noktada çalışma alanını kurduk. Sırada Next.js uygulamasında kullanacağımız CRUD API'sini oluşturmak var. Bunu yapmak için Express kullanacağız; monorepo desteğini göstermek için sunucumuzu çalışma alanında bir uygulama olarak oluşturacağız. İlk olarak, şu komutu çalıştırarak Nx için Express eklentisini kurmamız gerekiyor:
npm install --save-dev @nrwl/express
Bunu yaptıktan sonra, sağlanan çalışma alanında Express uygulamamızı kurmaya hazırız. Bir Express uygulaması oluşturmak için aşağıdaki komutu çalıştırın:
npx nx g @nrwl/express:application --name=todo-api --frontendProject=todo
nx g @nrwl/express:application
komutu, ek belirtim parametrelerini iletebileceğimiz bir Express uygulaması oluşturacaktır; uygulamanın adını belirtmek için --name
bayrağını kullanın; Express uygulamasını kullanacak olan ön uç uygulamayı belirtmek için, çalışma alanımızdaki bir uygulamanın adını --frontendProject
. Bir Ekspres uygulaması için birkaç başka seçenek daha mevcuttur. Bu yapıldığında, todo-api
klasörünün eklendiği apps
klasöründe güncellenmiş bir dosya yapısına sahip olacağız.
happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-api ┃ ┣ todo-e2e ┃ ┗ .gitkeep …
Todo todo-api
klasörü, main.ts
giriş dosyasına sahip bir Express ortak levhasıdır.
/** * 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);
Rotalarımızı bu uygulama içinde oluşturacağız. Başlamak için, uygulama bildiriminin hemen altında item
ve id
olmak üzere iki anahtar/değer çiftiyle bir dizi nesne başlatacağız.
/** * 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() }, ]; …
Sırada, app.get()
altındaki tüm yapılacaklar listelerini almak için rotayı ayarlayacağız:
… app.get('/api', (req, res) => { res.status(200).json({ data: todoArray, }); }); …
Yukarıdaki kod bloğu, todoArray
geçerli değerini döndürür. Ardından, diziden yapılacaklar öğelerini oluşturmak, güncellemek ve kaldırmak için rotalarımız olacak.
… 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', }); }); …
Yeni bir yapılacaklar öğesi oluşturmak için tek ihtiyacımız olan, yeni öğenin bir dize olarak değeridir. Sunucudaki dizideki son öğenin kimliğini artırarak bir kimlik oluşturacağız. Mevcut bir öğeyi güncellemek için, öğenin yeni değerini ve güncellenecek öğe nesnesinin kimliğini aktarırız; sunucuda, her öğeyi forEach
yöntemiyle döngüye alır ve kimliğin istekle gönderilen kimlikle eşleştiği yerde öğeyi güncellerdik. Son olarak, diziden bir öğeyi kaldırmak için, istekle birlikte kaldırılacak öğenin kimliğini göndeririz; daha sonra, diziyi filtreleriz ve istekle gönderilen kimlikle eşleşmeyen tüm öğelerin yeni bir dizisini döndürerek yeni diziyi todoArray
değişkenine atarız.
Not: Next.js uygulama klasörüne bakarsanız, aşağıdaki konfigürasyona sahip bir proxy.conf.json
dosyası görmelisiniz:
{ "/api": { "target": "http://localhost:3333", "secure": false } }
Bu, /api
ile eşleşen rotalara yapılan tüm API çağrılarının todo-api
sunucusunu hedeflemesine izin veren bir proxy oluşturur.
Nx ile Next.js Sayfaları Oluşturma
Next.js uygulamamızda yeni bir sayfa, home
sayfa ve bir öğe bileşeni oluşturacağız. Nx, kolayca bir sayfa oluşturmamız için bir CLI aracı sağlar:
npx nx g @nrwl/next:page home
Bu komutu çalıştırdıktan sonra, sayfa için kullanmak istediğimiz stil kitaplığını seçmemiz için bir istem alacağız; Bu makale için styled-components
seçeceğiz. işte! Sayfamız oluşturuldu. Bir bileşen oluşturmak için npx nx g @nrwl/next:component todo-item
çalıştırın; bu, todo-item
bileşeniyle bir component
klasörü oluşturacaktır.
Next.js Uygulamasında API Tüketimi
Her bir yapılacaklar öğesinde, yapılacaklar öğesini düzenlemek ve silmek için iki düğmemiz olacak. Bu eylemleri gerçekleştiren eşzamansız işlevler, ana sayfadan sahne olarak iletilir.
… 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> ); }
Güncelleme işlevi için isEditingItem
durumu false
olduğunda devre dışı bırakılan bir girdimiz var. "Düzenle" düğmesi tıklandığında, isEditingItem
durumunu true
olarak değiştirir ve "Güncelle" düğmesini görüntüler. Burada giriş bileşeni etkinleştirilir ve kullanıcı yeni bir değer girebilir; “Güncelle” düğmesine tıklandığında, girilen parametrelerle updateItem
işlevini çağırır ve isEditingItem
false
olarak değiştirir.
home
sayfa bileşeninde, CRUD işlemini gerçekleştiren asenkron fonksiyonlara sahibiz.
… 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(); }, []); …
Yukarıdaki kod bloğunda, sunucudan fetchItems
döndüren todoArray
var. Ardından, bir dize alan createItem
işlevine sahibiz; parametre, yeni yapılacak iş öğesinin değeridir. updateItem
işlevi, güncellenecek öğenin kimliği ve updatedItem
değeri olmak üzere iki parametre alır. Ve deleteItem
işlevi, geçirilen kimlikle eşleşen öğeyi kaldırır.
Yapılacaklar öğesini oluşturmak için, items
durumunu eşleriz:
… 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> ); …
Sunucumuz ve ön uç şimdi kuruldu. API uygulamasını npx nx serve todo-api
çalıştırarak sunabiliriz ve Next.js uygulaması için npx nx serve todo
çalıştırırız. “Devam” düğmesini tıkladığınızda, varsayılan yapılacaklar öğesinin görüntülendiği bir sayfa göreceksiniz.
Artık tek bir çalışma alanında birlikte çalışan çalışan bir Next.js ve Express uygulamamız var.
Nx, uygulamamızın bağımlılık grafiğini terminal çalıştırmamızda görmemizi sağlayan başka bir CLI aracına sahiptir. npx nx dep-graph
çalıştırın, uygulamamızın bağımlılık grafiğini gösteren aşağıdaki görüntüye benzer bir ekran görmeliyiz.
Nx için Diğer CLI Komutları
-
nx list
Şu anda yüklü olan Nx eklentilerini listeler. -
nx migrate latest
package.json
içindeki paketleri en son sürüme günceller. -
nx affected
Eylemi yalnızca etkilenen veya değiştirilen uygulamalarda gerçekleştirir. -
nx run-many --target serve --projects todo-api,todo
Listelenen tüm projelerde hedef komutu çalıştırır.
Çözüm
Nx'e genel bir bakış olarak, bu makale Nx'in neler sunduğunu ve bizim için çalışmayı nasıl kolaylaştırdığını ele alıyor. Ayrıca bir Nx çalışma alanında bir Next.js uygulaması kurma, mevcut bir çalışma alanına bir Express eklentisi ekleme ve çalışma alanımızda birden fazla uygulamayı barındırmak için monorepo özelliğini kullanma aşamalarını da inceledik.
Kaynak kodunun tamamını GitHub deposunda bulacaksınız. Nx hakkında ek bilgi için, belgelere veya Next.js için Nx belgelerine bakın.