Nx ile Next.js Uygulamalarını Optimize Etme

Yayınlanan: 2022-03-10
Hızlı özet ↬ Nx, optimizasyonu, uygulamaların verimli bir şekilde ölçeklenmesini ve paylaşılan kitaplıklar ve bileşenler gibi diğer özellikleri kolaylaştıran bir yapı çerçevesidir. Bu yazıda, Nx kullanarak Next.js uygulamalarını nasıl etkili bir şekilde ölçeklendirebileceğimize bakacağız.

Bu 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.
Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

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:

Yeni bir uygulama için Nx tarafından oluşturulan ana sayfa
Yeni bir uygulama için Nx varsayılan sayfası. (Büyük önizleme)

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.

Sunucudaki varsayılan yapılacaklar listesiyle uygulamanın yapılacaklar sayfası.
Uygulamanın yapılacaklar sayfası. (Büyük önizleme)

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.

Çalışma alanındaki uygulamalar arasındaki ilişkiyi gösteren bağımlılık grafiği
Uygulama dep-grafiği. (Büyük önizleme)

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.