Mengoptimalkan Aplikasi Next.js Dengan Nx

Diterbitkan: 2022-03-10
Ringkasan cepat Nx adalah kerangka kerja pembangunan yang memfasilitasi pengoptimalan, penskalaan aplikasi yang efisien, dan fitur lain seperti pustaka dan komponen bersama. Pada artikel ini, kita akan melihat bagaimana kita dapat secara efektif menskalakan aplikasi Next.js dengan menggunakan Nx.

Pada artikel ini, kita akan membahas cara mengoptimalkan dan membangun aplikasi Next.js berperforma tinggi menggunakan Nx dan fitur-fiturnya yang kaya. Kami akan membahas cara mengatur server Nx, cara menambahkan plugin ke server yang ada, dan konsep monorepo dengan visualisasi praktis.

Jika Anda seorang pengembang yang ingin mengoptimalkan aplikasi dan membuat komponen yang dapat digunakan kembali di seluruh aplikasi secara efektif, artikel ini akan menunjukkan cara menskalakan aplikasi dengan cepat, dan cara bekerja dengan Nx. Untuk mengikuti, Anda memerlukan pengetahuan dasar tentang kerangka kerja Next.js dan TypeScript.

Apa Itu Nx?

Nx adalah kerangka kerja pembangunan sumber terbuka yang membantu Anda merancang, menguji, dan membangun pada skala apa pun — berintegrasi mulus dengan teknologi dan pustaka modern, sambil menyediakan antarmuka baris perintah (CLI), caching, dan manajemen ketergantungan yang kuat. Nx menawarkan kepada pengembang alat dan plugin CLI canggih untuk kerangka kerja, pengujian, dan alat modern.

Untuk artikel ini, kita akan fokus pada bagaimana Nx bekerja dengan aplikasi Next.js. Nx menyediakan alat standar untuk pengujian dan penataan gaya di aplikasi Next.js Anda, seperti Cypress, Storybook, dan komponen gaya. Nx memfasilitasi monorepo untuk aplikasi Anda, menciptakan ruang kerja yang dapat menampung kode sumber dan pustaka beberapa aplikasi, memungkinkan Anda untuk berbagi sumber daya antar aplikasi.

Mengapa Menggunakan Nx?

Nx memberi pengembang sejumlah fungsionalitas yang wajar di luar kotak, termasuk boilerplates untuk pengujian aplikasi Anda dari ujung ke ujung (E2E), perpustakaan gaya, dan monorepo.

Banyak keuntungan datang dengan menggunakan Nx, dan kami akan membahas beberapa di antaranya di bagian ini.

  • Eksekusi tugas berbasis grafik
    Nx menggunakan eksekusi tugas berbasis grafik terdistribusi dan caching komputasi untuk mempercepat tugas. Sistem akan menjadwalkan tugas dan perintah menggunakan sistem grafik untuk menentukan node mana (yaitu aplikasi) yang harus menjalankan setiap tugas. Ini menangani eksekusi aplikasi dan mengoptimalkan waktu eksekusi secara efisien.
  • Pengujian
    Nx menyediakan alat uji prakonfigurasi untuk pengujian unit dan pengujian E2E.
  • Cache
    Nx juga menyimpan grafik proyek yang di-cache. Ini memungkinkannya untuk menganalisis ulang hanya file yang diperbarui. Nx melacak file yang diubah sejak komit terakhir dan memungkinkan Anda menguji, membangun, dan melakukan tindakan hanya pada file tersebut; ini memungkinkan pengoptimalan yang tepat saat Anda bekerja dengan basis kode yang besar.
  • Grafik ketergantungan
    Grafik ketergantungan visual memungkinkan Anda untuk memeriksa bagaimana komponen berinteraksi satu sama lain.
  • Penyimpanan awan
    Nx juga menyediakan penyimpanan cloud dan integrasi GitHub, sehingga Anda dapat berbagi tautan dengan anggota tim untuk meninjau log proyek.
  • Berbagi kode
    Membuat perpustakaan bersama baru untuk setiap proyek bisa sangat melelahkan. Nx menghilangkan kerumitan ini, membebaskan Anda untuk fokus pada fungsionalitas inti aplikasi Anda. Dengan Nx, Anda dapat berbagi pustaka dan komponen di seluruh aplikasi. Anda bahkan dapat berbagi kode yang dapat digunakan kembali antara aplikasi front-end dan back-end Anda.
  • Dukungan untuk monorepos
    Nx menyediakan satu ruang kerja untuk beberapa aplikasi. Dengan pengaturan ini, satu repositori GitHub dapat menampung sumber kode untuk berbagai aplikasi di bawah ruang kerja Anda.
Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Nx untuk Perpustakaan yang Dapat Diterbitkan

