Rumah >hujung hadapan web >tutorial js >Next.js Deep Dive: Membina Apl Nota dengan Ciri Lanjutan
## Pengenalan dan Objektif
Dalam artikel blog ini, saya ingin melihat ciri Next.js yang paling penting yang anda perlukan dalam senario praktikal.
Saya mencipta artikel blog ini sebagai rujukan tunggal untuk diri saya dan pembaca yang berminat. Daripada perlu melalui keseluruhan dokumentasi nextjs. Saya rasa lebih mudah untuk mempunyai artikel blog yang ringkas dengan semua ciri praktikal seterusnyajs penting yang boleh anda lawati secara berkala untuk menyegarkan pengetahuan anda!
Kami akan meneliti ciri di bawah bersama-sama sambil membina aplikasi nota secara selari.
Penghala Apl
Pemuatan dan Pengendalian Ralat
Tindakan Pelayan
Pengambilan Data dan Cache
Penstriman dan Saspens
Laluan Selari
Pengendalian Ralat
Nota akhir kami mengambil kod aplikasi akan kelihatan seperti ini:
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Jangan ragu untuk melompat terus ke kod akhir yang boleh anda temui dalam spithacode repositori Github ini.
Jadi tanpa berlengah lagi, mari mulakan!
Sebelum menyelami perkembangan aplikasi nota kami, saya ingin memperkenalkan beberapa konsep nextjs utama yang penting untuk diketahui sebelum bergerak ke hadapan.
Penghala Aplikasi ialah direktori baharu "/app" yang menyokong banyak perkara yang tidak mungkin dalam direktori "/page" warisan seperti :
Penghalaan Bersarang: anda boleh menyarangkan folder satu dalam satu lagi. Url laluan halaman akan mengikuti sarang folder yang sama. Contohnya, url sepadan halaman bersarang ini /app/notes/[noteId]/edit/page.tsx selepas mengandaikan bahawa parameter dinamik [noteId] adalah sama dengan "1" ialah "/notes/1/edit.
/loading.tsx yang mengeksport komponen yang dipaparkan apabila halaman distrim ke penyemak imbas pengguna.
/error.tsx yang mengeksport komponen yang dipaparkan apabila halaman melemparkan beberapa ralat yang tidak ditangkap.
Penghalaan Selari dan banyak ciri yang akan kami lalui semasa membina aplikasi nota kami.
Mari kita mendalami topik yang sangat penting yang semua orang harus kuasai sebelum menyentuh Nextjs /app Penghala.
komponen pelayan pada asasnya ialah komponen yang dipaparkan pada pelayan.
Mana-mana komponen yang tidak didahului dengan arahan "use client" secara lalai ialah komponen pelayan termasuk halaman dan reka letak.
Komponen pelayan boleh berinteraksi dengan mana-mana API nodejs, atau mana-mana komponen yang dimaksudkan untuk digunakan pada pelayan.
Adalah mungkin untuk mendahului komponen pelayan dengan kata kunci async tidak seperti komponen klien. Jadi, anda boleh memanggil mana-mana fungsi tak segerak dan menunggunya sebelum memaparkan komponen.
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Anda mungkin terfikir mengapa malah pra-memaparkan komponen pada pelayan?
Jawapannya boleh diringkaskan dalam beberapa perkataan SEO, prestasi dan pengalaman pengguna.
Apabila pengguna melawat halaman, penyemak imbas memuat turun aset tapak web termasuk html, css dan javascript.
Himpunan javascript (yang termasuk kod rangka kerja anda) mengambil lebih banyak masa daripada aset lain untuk dimuatkan kerana saiznya.
Jadi pengguna perlu menunggu untuk melihat sesuatu pada skrin.
Perkara yang sama berlaku untuk perangkak yang bertanggungjawab mengindeks tapak web anda.
Banyak metrik SEO lain seperti LCP, TTFB, Kadar Lantunan,... akan terjejas.
komponen klien hanyalah komponen yang dihantar ke penyemak imbas pengguna.
Komponen pelanggan bukan sekadar komponen html dan css yang terdedah. Mereka memerlukan interaktiviti untuk berfungsi jadi tidak mungkin untuk memaparkannya pada pelayan.
interaktiviti dijamin oleh sama ada rangka kerja javascript seperti react ( useState, useEffect) atau pelayar sahaja atau DOM API.
Perisytiharan komponen klien hendaklah didahului dengan arahan "use client". Yang memberitahu Nextjs untuk mengabaikan bahagian interaktifnya (useState,useEffect...) dan menghantarnya terus ke penyemak imbas pengguna.
/client-component.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Saya tahu, perkara yang paling mengecewakan dalam Nextjs, ialah pepijat pelik yang boleh anda hadapi jika anda terlepas peraturan bersarang antara Komponen Pelayan dan Komponen Pelanggan.
Jadi dalam bahagian seterusnya, kami akan menjelaskannya dengan mempamerkan pilih atur bersarang yang berbeza mungkin antara Komponen Pelayan dan Komponen Pelanggan.
Kami akan Melangkau dua pilih kasih ini kerana ia jelas dibenarkan: Komponen Pelanggan Komponen Pelanggan lain dan Komponen Pelayan di dalam Komponen Pelayan lain.
Anda boleh mengimport Komponen Pelanggan dan menjadikannya seperti biasa di dalam komponen pelayan. Pilih atur ini agak jelas kerana halaman dan reka letak adalah secara lalai komponen pelayan.
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Bayangkan menghantar komponen klien ke penyemak imbas pengguna dan kemudian menunggu komponen pelayan yang terletak di dalamnya untuk memaparkan dan mengambil data. Itu tidak mungkin kerana komponen pelayan sudah dihantar kepada pelanggan, Bagaimanakah anda boleh memaparkannya pada pelayan?
Itulah sebab pilih atur jenis ini tidak disokong oleh Nextjs.
Jadi sentiasa ingat untuk mengelak mengimport Komponen Pelayan di dalam Komponen Pelanggan untuk menjadikannya sebagai kanak-kanak.
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Sentiasa cuba kurangkan javascript yang dihantar ke penyemak imbas pengguna dengan menolak komponen klien ke bawah dalam pokok jsx.
Tidak mungkin untuk mengimport dan membuat Komponen Pelayan secara langsung sebagai anak kepada Komponen Pelanggan tetapi terdapat penyelesaian yang menggunakan sifat kebolehkomposisian bertindak balas.
Caranya ialah dengan melepasi Komponen Pelayan sebagai anak kepada Komponen Pelanggan pada komponen pelayan peringkat lebih tinggi ( Komponen ParentServer).
Mari kita panggil ia Tipu Papa :D.
Helah ini memastikan Komponen Pelayan yang diluluskan sedang dipaparkan di pelayan sebelum menghantar Komponen Pelanggan ke penyemak imbas pengguna.
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
Kami akan melihat contoh konkrit di /app/page.tsx halaman utama aplikasi nota kami.
Tempat kami akan memaparkan komponen pelayan yang diluluskan sebagai kanak-kanak dalam komponen klien. Komponen klien boleh menunjukkan atau menyembunyikan kandungan yang diberikan komponen pelayan bergantung pada nilai pembolehubah keadaan boolean.
Tindakan pelayan ialah ciri nextjs yang menarik yang membenarkan panggilan dari jauh dan selamat fungsi yang diisytiharkan pada pelayan daripada komponen sisi klien anda .
Untuk mengisytiharkan tindakan pelayan anda hanya perlu menambah arahan "guna pelayan" ke dalam badan fungsi seperti yang ditunjukkan di bawah.
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Arahan "guna pelayan" memberitahu Nextjs bahawa fungsi itu mengandungi kod sisi pelayan yang hanya dilaksanakan pada pelayan.
Di bawah tudung Nextjs menghantar Id Tindakan dan mencipta titik tamat terpelihara untuk tindakan ini.
Jadi apabila anda memanggil tindakan ini dalam komponen klien Nextjs akan melaksanakan permintaan POST kepada titik akhir unik tindakan yang dikenal pasti oleh Id Tindakan sambil menghantar hujah bersiri yang telah anda hantar semasa memanggil tindakan dalam badan permintaan.
Mari kita jelaskan dengan lebih baik dengan contoh mudah ini.
Kami melihat sebelum ini, bahawa anda perlu menggunakan "guna pelayan" dalam arahan badan fungsi untuk mengisytiharkan tindakan pelayan. Tetapi bagaimana jika anda perlu mengisytiharkan sekumpulan tindakan pelayan sekaligus.
Nah, anda hanya boleh menggunakan arahan pada pengepala atau permulaan fail seperti yang ditunjukkan dalam kod di bawah.
/server/actions.ts
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Perhatikan bahawa tindakan pelayan hendaklah sentiasa ditandakan sebagai tidak segerak
Jadi dalam kod di atas, kami mengisytiharkan tindakan pelayan bernama createLogAction.
Tindakan ini bertanggungjawab untuk menyimpan entri log dalam fail tertentu pada pelayan di bawah direktori /logs.
Fail dinamakan berdasarkan argumen tindakan nama.
Tindakan Menambahkan entri log yang terdiri daripada tarikh penciptaan dan mesej hujah tindakan.
Sekarang, mari kita gunakan tindakan yang kami buat dalam CreateLogButton komponen bahagian pelanggan.
/komponen/CreateLogButton.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Komponen butang mengisytiharkan pembolehubah keadaan setempat bernama isSubmitting yang digunakan untuk menjejaki sama ada tindakan itu dilaksanakan atau tidak. Apabila tindakan sedang melaksanakan teks butang berubah daripada "Butang Log" kepada "Memuatkan...".
tindakan pelayan dipanggil apabila kita mengklik pada komponen Butang Log.
Pertama sekali, mari mulakan dengan mencipta skema dan jenis pengesahan Nota kami.
Memandangkan model sepatutnya mengendalikan pengesahan data, kami akan menggunakan perpustakaan popular untuk tujuan itu yang dipanggil zod.
Perkara menarik tentang zod ialah API deskriptifnya yang mudah difahami yang menjadikan penentuan model dan penjanaan TypeScript yang sepadan sebagai tugas yang lancar.
Kami tidak akan menggunakan model kompleks yang mewah untuk nota kami. Setiap nota akan mempunyai id unik, tajuk, kandungan dan medan tarikh penciptaan.
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Kami juga mengisytiharkan beberapa skema tambahan yang berguna seperti InsertNoteSchema dan WhereNoteSchema yang akan menjadikan hidup kami lebih mudah apabila kami mencipta fungsi boleh guna semula kami yang memanipulasi model kami kemudian.
Kami akan menyimpan dan memanipulasi nota kami dalam ingatan.
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Kami menyimpan tatasusunan nota kami dalam objek global ini untuk mengelakkan kehilangan keadaan tatasusunan kami setiap kali pemalar nota diimport ke dalam fail (muat semula halaman...).
Kes penggunaan createNote akan membolehkan kami memasukkan nota ke dalam tatasusunan nota. Fikirkan kaedah notes.unshift sebagai songsangan kaedah notes.push kerana ia menolak elemen ke permulaan tatasusunan dan bukannya penghujungnya.
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Kami akan menggunakan Nota kemas kini untuk mengemas kini nota tertentu dalam tatasusunan nota yang diberikan idnya. Ia mula-mula mencari indeks unsur, membuang ralat jika ia tidak ditemui dan mengembalikan nota yang sepadan berdasarkan indeks yang ditemui.
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
Fungsi kes guna deleteNote akan digunakan untuk memadam nota yang diberikan diberikan id nota.
Kaedah ini berfungsi sama, mula-mula ia mencari indeks nota yang diberikan idnya, membuang ralat jika ia tidak ditemui kemudian mengembalikan nota sepadan yang diindeks oleh id yang ditemui.
"use client" import { ServerComponent } from '@/components' // Not allowed :( export const ClientComponent = ()=>{ return ( <> <ServerComponent/> </> ) }
Fungsi getNote adalah jelas, ia hanya akan mencari nota yang diberikan idnya.
import {ClientComponent} from '@/components/...' import {ServerComponent} from '@/components/...' export const ParentServerComponent = ()=>{ return ( <> <ClientComponent> <ServerComponent/> </ClientComponent> </> ) }
Memandangkan kami tidak mahu menolak keseluruhan pangkalan data nota kami ke sisi pelanggan, kami hanya akan mengambil sebahagian daripada jumlah nota yang tersedia. Oleh itu, kita perlu melaksanakan penomboran sebelah pelayan.
export const Component = ()=>{ const serverActionFunction = async(params:any)=>{ "use server" // server code lives here //... / } const handleClick = ()=>{ await serverActionFunction() } return <button onClick={handleClick}>click me</button> }
Jadi fungsi getNotes pada asasnya akan membolehkan kami mengambil halaman tertentu daripada pelayan kami dengan menghantar hujah halaman.
Argumen had berfungsi untuk menentukan bilangan item yang terdapat pada halaman tertentu.
Contohnya:
Jika tatasusunan nota mengandungi 100 elemen dan hujah had bersamaan dengan 10.
Dengan meminta halaman 1 dari pelayan kami hanya 10 item pertama akan dikembalikan.
Argumen carian akan digunakan untuk melaksanakan carian sebelah pelayan. Ia akan memberitahu pelayan untuk hanya mengembalikan nota yang mempunyai carian Rentetan sebagai subrentetan sama ada dalam tajuk atau atribut kandungan.
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Kes penggunaan ini akan digunakan untuk mendapatkan beberapa data palsu tentang aktiviti terbaru pengguna.
Kami akan menggunakan fungsi ini dalam halaman /papan pemuka.
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Fungsi use case ini akan bertanggungjawab untuk mendapatkan statistik tentang teg berbeza yang digunakan dalam nota kami (#something).
Kami akan menggunakan fungsi ini dalam halaman /papan pemuka.
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Kami akan menggunakan fungsi use case ini untuk hanya mengembalikan beberapa data palsu tentang beberapa maklumat pengguna seperti nama, e-mel...
Kami akan menggunakan fungsi ini dalam halaman /papan pemuka.
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
"use client" import { ServerComponent } from '@/components' // Not allowed :( export const ClientComponent = ()=>{ return ( <> <ServerComponent/> </> ) }
Dalam halaman utama ini, kami akan menunjukkan helah atau penyelesaian sebelumnya untuk memaparkan Komponen Pelayan di dalam Komponen Pelanggan (Helah PaPa :D) .
/app/page.tsx
import {ClientComponent} from '@/components/...' import {ServerComponent} from '@/components/...' export const ParentServerComponent = ()=>{ return ( <> <ClientComponent> <ServerComponent/> </ClientComponent> </> ) }
Dalam kod di atas kami mengisytiharkan Komponen Pelayan Induk dipanggil Home yang bertanggungjawab untuk memaparkan halaman "/" dalam aplikasi kami.
Kami mengimport Komponen Pelayan bernama RandomNote dan Komponen Pelanggan bernama NoteOfTheDay.
Kami menghantar RandomNote Komponen Pelayan sebagai kanak-kanak kepada NoteOfTheDay Komponen Bahagian Pelanggan.
/app/components/RandomNote.ts
export const Component = ()=>{ const serverActionFunction = async(params:any)=>{ "use server" // server code lives here //... / } const handleClick = ()=>{ await serverActionFunction() } return <button onClick={handleClick}>click me</button> }
Komponen Pelayan RandomNote berfungsi seperti berikut:
ia mengambil nota rawak menggunakan fungsi kes penggunaan getRandomNote.
ia memaparkan butiran nota yang terdiri daripada tajuk dan bahagian atau subrentetan nota penuh kandungan.
/app/components/NoteOfTheDay.ts
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Komponen Pelanggan NoteOfTheDay di sisi lain berfungsi seperti yang diterangkan di bawah:
/app/notes/page.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Kami akan bermula dengan mencipta halaman /app/notes/page.tsx yang merupakan komponen pelayan yang bertanggungjawab untuk:
Mendapatkan parameter carian halaman yang merupakan rentetan yang dilampirkan di hujung URL selepas tanda ?: http://localhost:3000/notes?page=1&search=Sesuatu
Melalukan parameter carian ke dalam fungsi yang diisytiharkan secara setempat yang dipanggil fetchNotes.
Fungsi fetchNotes menggunakan fungsi use case kami yang diisytiharkan sebelum ini getNotes untuk mengambil halaman nota semasa.
Anda dapat melihat bahawa kami sedang membungkus fungsi getNotes dengan fungsi utiliti yang diimport daripada "next/cache" dipanggil unstable_cache. Fungsi cache tidak stabil digunakan untuk cache respons daripada fungsi getNotes.
Jika kami pasti tiada nota ditambahkan pada pangkalan data. Tidak masuk akal untuk memukulnya setiap kali halaman dimuat semula. Jadi fungsi unstable_cache ialah menandai hasil fungsi getNotes dengan tag "nota" yang boleh kita gunakan kemudian untuk membatalkan "nota" cache jika nota ditambah atau dipadamkan.
Fungsi fetchNotes mengembalikan dua nilai: nota dan jumlah.
Data yang terhasil (nota dan jumlah) dihantar ke Komponen Sebelah Pelanggan dipanggil NotesList yang bertanggungjawab untuk memaparkan nota kami.
Apabila pengguna menekan muat semula. Halaman kosong akan muncul kepada pengguna semasa data nota kami sedang diambil.
Untuk menyelesaikan masalah itu, kami akan menggunakan ciri Nextjs hebat yang dipanggil. Penstriman Halaman Sisi Pelayan.
Kita boleh melakukannya dengan mencipta fail loading.tsx, di sebelah fail /app/notes/page.tsx kami.
/app/notes/loading.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Semasa halaman itu distrim daripada pelayan, pengguna akan melihat halaman memuatkan rangka, yang memberi pengguna idea tentang jenis kandungan yang akan datang.
Bukankah itu keren :). cuma buat fail loading.tsx dan voila anda sudah selesai. ux anda berkembang maju ke peringkat seterusnya.
/app/notes/components/NotesList.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Senarai Nota Komponen Sebelah Pelanggan Menerima nota dan data berkaitan penomboran daripada induknya Komponen Pelayan iaitu Halaman Nota.
Kemudian komponen mengendalikan pemaparan halaman semasa nota. Setiap kad nota individu diberikan menggunakan komponen NoteView.
Ia juga menyediakan pautan ke halaman sebelumnya dan seterusnya menggunakan komponen Next.js Pautan yang penting untuk pra-mengambil data halaman seterusnya dan sebelumnya untuk membolehkan kami mempunyai pelanggan yang lancar dan pantas -navigasi sisi.
Untuk mengendalikan Carian Sisi Pelayan kami menggunakan cangkuk tersuai dipanggil useNotesSearch yang pada asasnya mengendalikan mencetuskan pengambilan semula nota apabila pengguna menaip pertanyaan tertentu dalam carian Input.
/app/notes/components/NoteView.ts
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Komponen NoteView adalah mudah, ia hanya bertanggungjawab untuk memberikan setiap kad nota individu dengan yang sepadan: tajuk, sebahagian daripada kandungan dan pautan tindakan untuk melihat butiran nota atau untuk mengeditnya.
/app/notes/components/hooks/use-notes-search.ts
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Kait tersuai useNotesSearch berfungsi seperti berikut:
Ia menyimpan prop initialSearch dalam keadaan setempat menggunakan cangkuk useState.
Kami menggunakan cangkuk useEffect React untuk mencetuskan navigasi halaman apabila nilai pembolehubah currentPage atau debouncedSearchValue.
URL halaman baharu dibina sambil mengambil kira halaman semasa dan nilai carian.
Fungsi setSearch akan dipanggil setiap kali aksara berubah apabila pengguna menaip sesuatu dalam Input carian. Itu akan menyebabkan terlalu banyak navigasi dalam masa yang singkat.
Untuk mengelakkannya kami hanya mencetuskan navigasi apabila pengguna berhenti menaip dalam istilah lain, kami menyahlantunkan nilai carian untuk tempoh masa tertentu (300ms dalam kes kami).
Seterusnya, mari kita lihat /app/notes/create/page.tsx yang merupakan pembalut komponen pelayan di sekitar komponen klien CreateNoteForm.
/app/notes/create/page.tsx
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
/app/notes/create/components/CreateNoteForm.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Borang komponen klien CreateNoteForm bertanggungjawab untuk mendapatkan semula data daripada pengguna kemudian menyimpannya dalam pembolehubah keadaan setempat (tajuk, kandungan).
Apabila borang diserahkan selepas mengklik pada butang hantar createNoteAction akan diserahkan dengan tajuk dan kandungan hujah negeri setempat .
Pembolehubah boolean keadaan isSubmitting digunakan untuk menjejak status penyerahan tindakan.
Jika createNoteAction berjaya diserahkan tanpa sebarang ralat, kami mengubah hala pengguna ke halaman /notes.
/app/notes/create/actions/create-note.action.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Kod tindakan createNoteAction adalah mudah, fail yang mengandungi didahului dengan arahan "use server" yang menunjukkan kepada Next.js bahawa tindakan ini boleh dipanggil dalam komponen klien.
Satu perkara yang harus kami tekankan tentang tindakan pelayan ialah hanya antara muka tindakan dihantar kepada klien tetapi bukan kod di dalam tindakan itu sendiri.
Dalam istilah lain, kod di dalam tindakan akan hidup pada pelayan, jadi kami tidak seharusnya mempercayai sebarang input yang datang daripada klien ke pelayan kami.
Itulah sebabnya kami menggunakan zod di sini untuk mengesahkan rawNote hujah tindakan menggunakan skema kami yang telah dibuat sebelum ini.
Selepas mengesahkan input kami, kami memanggil kes penggunaan createNote dengan data yang disahkan.
Jika nota berjaya dibuat, fungsi revalidateTag akan dipanggil untuk membatalkan entri cache yang ditag sebagai "nota" (Ingat fungsi unstable_cache yang digunakan dalam halaman /nota).
Halaman butiran nota memaparkan tajuk dan kandungan penuh nota tertentu berdasarkan id uniknya. Selain itu, ia menunjukkan beberapa butang tindakan untuk mengedit atau memadam nota.
/app/notes/[noteId]/page.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Mula-mula kami mendapatkan semula param halaman daripada prop halaman. Dalam Next.js 13 kita perlu menunggu hujah halaman params kerana ia adalah janji.
Selepas melakukan itu, kami menghantar params.noteId ke fetchNote fungsi yang diisytiharkan secara tempatan.
/app/notes/[noteId]/fetchers/fetch-note.ts
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Fungsi fetchNote membungkus kes penggunaan getNote kami dengan unstable_cache sambil menandakan hasil yang dikembalikan dengan "nota-detail" dan butiran nota/${id} Teg.
Teg "nota-butiran" boleh digunakan untuk membatalkan semua entri cache butiran nota sekaligus.
Sebaliknya, teg note-details/${id} hanya dikaitkan dengan nota tertentu yang ditakrifkan oleh id uniknya. Jadi kita boleh menggunakannya untuk membatalkan kemasukan cache nota tertentu dan bukannya keseluruhan set nota.
/app/notes/[noteId]/loading.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Peringatan
loading.tsx ialah halaman Next.js khas yang dipaparkan semasa halaman butiran nota sedang mengambil datanya di pelayan.
Atau dalam istilah lain semasa fungsi fetchNote sedang melaksanakan halaman rangka akan ditunjukkan kepada pengguna dan bukannya skrin kosong.
Ciri nextjs ini dipanggil Penstriman Halaman. Ia membolehkan anda menghantar keseluruhan reka letak induk statik halaman dinamik semasa menstrim kandungannya secara beransur-ansur.
Ini meningkatkan prestasi dan pengalaman pengguna dengan mengelak daripada menyekat ui semasa kandungan dinamik halaman sedang diambil pada pelayan.
/app/notes/[noteId]/components/DeleteNoteButton.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Sekarang mari kita selami DeleteNoteButton komponen bahagian klien.
Komponen bertanggungjawab untuk memaparkan butang padam dan melaksanakan deleteNoteAction kemudian mengubah hala pengguna ke halaman /notes apabila tindakan itu berjaya dilaksanakan.
Untuk menjejak status pelaksanaan tindakan kami menggunakan pembolehubah keadaan setempat isDeleting.
/app/notes/[noteId]/actions/delete-note.action.tsx
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
Kod deleteNoteAction berfungsi seperti berikut:
/app/notes/[noteId]/edit/page.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Halaman /app/notes/[noteId]/edit/page.tsx ialah komponen pelayan yang mendapat param noteId daripada janji params.
Kemudian ia mengambil nota menggunakan fungsi fetchNote.
Selepas pengambilan berjaya. Ia menyerahkan nota kepada EditNoteForm komponen bahagian klien.
/app/notes/[noteId]/edit/components/EditNoteForm.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Komponen bahagian sisi klien EditNoteForm menerima nota dan memberikan borang yang membolehkan pengguna mengemas kini butiran nota.
Pembolehubah keadaan setempat tajuk dan kandungan digunakan untuk menyimpan nilai input atau kawasan teks yang sepadan.
Apabila borang diserahkan melalui butang Kemas Kini Nota. updateNoteAction dipanggil dengan tajuk dan nilai kandungan sebagai argumen.
Pembolehubah keadaan isSubmitting digunakan untuk menjejak status penyerahan tindakan, membenarkan untuk menunjukkan penunjuk pemuatan apabila tindakan sedang dilaksanakan.
/app/notes/[noteId]/edit/actions/edit-note.action.ts
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Tindakan updateNoteAction berfungsi seperti berikut:
/app/dashboard/page.tsx
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
Halaman /app/dashboard/page.tsx dipecahkan kepada komponen sisi pelayan yang lebih kecil: Ringkasan Nota, RecentActivity dan TagCloud.
Setiap komponen pelayan mengambil datanya sendiri secara berasingan.
Setiap komponen pelayan dibalut dengan React Suspense Sempadan.
Peranan sempadan suspens adalah untuk memaparkan komponen sandaran(a Skeleton dalam kes kami) Apabila komponen pelayan kanak-kanak sedang mengambil datanya sendiri.
Atau dalam istilah lain sempadan Suspense membenarkan kami menangguhkan atau menangguhkan pemberian anak-anaknya sehingga beberapa syarat dipenuhi( Data di dalam kanak-kanak sedang dimuatkan).
Jadi pengguna akan dapat melihat halaman sebagai gabungan sekumpulan rangka. Semasa respons untuk setiap komponen individu sedang distrim oleh pelayan.
Satu kelebihan utama pendekatan ini adalah untuk mengelak daripada menyekat ui jika satu atau lebih komponen pelayan mengambil lebih banyak masa berbanding yang lain.
Jadi, jika kita mengandaikan bahawa masa pengambilan individu untuk setiap komponen diedarkan seperti berikut:
Apabila kita menekan muat semula, perkara pertama yang akan kita lihat ialah 3 pemuat rangka.
Selepas 1 saat komponen RecentActivity akan muncul.
Selepas 2 saat NotesSummary akan mengikuti kemudian TagCloud.
Jadi daripada membuat pengguna menunggu selama 3 saat sebelum melihat sebarang kandungan. Kami mengurangkan masa itu sebanyak 2 saat dengan menunjukkan Aktiviti Terkini dahulu.
Pendekatan pemaparan tambahan ini menghasilkan pengalaman dan prestasi pengguna yang lebih baik.
Kod untuk Komponen Pelayan individu diserlahkan di bawah.
/app/dashboard/components/RecentActivity.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Komponen pelayan RecentActivity pada asasnya mengambil aktiviti terakhir menggunakan fungsi kes penggunaan getRecentActivity dan menjadikannya dalam senarai tidak tertib.
/app/papan pemuka/komponen/TagCloud.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
Komponen sisi pelayan TagCloud diambil kemudian memaparkan semua nama tag yang digunakan dalam kandungan nota dengan kiraan masing-masing.
/app/dashboard/components/NotesSummary.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Komponen pelayan NotesSummary memaparkan maklumat ringkasan selepas mengambilnya menggunakan getNoteSummary fungsi use case.
Sekarang mari kita beralih ke halaman profil di mana kita akan melalui ciri nextjs yang menarik dipanggil Laluan Selari.
Laluan selari membolehkan kami secara serentak atau bersyarat memaparkan satu atau lebih halaman dalam reka letak yang sama.
Dalam contoh kami di bawah, kami akan memaparkan halaman maklumat pengguna dan halaman nota pengguna dalam reka letak yang sama iaitu /app/profile .
Anda boleh mencipta Laluan Selari dengan menggunakan slot bernama. Slot bernama diisytiharkan betul-betul sebagai subhalaman tetapi simbol @ harus mendahului nama folder tidak seperti halaman biasa.
Sebagai contoh, dalam folder /app/profile/ kami akan mencipta dua slot bernama:
Sekarang mari buat fail reka letak /app/profile/layout.tsx fail yang akan menentukan reka letak halaman /profile kami.
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Seperti yang anda lihat daripada kod di atas, kami kini mendapat akses kepada maklumat dan nota param yang mengandungi kandungan di dalam halaman @info dan @nota.
Jadi halaman @info akan dipaparkan di sebelah kiri dan @nota akan dipaparkan di sebelah kanan.
Kandungan dalam page.tsx (dirujuk oleh kanak-kanak) akan dipaparkan di bahagian bawah halaman.
/app/profile/@info/page.tsx
export const ServerComponent = async ()=>{ const posts = await getSomeData() // call any nodejs api or server function during the component rendering // Don't even think about it. No useEffects are allowed here x_x const pasta = await getPasta() return ( <ul> { data.map(d=>( <li>{d.title}</li> )) } </ul> ) }
UserInfoPage ialah komponen pelayan yang akan mengambil maklumat pengguna menggunakan getUserInfo fungsi use case.
Rangka sandaran di atas akan dihantar ke penyemak imbas pengguna apabila komponen sedang mengambil data dan dipaparkan pada pelayan (Server Side Streaming).
/app/profile/@info/loading.tsx
"use client" import React,{useEffect,useState} from "react" export const ClientComponent = ()=>{ const [value,setValue] = useState() useEffect(()=>{ alert("Component have mounted!") return ()=>{ alert("Component is unmounted") } },[]) //.......... return ( <> <button onClick={()=>alert("Hello, from browser")}></button> {/* .......... JSX Code ...............*/} </> ) }
Perkara yang sama berlaku untuk LastNotesPage komponen bahagian pelayan. ia akan mengambil data dan membuat pada pelayan sementara ui rangka sedang dipaparkan kepada pengguna
/app/profile/@notes/page.tsx
import { ClientComponent } from '@/components' // Allowed :) export const ServerComponent = ()=>{ return ( <> <ClientComponent/> </> ) }
/app/profile/@notes/loading.tsx
"use client" import { ServerComponent } from '@/components' // Not allowed :( export const ClientComponent = ()=>{ return ( <> <ServerComponent/> </> ) }
Sekarang mari kita terokai ciri yang cukup bagus dalam Nextjs halaman error.tsx.
Apabila anda menggunakan aplikasi anda ke pengeluaran, anda pasti mahu memaparkan ralat mesra pengguna apabila ralat tidak ditangkap dilemparkan daripada salah satu halaman anda.
Di situlah fail error.tsx masuk.
Mari kita buat halaman contoh dahulu yang melemparkan ralat tidak ditangkap selepas beberapa saat.
/app/error-page/page.tsx
import {ClientComponent} from '@/components/...' import {ServerComponent} from '@/components/...' export const ParentServerComponent = ()=>{ return ( <> <ClientComponent> <ServerComponent/> </ClientComponent> </> ) }
Apabila halaman tidur atau menunggu untuk fungsi tidur dilaksanakan. Halaman pemuatan di bawah akan ditunjukkan kepada pengguna.
/app/error-page/loading.tsx
export const Component = ()=>{ const serverActionFunction = async(params:any)=>{ "use server" // server code lives here //... / } const handleClick = ()=>{ await serverActionFunction() } return <button onClick={handleClick}>click me</button> }
Selepas beberapa saat ralat akan dibuang dan mencatat halaman anda :(.
Untuk mengelakkannya, kami akan mencipta fail error.tsx yang mengeksport komponen yang akan bertindak sebagai Sempadan Ralat untuk /app/error-page/page .tsx.
/app/error-page/error.tsx
- app/ - notes/ --------------------------------> Server Side Caching Features - components/ - NotesList.tsx - [noteId]/ - actions/ -------------------------> Server Actions feature - delete-note.action.ts - edit-note.action.ts - components/ - DeleteButton.tsx - page.tsx - edit/ - components/ - EditNoteForm.tsx - page.tsx - loading.tsx --------------------> Page level Streaming feature - create/ - actions/ - create-note.action.ts - components/ - CreateNoteForm.tsx - page.tsx - error-page/ - page.tsx - error.tsx --------------------------> Error Boundary as a page feature - dashboard/ ---------------------------> Component Level Streaming Feature - components/ - NoteActivity.tsx - TagCloud.tsx - NotesSummary.tsx - page.tsx - profile/ ----------------------------->[6] Parallel Routes Feature - layout.tsx - page.tsx - @info/ - page.tsx - loading.tsx - @notes/ - page.tsx - loading.tsx - core/ --------------------------> Our business logic lives here - entities/ - note.ts - use-cases/ - create-note.use-case.ts - update-note.use-case.ts - delete-note.use-case.ts - get-note.use-case.ts - get-notes.use-case.ts - get-notes-summary.use-case.ts - get-recent-activity.use-case.ts - get-recent-tags.use-case.ts
Dalam panduan ini, kami telah meneroka ciri utama Next.js dengan membina aplikasi nota praktikal. Kami telah membincangkan:
Dengan menggunakan konsep ini dalam projek dunia sebenar, kami telah memperoleh pengalaman praktikal dengan keupayaan hebat Next.js. Ingat, cara terbaik untuk mengukuhkan pemahaman anda adalah melalui latihan.
Jika anda mempunyai sebarang pertanyaan atau ingin membincangkan sesuatu dengan lebih lanjut, sila hubungi saya di sini.
Selamat pengekodan!
Atas ialah kandungan terperinci Next.js Deep Dive: Membina Apl Nota dengan Ciri Lanjutan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!