pengenalan
Hello, apa khabar? Ini ialah Vítor, kembali dengan projek baharu untuk membantu anda meningkatkan kemahiran pengaturcaraan anda. Sudah lama sejak kali terakhir saya menerbitkan tutorial. Sejak beberapa bulan lalu, saya mengambil sedikit masa untuk berehat dan fokus kepada aktiviti lain. Dalam tempoh ini, saya membangunkan projek web kecil: blog, yang menjadi tumpuan tutorial ini.
Dalam panduan ini, kami akan mencipta bahagian hadapan halaman blog yang mampu memaparkan Markdown. Aplikasi ini akan merangkumi laluan awam dan peribadi, pengesahan pengguna dan keupayaan untuk menulis teks Markdown, menambah foto, memaparkan artikel dan banyak lagi.
Jangan teragak-agak untuk menyesuaikan aplikasi anda mengikut kehendak anda—saya juga menggalakkannya.
Anda boleh mengakses repositori untuk aplikasi ini di sini:
Gondrak08
/
platform blog
Plataform blog yang dibuat dengan Next.js/typescript.
Plataforma para blog
- Tutorial em teks
Bahan-bahan
- next-auth - biblioteca de autenticação para Next.js
- github.com/markdown-it/markdown-it - markdown biblioteca.
- github.com/sindresorhus/github-markdown-css- Para dar style ao nosso editor markdown.
- github.com/remarkjs/react-markdown - Biblioteca untuk renderizar markdown em nosso componente react.
- github.com/remarkjs/remark-react/tree/4722bdf - Pemalam untuk pengubah Markdown em React.
- codemirror.net - Komponen penyunting untuk web.
- ikon reaksi - lib de icones untuk bertindak balas.
Como usar
npm i npm run start
Pelayan
você pode encontrar or servidor dessa applicação em server
Tutorial ini juga termasuk penulisan pelayan Node.js yang akan digunakan dalam panduan ini:
Saya harap anda menikmatinya.
Selamat pengekodan!
Perpustakaan
Berikut ialah ringkasan perpustakaan yang digunakan dalam projek ini:
- next-auth - Pustaka pengesahan untuk Next.js
- github.com/markdown-it/markdown-it - Pustaka penurunan harga.
- github.com/sindresorhus/github-markdown-css - Untuk menggayakan editor Markdown kami.
- github.com/remarkjs/react-markdown - Pustaka untuk memberikan Markdown dalam komponen React kami.
- github.com/remarkjs/remark-react/tree/4722bdf - Pemalam untuk mengubah Markdown menjadi React.
- codemirror.net - Editor komponen web.
- react-icons - Pustaka ikon untuk React.
Mencipta Projek React
Kami akan menggunakan versi terkini rangka kerja Next.js, yang, pada masa menulis tutorial ini, ialah versi 13.4.
Jalankan arahan berikut untuk mencipta projek:
npm i npm run start
Semasa pemasangan, pilih tetapan templat. Dalam tutorial ini, saya akan menggunakan TypeScript sebagai bahasa pengaturcaraan dan rangka kerja Tailwind CSS untuk menggayakan aplikasi kami.
Konfigurasi
Sekarang mari pasang semua perpustakaan yang akan kami gunakan.
Penurunan harga
npx create-next-app myblog
React Remark
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Codemirror
remark remark-gfm remark-react
ikon
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Kemudian bersihkan struktur awal pemasangan anda dengan mengalih keluar semua yang tidak akan kami gunakan.
Seni bina
Ini adalah struktur akhir aplikasi kami.
npm i react-icons @types/react-icons
Langkah Pertama
Mengkonfigurasi seterusnya.config
Dalam akar projek, dalam fail next.config.js, mari kita konfigurasikan alamat domain dari mana kita akan mengakses imej untuk artikel kita. Untuk tutorial ini, atau jika anda menggunakan pelayan tempatan, kami akan menggunakan localhost.
Pastikan anda memasukkan konfigurasi ini untuk memastikan pemuatan imej yang betul dalam aplikasi anda.
src- |- app/ | |-(pages)/ | | |- (private)/ | | | |- (home) | | | |- editArticle/[id] | | | | | | | |- newArticle | | | - (public)/ | | | - article/[id] | | | - login | | | api/ | |- auth/[...nextAuth]/route.ts | |- global.css | |- layout.tsx | | - components/ | - context/ | - interfaces/ | - lib/ | - services/ middleware.ts
Mengkonfigurasi Middleware
Dalam folder akar aplikasi src/, cipta middleware.ts untuk mengesahkan akses kepada laluan peribadi.
const nextConfig = { images: { domains: ["localhost"], }, };
Untuk mengetahui lebih lanjut tentang perisian tengah dan semua yang anda boleh lakukan dengannya, semak dokumentasi.
Mengkonfigurasi Laluan Pengesahan
Di dalam folder /app, cipta fail bernama route.ts dalam api/auth/[...nextauth]. Ia akan mengandungi konfigurasi untuk laluan kami, menyambung ke API pengesahan kami menggunakan CredentialsProvider.
Penyedia Kredensial membolehkan anda mengendalikan log masuk dengan bukti kelayakan sewenang-wenangnya, seperti nama pengguna dan kata laluan, domain, pengesahan dua faktor, peranti perkakasan, dll.
Pertama, dalam akar projek anda, buat fail .env.local dan tambahkan token yang akan digunakan sebagai rahsia kami.
npm i npm run start
Seterusnya, mari tulis sistem pengesahan kami, di mana NEXTAUTH_SECRET ini akan ditambahkan pada rahsia kami dalam fail src/app/auth/[...nextauth]/routes.ts.
npx create-next-app myblog
Pembekal Pengesahan
Mari kita cipta penyedia pengesahan, konteks, yang akan berkongsi data pengguna kami merentasi halaman laluan peribadi kami. Kami akan menggunakannya kemudian untuk membungkus salah satu fail susun atur.tsx kami.
Buat fail dalam src/context/auth-provider.tsx dengan kandungan berikut:
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Gaya Global
Secara keseluruhan, dalam aplikasi kami, kami akan menggunakan CSS Tailwind untuk mencipta gaya kami. Walau bagaimanapun, di sesetengah tempat, kami akan berkongsi kelas CSS tersuai antara halaman dan komponen.
remark remark-gfm remark-react
Susun atur
Sekarang mari kita tulis reka letak, baik peribadi mahupun awam.
app/layout.tsx
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
pages/layout.tsx
npm i react-icons @types/react-icons
Panggilan API
Aplikasi kami akan membuat beberapa panggilan ke API kami, dan anda boleh menyesuaikan aplikasi ini untuk menggunakan mana-mana API luaran. Dalam contoh kami, kami menggunakan aplikasi tempatan kami. Jika anda belum melihat tutorial bahagian belakang dan penciptaan pelayan, semaknya.
Dalam src/services/, mari tulis fungsi berikut:
- authService.ts: fungsi yang bertanggungjawab untuk mengesahkan pengguna kami pada pelayan.
src- |- app/ | |-(pages)/ | | |- (private)/ | | | |- (home) | | | |- editArticle/[id] | | | | | | | |- newArticle | | | - (public)/ | | | - article/[id] | | | - login | | | api/ | |- auth/[...nextAuth]/route.ts | |- global.css | |- layout.tsx | | - components/ | - context/ | - interfaces/ | - lib/ | - services/ middleware.ts
2.refreshAccessToken.tsx:
const nextConfig = { images: { domains: ["localhost"], }, };
- getArticles.tsx: fungsi yang bertanggungjawab untuk mengambil semua artikel yang disimpan dalam pangkalan data kami:
export { default } from "next-auth/middleware"; export const config = { matcher: ["/", "/newArticle/", "/article/", "/article/:path*"], };
- postArticle.tsx: fungsi yang bertanggungjawab untuk menyerahkan data artikel ke pelayan kami.
.env.local NEXTAUTH_SECRET = SubsTituaPorToken
- editArticle.tsx: fungsi yang bertanggungjawab untuk mengubah suai artikel tertentu dalam pangkalan data.
import NextAuth from "next-auth/next"; import type { AuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import { authenticate } from "@/services/authService"; import refreshAccessToken from "@/services/refreshAccessToken"; export const authOptions: AuthOptions = { providers: [ CredentialsProvider({ name: "credentials", credentials: { email: { name: "email", label: "email", type: "email", placeholder: "Email", }, password: { name: "password", label: "password", type: "password", placeholder: "Password", }, }, async authorize(credentials, req) { if (typeof credentials !== "undefined") { const res = await authenticate({ email: credentials.email, password: credentials.password, }); if (typeof res !== "undefined") { return { ...res }; } else { return null; } } else { return null; } }, }), ], session: { strategy: "jwt" }, secret: process.env.NEXTAUTH_SECRET, callbacks: { async jwt({ token, user, account }: any) { if (user && account) { return { token: user?.token, accessTokenExpires: Date.now() + parseInt(user?.expiresIn, 10), refreshToken: user?.tokenRefresh, }; } if (Date.now() <ol> <li> deleteArticle.tsx: fungsi yang bertanggungjawab untuk mengalih keluar artikel tertentu daripada pangkalan data kami. </li> </ol> <pre class="brush:php;toolbar:false">'use client'; import React from 'react'; import { SessionProvider } from "next-auth/react"; export default function Provider({ children, session }: { children: React.ReactNode, session: any }): React.ReactNode { return ( <sessionprovider session="{session}"> {children} </sessionprovider> ) };
Komponen
Seterusnya, mari tulis setiap komponen yang digunakan sepanjang aplikasi.
Komponen/Navbar.tsx
Komponen ringkas dengan dua pautan navigasi.
/*global.css*/ .container { max-width: 1100px; width: 100%; margin: 0px auto; } .image-container { position: relative; width: 100%; height: 5em; padding-top: 56.25%; /* Aspect ratio 16:9 (dividindo a altura pela largura) */ } .image-container img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; } @keyframes spinner { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .loading-spinner { width: 50px; height: 50px; border: 10px solid #f3f3f3; border-top: 10px solid #293d71; border-radius: 50%; animation: spinner 1.5s linear infinite; }
Komponen/Pemuatan.tsx
Komponen pemuatan mudah, digunakan sementara menunggu panggilan API selesai.
import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import Provider from "@/context/auth-provider"; import { getServerSession } from "next-auth"; import { authOptions } from "./api/auth/[...nextauth]/route"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Markdown Text Editor", description: "Created by ", }; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { const session = await getServerSession(authOptions); return ( <provider session="{session}"> {children} </provider> ); }
Komponen/Penomboran.tsx
Komponen penomboran yang digunakan pada halaman kami memaparkan semua artikel kami, dalam laluan peribadi kami. Anda boleh mendapatkan artikel yang lebih terperinci tentang cara menulis komponen ini di sini
npm i npm run start
Komponen/ArticleCard.tsx
Komponen kad untuk memaparkan artikel bertulis.
Komponen ini juga mengandungi pautan yang akan membawa kepada kedua-dua halaman paparan artikel dan halaman untuk mengedit artikel yang ditulis sebelum ini.
npx create-next-app myblog
Komponen/ArticleList.tsx
Komponen yang bertanggungjawab untuk membuat panggilan API dan memaparkan respons.
Di sini, kami akan menggunakan dua panggilan API melalui fungsi yang kami tulis:
- getArticles.ts - mengembalikan semua artikel yang akan dipaparkan dalam komponen.
- removeArticle - mengalih keluar artikel tertentu daripada senarai kami dan daripada pelayan kami.
Kami akan menggunakan komponen Pagination.tsx, yang ditulis sebelum ini, untuk membahagikan bilangan artikel merentas halaman.
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
muka surat
Seterusnya, kami akan melalui setiap halaman kami, dibahagikan dengan laluan masing-masing.
Halaman Awam
Log masuk
Ini adalah halaman utama aplikasi kami. Ia adalah halaman yang mudah, dan anda boleh mengubah suainya mengikut kesesuaian anda. Pada halaman ini, kami akan menggunakan fungsi log masuk yang disediakan oleh pustaka navigasi pengesahan seterusnya.
Dalam fail src/app/pages/public/login/page.tsx.
remark remark-gfm remark-react
Halaman Artikel
Untuk mencipta halaman bacaan artikel, kami akan membangunkan halaman dinamik.
Setiap platform blog yang anda lawati berkemungkinan mempunyai halaman khusus untuk membaca artikel, boleh diakses melalui URL. Sebabnya ialah laluan halaman dinamik. Nasib baik, Next.js memudahkan perkara ini dengan kaedah AppRouter baharunya, menjadikan kehidupan kita lebih mudah.
Pertama: kita perlu mencipta laluan dalam struktur kita dengan menambahkan folder [id]. Ini akan menghasilkan struktur berikut: pages/(public)/articles/[id]/pages.tsx.
- Id sepadan dengan slug laluan navigasi kami.
- params ialah harta yang diluluskan melalui pepohon aplikasi kami yang mengandungi slug navigasi.
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Kedua: gunakan pustaka MarkdownIt untuk membolehkan halaman memaparkan teks dalam format Markdown.
npm i react-icons @types/react-icons
Dan akhirnya,
setelah halaman itu sedia, dengan mengakses, sebagai contoh, localhost:3000/articles/1 dalam penyemak imbas, anda akan dapat melihat artikel dengan ID yang disediakan.
Dalam kes kami, ID akan dihantar melalui navigasi apabila mengklik pada salah satu komponen ArticleCards.tsx, yang akan dipaparkan pada halaman utama laluan peribadi kami.
src- |- app/ | |-(pages)/ | | |- (private)/ | | | |- (home) | | | |- editArticle/[id] | | | | | | | |- newArticle | | | - (public)/ | | | - article/[id] | | | - login | | | api/ | |- auth/[...nextAuth]/route.ts | |- global.css | |- layout.tsx | | - components/ | - context/ | - interfaces/ | - lib/ | - services/ middleware.ts
Halaman Persendirian
Berikut ialah halaman peribadi kami, yang hanya boleh diakses setelah pengguna disahkan dalam aplikasi kami.
Rumah
Di dalam folder apl/halaman/ kami, apabila fail diisytiharkan di dalam (), ini bermakna laluan itu sepadan dengan /.
Dalam kes kami, folder (Laman Utama) merujuk kepada halaman utama laluan peribadi kami. Ia adalah halaman pertama yang pengguna lihat apabila mengesahkan ke dalam sistem. Halaman ini akan memaparkan senarai artikel daripada pangkalan data kami.
Data akan diproses oleh komponen ArticlesList.tsx kami. Jika anda belum menulis kod ini lagi, rujuk semula bahagian komponen.
Dalam apl/(halaman)/(peribadi)/(rumah)/halaman.tsx.
npm i npm run start
Artikel Baru
Ini adalah salah satu halaman paling penting dalam aplikasi kami, kerana ia membolehkan kami mendaftarkan artikel kami.
Halaman ini akan membolehkan pengguna untuk:
- Tulis artikel dalam format Markdown.
- Tetapkan imej pada artikel.
- Pratonton teks Markdown sebelum menyerahkannya ke pelayan.
Halaman menggunakan beberapa cangkuk:
- useCallback - digunakan untuk menghafal fungsi.
- useState - membolehkan anda menambah pembolehubah keadaan pada komponen kami.
- useSession - membolehkan kami menyemak sama ada pengguna disahkan dan mendapatkan token pengesahan.
Untuk ini, kami akan menggunakan dua komponen:
- TextEditor.tsx: editor teks yang kami tulis sebelum ini.
- Preview.tsx: komponen untuk memaparkan fail dalam format Markdown.
Semasa membina halaman ini, kami akan menggunakan API kami:
- POST: Menggunakan fungsi postArticle kami, kami akan menghantar artikel ke pelayan.
Kami juga akan menggunakan cangkuk useSession, yang disediakan oleh perpustakaan pengesahan seterusnya, untuk mendapatkan token pengesahan pengguna, yang akan digunakan untuk mendaftarkan artikel pada pelayan.
Ini akan melibatkan tiga panggilan API yang berbeza.
Dalam app/pages/(private)/newArticle/page.tsx.
"gunakan klien"; import React, { ChangeEvent, useCallback, useState } daripada "react"; import { useSession } daripada "next-auth/react"; import { ubah hala } daripada "next/navigation"; import postArtical daripada "@/services/postArticle"; import { AiOutlineFolderOpen } daripada "react-icons/ai"; import { RiImageEditLine } daripada "react-icons/ri"; import Imej daripada "seterusnya/imej"; import TextEditor daripada "@/components/textEditor"; import Pratonton daripada "@/komponen/PreviewText"; import { AiOutlineSend } daripada "react-icons/ai"; import { BsBodyText } daripada "react-icons/bs"; eksport fungsi lalai NewArticle(params:any) { const { data: session }: any = useSession({ dikehendaki: benar, onUnauthenticated() { redirect("/log masuk"); }, }); const [imageUrl, setImageUrl] = useState<objek>({}); const [previewImage, setPreviewImage] = useState<rentetan>(""); const [previewText, setPreviewText] = useState<boolean>(false); const [title, setTitle] = useState<rentetan>(""); const [doc, setDoc] = useState<string>("# Escreva o seu texto... n"); const handleDocChange = useCallback((newDoc: any) => { setDoc(newDoc); }, []); jika (!session?.user) mengembalikan null; const handleArticleSubmit = tak segerak (e:mana-mana) => { e.preventDefault(); token const: rentetan = session.user.token; cuba { const res = menunggu postArtical({ id: session.user.userId.toString(), token: token, imageUrl: imageUrl, tajuk: "tajuk," doc: doc, }); console.log('re--->', res); redirect('/success'); } tangkap (ralat) { console.error('Ralat menghantar artikel:', ralat); // Kendalikan ralat jika perlu ralat lontaran; } }; const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => { jika (e.target.files && e.target.files.length > 0) { fail const = e.target.files[0]; const url = URL.createObjectURL(file); setPreviewImage(url); setImageUrl(fail); } }; const handleTextPreview = (e: mana-mana) => { e.preventDefault(); setPreviewText(!previewText); }; kembali ( <section classname="w-full h-penuh min-h-skrin relatif py-8"> {previewTeks && ( <div classname="kanan mutlak-16 atas-5 p-5 sempadan-2 sempadan-slate-500 bg-slate-100 rounded-xl w-full max-w-[33em] z-30"> <pratonton doc="{doc}" tajuk="{tajuk}" previewimage="{previewImage}" onpreview="{()"> setPreviewText(!previewText)} /> </pratonton> </div> )} <form classname="relative mx-auto max-w-[700px] h-penuh min-h-[90%] w-penuh p-2 sempadan-2 sempadan-slate-200 bulat-md bg-slate-50 drop-shadow-xl flex flex-col gap-2 "> {" "} <div classname="flex justify-antara item-center"> <butang classname="border-b-2 rounded-md border-slate-500 p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800" onclick="{handleTextPreview}"> <bsbodytext></bsbodytext> Pratonton {" "} <butang classname="group border border-b-2 border-slate-500 rounded-md p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800 " onclick="{handleArticleSubmit}"> Enviar Texto <aioutlinesend classname="w-5 h-5 group-hover:text-red-500"></aioutlinesend> </butang> </butang> </div> <div classname="header-wrapper flex flex-col gap-2 "> <div classname="image-box"> {previewImage.length === 0 && ( <div classname="select-image"> <label htmluntuk="imej" classname="p-4 sempadan putus-putus sempadan-4 sempadan-slate-400 kursor-penunjuk flex flex-col item-pusat justify-center"> <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen> drang dan lepaskan imej </label> <masukan> <h4> Edit Artikel </h4> <p>Halaman yang serupa dengan <em>Artikel Baharu</em> (Artikel baharu), dengan beberapa perbezaan.</p> <p>Pertama, kami mentakrifkan laluan dinamik di mana kami menerima id sebagai parameter navigasi. Ini hampir sama dengan apa yang dilakukan pada halaman membaca artikel. <br> app/(pages)/(private)/editArticle/[id]/page.tsx<br> </p> <pre class="brush:php;toolbar:false">"gunakan klien"; import React, { useState, useEffect, useCallback, useRef, ChangeEvent } daripada "react"; import { useSession } daripada "next-auth/react"; import { ubah hala } daripada "next/navigation"; import Imej daripada 'next/image'; import { IArticle } daripada "@/interfaces/article.interface"; import { AiOutlineEdit } daripada "react-icons/ai"; import { BsBodyText } daripada "react-icons/bs"; import { AiOutlineFolderOpen } daripada "react-icons/ai"; import { RiImageEditLine } daripada "react-icons/ri"; import Pratonton daripada "@/komponen/PreviewText"; import TextEditor daripada "@/components/textEditor"; import Pemuatan daripada '@/komponen/Pemuatan'; import editArtical daripada "@/services/editArticle"; eksport fungsi lalai EditArticle({ params }: { params: any }) { const { data: session }: any = useSession({ dikehendaki: benar, onUnauthenticated() { redirect("/log masuk"); }, }); const id: nombor = params.id; const [artikel, setArticle] = useState<iarticle null>(null); const [imageUrl, setImageUrl] = useState<objek>({}); const [previewImage, setPreviewImage] = useState<rentetan>(""); const [previewText, setPreviewText] = useState<boolean>(false) const [title, setTitle] = useState<rentetan>(""); const [doc, setDoc] = useState<rentetan>(''); const handleDocChange = useCallback((newDoc: any) => { setDoc(newDoc); }, []); const inputRef= useRef<htmlinputelement>(null); const fetchArticle = tak segerak (id: nombor) => { cuba { respons const = tunggu ambil( `http://localhost:8080/articles/getById/${id}`, ); const jsonData = menunggu respons.json(); setArticle(jsonData); } tangkap (err) { console.log("sesuatu telah berlaku:", err); } }; useEffect(() => { jika (artikel !== null || artikel !== tidak ditentukan) { fetchArticle(id); } }, [id]); useEffect(()=>{ if(artikel != null && article.content){ setDoc(article.content) } if(artikel !=null && article.image){ setPreviewImage(`http://localhost:8080/` article.image) } },[artikel]) const handleArticleSubmit = tak segerak (e:mana-mana) => { e.preventDefault(); token const: rentetan = session.user.token; cuba{ const res = menunggu editArtical({ id: id, token: token, imageUrl:imageUrl, tajuk: tajuk, doc: doc, }); console.log('re--->',res) kembalikan semula; } tangkap (ralat){ console.log("Ralat:", ralat) } }; const handleImageClick = ()=>{ console.log('hiii') if(inputRef.current){ inputRef.current.click(); } }const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => { jika (e.target.files && e.target.files.length > 0) { fail const = e.target.files[0]; const url = URL.createObjectURL(file); setPreviewImage(url); setImageUrl(fail); } }; const handleTextPreview = (e: mana-mana) => { e.preventDefault(); setPreviewText(!previewText); console.log('hello dari pratonton!') }; if(!article) return <memuatkan></memuatkan> jika(artikel?.kandungan) kembali ( <section classname="w-penuh h-penuh min-h-skrin relatif py-8"> {previewTeks && ( <div classname="kanan mutlak-16 atas-5 p-5 sempadan-2 sempadan-slate-500 bg-slate-100 rounded-xl w-full max-w-[33em] z-30"> <pratonton doc="{doc}" tajuk="{tajuk}" previewimage="{previewImage}" onpreview="{()"> setPreviewText(!previewText)} /> </pratonton> </div> )} <div classname="relative mx-auto max-w-[700px] h-min-h-penuh-[90%] w-penuh p-2 sempadan-2 sempadan-slate-200 bulat-md bg-titik putih- shadow-md flex flex-col gap-2"> <form classname="relative mx-auto max-w-[700px] h-penuh min-h-[90%] w-penuh p-2 sempadan-2 sempadan-slate-200 bulat-md bg-slate-50 drop-shadow-md flex flex-col gap-2 "> {" "} <div classname="flex justify-antara item-center"> <butang classname="border-b-2 rounded-md border-slate-500 p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800" onclick="{handleTextPreview}"> <bsbodytext></bsbodytext> Pratonton {" "} <butang classname="group border border-b-2 border-slate-500 rounded-md p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800 " onclick="{handleArticleSubmit}"> Edit artigo <aioutlineedit classname="w-5 h-5 group-hover:text-red-500"></aioutlineedit> </butang> </butang> </div> <div classname="header-wrapper flex flex-col gap-2 "> <div classname="image-box"> {previewImage.length === 0 && ( <div classname="select-image"> <label htmlfor="image" classname="p-4 sempadan putus-putus sempadan-4 sempadan-slate-400 kursor-penunjuk flex flex-col item-pusat justify-center"> <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen> drang dan lepaskan imej </label> <masukan> <h2> Kesimpulan </h2> <p>Pertama, saya ingin mengucapkan terima kasih kerana meluangkan masa untuk membaca tutorial ini, dan saya juga ingin mengucapkan tahniah kepada anda kerana telah menyelesaikannya. Saya harap ia berfungsi dengan baik dan arahan langkah demi langkah mudah diikuti.</p> <p>Kedua, saya ingin menyerlahkan beberapa perkara tentang perkara yang baru kami bina. Ini adalah asas sistem blog, dan masih banyak yang perlu ditambah, seperti halaman awam yang memaparkan semua artikel, halaman pendaftaran pengguna, atau halaman ralat 404 tersuai. Jika, semasa tutorial, anda tertanya-tanya tentang halaman ini dan terlepasnya, ketahui bahawa ini adalah disengajakan. Tutorial ini memberikan anda pengalaman yang mencukupi untuk membuat halaman baharu ini sendiri, menambah banyak lagi dan melaksanakan ciri baharu.</p> <p>Terima kasih banyak-banyak, dan sehingga kali seterusnya. o/</p> </masukan> </div> </div> </div> </form> </div></section></htmlinputelement></htmlinputelement></rentetan></rentetan></boolean></rentetan></objek></iarticle>
Atas ialah kandungan terperinci Membina Papan Pemuka Blog Dinamik dengan Next.js. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

JavaandjavascriptaredistinctLanguages: javaisusedforenterpriseandMobileApps, whilvascriptisforinteractivewebpages.1) javaisco mpiled, statiktyped, andrunsonjvm.2) javascriptisinterinterpreted, dinamicallytyped, andrunsinbrowsersornode.js.3) javausesoopwithcl

Jenis data teras JavaScript adalah konsisten dalam penyemak imbas dan node.js, tetapi ditangani secara berbeza dari jenis tambahan. 1) Objek global adalah tetingkap dalam penyemak imbas dan global di Node.js. 2) Objek penampan unik Node.js, digunakan untuk memproses data binari. 3) Terdapat juga perbezaan prestasi dan pemprosesan masa, dan kod perlu diselaraskan mengikut persekitaran.

JavaScriptusestWotypesofcomments: Single-line (//) danMulti-line (//)

Perbezaan utama antara Python dan JavaScript ialah sistem jenis dan senario aplikasi. 1. Python menggunakan jenis dinamik, sesuai untuk pengkomputeran saintifik dan analisis data. 2. JavaScript mengamalkan jenis yang lemah dan digunakan secara meluas dalam pembangunan depan dan stack penuh. Kedua -duanya mempunyai kelebihan mereka sendiri dalam pengaturcaraan dan pengoptimuman prestasi yang tidak segerak, dan harus diputuskan mengikut keperluan projek ketika memilih.

Sama ada untuk memilih Python atau JavaScript bergantung kepada jenis projek: 1) Pilih Python untuk Sains Data dan Tugas Automasi; 2) Pilih JavaScript untuk pembangunan front-end dan penuh. Python disukai untuk perpustakaannya yang kuat dalam pemprosesan data dan automasi, sementara JavaScript sangat diperlukan untuk kelebihannya dalam interaksi web dan pembangunan stack penuh.

Python dan JavaScript masing -masing mempunyai kelebihan mereka sendiri, dan pilihan bergantung kepada keperluan projek dan keutamaan peribadi. 1. Python mudah dipelajari, dengan sintaks ringkas, sesuai untuk sains data dan pembangunan back-end, tetapi mempunyai kelajuan pelaksanaan yang perlahan. 2. JavaScript berada di mana-mana dalam pembangunan front-end dan mempunyai keupayaan pengaturcaraan tak segerak yang kuat. Node.js menjadikannya sesuai untuk pembangunan penuh, tetapi sintaks mungkin rumit dan rawan kesilapan.

Javascriptisnotbuiltoncorc; it'saninterpretedlanguagethatrunsonenginesoftenwritteninc .1) javascriptwasdesignedasalightweight, interpratedlanguageforwebbrowsers.2)

JavaScript boleh digunakan untuk pembangunan front-end dan back-end. Bahagian depan meningkatkan pengalaman pengguna melalui operasi DOM, dan back-end mengendalikan tugas pelayan melalui Node.js. 1. Contoh front-end: Tukar kandungan teks laman web. 2. Contoh backend: Buat pelayan Node.js.


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Pelayar Peperiksaan Selamat
Pelayar Peperiksaan Selamat ialah persekitaran pelayar selamat untuk mengambil peperiksaan dalam talian dengan selamat. Perisian ini menukar mana-mana komputer menjadi stesen kerja yang selamat. Ia mengawal akses kepada mana-mana utiliti dan menghalang pelajar daripada menggunakan sumber yang tidak dibenarkan.

Versi Mac WebStorm
Alat pembangunan JavaScript yang berguna

Dreamweaver CS6
Alat pembangunan web visual

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

MantisBT
Mantis ialah alat pengesan kecacatan berasaskan web yang mudah digunakan yang direka untuk membantu dalam pengesanan kecacatan produk. Ia memerlukan PHP, MySQL dan pelayan web. Lihat perkhidmatan demo dan pengehosan kami.