Nx memungkinkan Anda membuat perpustakaan yang dapat diterbitkan. Ini penting ketika Anda memiliki perpustakaan yang akan Anda gunakan di luar monorepo. Dalam setiap contoh di mana Anda mengembangkan komponen UI organisasi dengan integrasi Buku Cerita Nx, Nx akan membuat komponen yang dapat diterbitkan bersama cerita Anda. Komponen yang dapat diterbitkan dapat mengompilasi komponen ini untuk membuat bundel pustaka yang dapat Anda terapkan ke registri eksternal. Anda akan menggunakan opsi --publishable saat membuat perpustakaan, tidak seperti --buildable , yang digunakan untuk menghasilkan perpustakaan yang hanya digunakan di monorepo. Nx tidak menyebarkan pustaka yang dapat diterbitkan secara otomatis; Anda dapat menjalankan build melalui perintah seperti nx build mylib (di mana mylib adalah nama perpustakaan), yang kemudian akan menghasilkan bundel yang dioptimalkan di folder dist / mylib yang dapat digunakan ke registri eksternal.

Nx memberi Anda opsi untuk membuat ruang kerja baru dengan Next.js sebagai preset, atau menambahkan Next.js ke ruang kerja yang ada.

Untuk membuat ruang kerja baru dengan Next.js sebagai preset, Anda dapat menggunakan perintah berikut:

 npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo

Perintah ini akan membuat ruang kerja Nx baru dengan aplikasi Next.js bernama “todo” dan dengan styled-components gaya sebagai pustaka gaya.

Kemudian, kita dapat menambahkan aplikasi Next.js ke ruang kerja Nx yang ada dengan perintah berikut:

 npx nx g @nrwl/next:app

Membangun Aplikasi Next.js dan Nx

Plugin Nx untuk Next.js menyertakan alat dan eksekutor untuk menjalankan dan mengoptimalkan aplikasi Next.js. Untuk memulai, kita perlu membuat ruang kerja Nx baru dengan next sebagai preset:

 npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo

Blok kode di atas akan menghasilkan ruang kerja Nx baru dan aplikasi Next.js. Kami akan mendapatkan prompt untuk menggunakan Nx Cloud. Untuk tutorial ini, kita akan memilih “No”, dan kemudian menunggu dependensi kita terinstal. Setelah selesai, kita harus memiliki pohon file yang mirip dengan ini:

 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

Di folder apps , kita akan memiliki aplikasi Next.js “todo”, dengan tes E2E yang telah dikonfigurasi untuk aplikasi to-do. Semua ini dibuat secara otomatis dengan alat Nx CLI yang kuat.

Untuk menjalankan aplikasi kita, gunakan perintah npx nx serve todo . Setelah Anda selesai menyajikan aplikasi, Anda akan melihat layar di bawah ini:

Halaman beranda yang dihasilkan Nx untuk aplikasi baru
Halaman default Nx untuk aplikasi baru. (Pratinjau besar)

Membangun API

Pada titik ini, kami telah menyiapkan ruang kerja. Selanjutnya adalah membangun CRUD API yang akan kita gunakan pada aplikasi Next.js. Untuk melakukan ini, kita akan menggunakan Express; untuk mendemonstrasikan dukungan monorepo, kami akan membangun server kami sebagai aplikasi di ruang kerja. Pertama, kita harus menginstal plugin Express untuk Nx dengan menjalankan perintah ini:

 npm install --save-dev @nrwl/express

Setelah selesai, kami siap untuk mengatur aplikasi Express kami di ruang kerja yang disediakan. Untuk membuat aplikasi Express, jalankan perintah di bawah ini:

 npx nx g @nrwl/express:application --name=todo-api --frontendProject=todo

Perintah nx g @nrwl/express:application akan menghasilkan aplikasi Express yang dapat kita lewati parameter spesifikasi tambahan; untuk menentukan nama aplikasi, gunakan flag --name ; untuk menunjukkan aplikasi front-end yang akan menggunakan aplikasi Express, teruskan nama aplikasi di ruang kerja kami ke --frontendProject . Beberapa opsi lain tersedia untuk aplikasi Express. Ketika ini selesai, kami akan memiliki struktur file yang diperbarui di folder apps dengan folder todo-api ditambahkan ke dalamnya.

 happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-api ┃ ┣ todo-e2e ┃ ┗ .gitkeep …

Folder todo-api adalah boilerplate Express dengan file entri 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);

Kami akan membuat rute kami di dalam aplikasi ini. Untuk memulai, kita akan menginisialisasi array objek dengan dua pasangan nilai kunci, item dan id , tepat di bawah deklarasi aplikasi.

 /** * 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() }, ]; …

Selanjutnya, kita akan menyiapkan rute untuk mengambil semua daftar tugas di bawah app.get() :

 … app.get('/api', (req, res) => { res.status(200).json({ data: todoArray, }); }); …

Blok kode di atas akan mengembalikan nilai todoArray saat ini. Selanjutnya, kami akan memiliki rute untuk membuat, memperbarui, dan menghapus item yang harus dilakukan dari array.

 … 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', }); }); …

Untuk membuat item to-do baru, yang kita butuhkan hanyalah nilai item baru sebagai string. Kami akan menghasilkan ID dengan menambahkan ID elemen terakhir dalam array di server. Untuk memperbarui item yang ada, kami akan memberikan nilai baru untuk item dan ID objek item yang akan diperbarui; di server, kami akan mengulang setiap item dengan metode forEach , dan memperbarui item di tempat ID cocok dengan ID yang dikirim dengan permintaan. Terakhir, untuk menghapus item dari larik, kami akan mengirimkan ID item untuk dihapus dengan permintaan; kemudian, kami memfilter melalui larik, dan mengembalikan larik baru dari semua item yang tidak cocok dengan ID yang dikirim dengan permintaan, menetapkan larik baru ke variabel todoArray .

Catatan: Jika Anda melihat di folder aplikasi Next.js, Anda akan melihat file proxy.conf.json dengan konfigurasi di bawah ini:

 { "/api": { "target": "http://localhost:3333", "secure": false } }

Ini membuat proxy, memungkinkan semua panggilan API ke rute yang cocok dengan /api untuk menargetkan server todo-api .

Membuat Halaman Next.js Dengan Nx

Dalam aplikasi Next.js kami, kami akan menghasilkan halaman baru, home , dan komponen item. Nx menyediakan alat CLI bagi kita untuk membuat halaman dengan mudah:

 npx nx g @nrwl/next:page home

Saat menjalankan perintah ini, kita akan mendapatkan prompt untuk memilih perpustakaan gaya yang ingin kita gunakan untuk halaman; untuk artikel ini, kita akan memilih styled-components . Voila! Halaman kami dibuat. Untuk membuat komponen, jalankan npx nx g @nrwl/next:component todo-item ; ini akan membuat folder component dengan komponen todo-item .

Konsumsi API di Aplikasi Next.js

Di setiap item yang harus dilakukan, kita akan memiliki dua tombol, untuk mengedit dan menghapus item yang harus dilakukan. Fungsi asinkron yang melakukan tindakan ini diteruskan sebagai alat peraga dari halaman beranda.

 … 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> ); }

Untuk fungsionalitas pembaruan, kami memiliki input yang dinonaktifkan saat status isEditingItem false . Setelah tombol "Edit" diklik, ini akan mengubah status isEditingItem menjadi true dan menampilkan tombol "Perbarui". Di sini, komponen input diaktifkan, dan pengguna dapat memasukkan nilai baru; ketika tombol "Perbarui" diklik, itu akan memanggil fungsi updateItem dengan parameter yang diteruskan, dan itu mengubah isEditingItem kembali ke false .

Di komponen home , kami memiliki fungsi asinkron yang melakukan operasi 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(); }, []); …

Di blok kode di atas, kami memiliki fetchItems , yang mengembalikan todoArray dari server. Kemudian, kita memiliki fungsi createItem , yang mengambil string; parameternya adalah nilai item tugas baru. Fungsi updateItem mengambil dua parameter, ID item yang akan diperbarui dan nilai updatedItem . Dan fungsi deleteItem menghapus item yang cocok dengan ID yang diteruskan.

Untuk merender item yang harus dilakukan, kami memetakan melalui status 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> ); …

Server dan front end kami sekarang sudah siap. Kita bisa melayani aplikasi API dengan menjalankan npx nx serve todo-api , dan untuk aplikasi Next.js kita menjalankan npx nx serve todo . Klik tombol "Lanjutkan", dan Anda akan melihat halaman dengan item tugas default yang ditampilkan.

Halaman item yang harus dilakukan aplikasi, dengan daftar tugas default di server.
Halaman item todo aplikasi. (Pratinjau besar)

Kami sekarang memiliki aplikasi Next.js dan Express yang bekerja bersama dalam satu ruang kerja.

Nx memiliki alat CLI lain yang memungkinkan kita untuk melihat grafik ketergantungan aplikasi kita di terminal yang kita jalankan. Jalankan npx nx dep-graph , dan kita akan melihat layar yang mirip dengan gambar di bawah, yang menggambarkan grafik ketergantungan aplikasi kita.

Grafik ketergantungan yang menunjukkan hubungan antara aplikasi di ruang kerja
Dep-grafik aplikasi. (Pratinjau besar)

Perintah CLI lainnya untuk Nx

  • nx list
    Mencantumkan plugin Nx yang saat ini diinstal.
  • nx migrate latest
    Memperbarui paket di package.json ke versi terbaru.
  • nx affected
    Melakukan tindakan hanya pada aplikasi yang terpengaruh atau dimodifikasi.
  • nx run-many --target serve --projects todo-api,todo
    Menjalankan perintah target di semua proyek yang terdaftar.

Kesimpulan

Sebagai gambaran umum Nx, artikel ini telah membahas apa yang ditawarkan Nx dan bagaimana Nx mempermudah pekerjaan kami. Kami juga berjalan melalui pengaturan aplikasi Next.js di ruang kerja Nx, menambahkan plugin Express ke ruang kerja yang ada, dan menggunakan fitur monorepo untuk menampung lebih dari satu aplikasi di ruang kerja kami.

Anda akan menemukan kode sumber lengkap di repositori GitHub. Untuk informasi tambahan tentang Nx, lihat dokumentasi atau dokumentasi Nx untuk Next.js.