Introduction
Bonjour, comment vas-tu ? Voici Vítor, qui revient avec un nouveau projet pour vous aider à améliorer vos compétences en programmation. Cela fait un moment que je n'ai pas publié de tutoriel. Au cours des derniers mois, j'ai pris du temps pour me reposer et me concentrer sur d'autres activités. Durant cette période, j'ai développé un petit projet web : un blog, qui est devenu l'objet de ce tutoriel.
Dans ce guide, nous allons créer le frontend d'une page de blog capable de rendre du Markdown. L'application comprendra des itinéraires publics et privés, l'authentification des utilisateurs et la possibilité d'écrire du texte Markdown, d'ajouter des photos, d'afficher des articles et bien plus encore.
N'hésitez pas à personnaliser votre candidature comme vous le souhaitez, je l'encourage même.
Vous pouvez accéder au référentiel de cette application ici :
Gondrak08
/
plateforme de blog
Une plateforme de blog réalisée avec Next.js/typescript.
Plateforme pour blog
- Tutoriel em texto
Ingrédients
- next-auth - bibliothèque d'authentification pour Next.js
- github.com/markdown-it/markdown-it - bibliothèque markdown.
- github.com/sindresorhus/github-markdown-css- Pour le style de notre éditeur markdown.
- github.com/remarkjs/react-markdown - Bibliothèque pour rendre le markdown dans nos composants React.
- github.com/remarkjs/remark-react/tree/4722bdf - Plugin pour transformer Markdown en React.
- codemirror.net - Composant éditeur pour le Web.
- réagir-icons - bibliothèque d'icônes pour réagir.
Comment utiliser
npm i npm run start
Serveur
vous pouvez trouver le serveur de votre application sur le serveur
Ce tutoriel comprend également l'écriture du serveur Node.js qui sera utilisé dans ce guide :
J'espère que vous l'apprécierez.
Bon codage !
Bibliothèques
Voici un résumé des bibliothèques utilisées dans ce projet :
- next-auth - Bibliothèque d'authentification pour Next.js
- github.com/markdown-it/markdown-it - Bibliothèque Markdown.
- github.com/sindresorhus/github-markdown-css - Pour styliser notre éditeur Markdown.
- github.com/remarkjs/react-markdown - Bibliothèque pour le rendu Markdown dans notre composant React.
- github.com/remarkjs/remark-react/tree/4722bdf - Plugin pour transformer Markdown en React.
- codemirror.net - Éditeur de composants Web.
- React-icons - Bibliothèque d'icônes pour React.
Création du projet React
Nous utiliserons la dernière version du framework Next.js, qui, au moment de la rédaction de ce tutoriel, est la version 13.4.
Exécutez la commande suivante pour créer le projet :
npm i npm run start
Lors de l'installation, sélectionnez les paramètres du modèle. Dans ce tutoriel, j'utiliserai TypeScript comme langage de programmation et le framework CSS Tailwind pour styliser notre application.
Configuration
Installons maintenant toutes les bibliothèques que nous utiliserons.
Réduction
npx create-next-app myblog
Réagir Remarque
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Codemiroir
remark remark-gfm remark-react
Icônes
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Nettoyez ensuite la structure initiale de votre installation en supprimant tout ce que nous n'utiliserons pas.
Architecture
Voici la structure finale de notre application.
npm i react-icons @types/react-icons
Premiers pas
Configuration de next.config
A la racine du projet, dans le fichier next.config.js, configurons l'adresse de domaine à partir de laquelle nous accéderons aux images de nos articles. Pour ce tutoriel, ou si vous utilisez un serveur local, nous utiliserons localhost.
Assurez-vous d'inclure cette configuration pour garantir le bon chargement des images dans votre application.
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
Configuration du middleware
Dans le dossier racine de l'application src/, créez un middleware.ts pour vérifier l'accès aux routes privées.
const nextConfig = { images: { domains: ["localhost"], }, };
Pour en savoir plus sur les middlewares et tout ce que vous pouvez faire avec eux, consultez la documentation.
Configuration de la route d'authentification
Dans le dossier /app, créez un fichier nommé route.ts dans api/auth/[...nextauth]. Il contiendra la configuration de nos routes, se connectant à notre API d'authentification à l'aide du CredentialsProvider.
Le CredentialsProvider vous permet de gérer la connexion avec des informations d'identification arbitraires, telles que le nom d'utilisateur et le mot de passe, le domaine, l'authentification à deux facteurs, le périphérique matériel, etc.
Tout d'abord, à la racine de votre projet, créez un fichier .env.local et ajoutez un token qui sera utilisé comme notre secret.
npm i npm run start
Ensuite, écrivons notre système d'authentification, où ce NEXTAUTH_SECRET sera ajouté à notre secret dans le fichier src/app/auth/[...nextauth]/routes.ts.
npx create-next-app myblog
Fournisseur d'authentification
Créons un fournisseur d'authentification, un contexte, qui partagera les données de nos utilisateurs à travers les pages de notre parcours privé. Nous l'utiliserons plus tard pour envelopper l'un de nos fichiers layout.tsx.
Créez un fichier dans src/context/auth-provider.tsx avec le contenu suivant :
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Styles globaux
Dans l'ensemble, dans notre application, nous utiliserons Tailwind CSS pour créer notre style. Cependant, à certains endroits, nous partagerons des classes CSS personnalisées entre les pages et les composants.
remark remark-gfm remark-react
Mises en page
Écrivons maintenant les mises en page, à la fois privées et publiques.
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
Appels API
Notre application effectuera plusieurs appels à notre API, et vous pourrez adapter cette application pour utiliser n'importe quelle API externe. Dans notre exemple, nous utilisons notre application locale. Si vous n'avez pas vu le tutoriel backend et la création du serveur, consultez-le.
Dans src/services/, écrivons les fonctions suivantes :
- authService.ts : fonction chargée d'authentifier notre utilisateur sur le serveur.
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 : fonction chargée de récupérer tous les articles enregistrés dans notre base de données :
export { default } from "next-auth/middleware"; export const config = { matcher: ["/", "/newArticle/", "/article/", "/article/:path*"], };
- postArticle.tsx : fonction chargée de soumettre les données de l'article à notre serveur.
.env.local NEXTAUTH_SECRET = SubsTituaPorToken
- editArticle.tsx : fonction chargée de modifier un article spécifique au sein de la base de données.
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 : fonction chargée de supprimer un article spécifique de notre base de données. </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> ) };
Composants
Ensuite, écrivons chaque composant utilisé dans l'application.
Composants/Navbar.tsx
Un composant simple avec deux liens de navigation.
/*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; }
Composants/Chargement.tsx
Un composant de chargement simple, utilisé en attendant la fin des appels d'API.
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> ); }
Composants/Pagination.tsx
Un composant de pagination utilisé sur notre page affichant l'ensemble de nos articles, dans notre parcours privé. Vous pouvez trouver un article plus détaillé sur la façon d'écrire ce composant ici
npm i npm run start
Composants/ArticleCard.tsx
Un composant de carte pour afficher des articles écrits.
Ce composant contient également un lien qui mènera à la fois à la page d'affichage de l'article et à la page d'édition d'un article préalablement rédigé.
npx create-next-app myblog
Composants/ArticleList.tsx
Un composant chargé d'effectuer des appels API et d'afficher la réponse.
Ici, nous utiliserons deux appels API via les fonctions que nous avons écrites :
- getArticles.ts - renvoie tous les articles qui seront affichés dans le composant.
- RemoveArticle - supprime un article spécifique de notre liste et de notre serveur.
Nous utiliserons le composant Pagination.tsx, écrit précédemment, pour répartir le nombre d'articles sur les pages.
npm i markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Pages
Ensuite, nous passerons en revue chacune de nos pages, divisées selon leurs itinéraires respectifs.
Pages publiques
Se connecter
Voici la page d'accueil de notre application. Il s’agit d’une page simple, et vous pouvez la modifier comme bon vous semble. Sur cette page, nous utiliserons la fonction de connexion fournie par la bibliothèque de navigation next-auth.
Dans le fichier src/app/pages/public/login/page.tsx.
remark remark-gfm remark-react
Page de l'article
Pour créer la page de lecture de l'article, nous allons développer une page dynamique.
Chaque plateforme de blog que vous avez visitée possède probablement une page dédiée à la lecture d'articles, accessible via une URL. La raison en est un itinéraire de page dynamique. Heureusement, Next.js facilite cela grâce à sa nouvelle méthode AppRouter, qui nous simplifie grandement la vie.
Premièrement : nous devons créer la route dans notre structure en ajoutant un dossier [id]. Cela donnera la structure suivante : pages/(public)/articles/[id]/pages.tsx.
- L'identifiant correspond au slug de notre itinéraire de navigation.
- params est une propriété passée dans l'arborescence de notre application contenant le slug de navigation.
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Deuxième : utilisez la bibliothèque MarkdownIt pour permettre à la page d'afficher le texte au format Markdown.
npm i react-icons @types/react-icons
Et enfin,
une fois la page prête, en accédant par exemple à localhost:3000/articles/1 dans le navigateur, vous pourrez visualiser l'article avec l'ID fourni.
Dans notre cas, l'ID sera transmis via la navigation en cliquant sur l'un des composants ArticleCards.tsx, qui sera rendu sur la page principale de notre itinéraire privé.
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
Pages privées
Voici nos pages privées, accessibles uniquement une fois l'utilisateur authentifié dans notre application.
Maison
Dans notre dossier app/pages/, lorsqu'un fichier est déclaré à l'intérieur de (), cela signifie que la route correspond à /.
Dans notre cas, le dossier (Accueil) fait référence à la page d'accueil de notre parcours privé. Il s'agit de la première page que l'utilisateur voit lors de son authentification dans le système. Cette page affichera la liste des articles de notre base de données.
Les données seront traitées par notre composant ArticlesList.tsx. Si vous n'avez pas encore écrit ce code, reportez-vous à la section composants.
Dans app/(pages)/(privé)/(accueil)/page.tsx.
npm i npm run start
Nouvel article
C'est l'une des pages les plus importantes de notre application, car elle nous permet d'enregistrer nos articles.
Cette page permettra à l'utilisateur de :
- Écrivez un article au format Markdown.
- Attribuez une image à l'article.
- Prévisualisez le texte Markdown avant de le soumettre au serveur.
La page utilise plusieurs hooks :
- useCallback - utilisé pour mémoriser des fonctions.
- useState - vous permet d'ajouter une variable d'état à notre composant.
- useSession - nous permet de vérifier si l'utilisateur est authentifié et d'obtenir le jeton d'authentification.
Pour cela, nous utiliserons deux composants :
- TextEditor.tsx : l'éditeur de texte que nous avons écrit précédemment.
- Preview.tsx : un composant d'affichage des fichiers au format Markdown.
Lors de la construction de cette page, nous utiliserons notre API :
- POST : Grâce à notre fonction postArticle, nous enverrons l'article au serveur.
Nous utiliserons également le hook useSession, fourni par la bibliothèque next-auth, pour obtenir le jeton d'authentification de l'utilisateur, qui servira à enregistrer l'article sur le serveur.
Cela impliquera trois appels API distincts.
Dans app/pages/(private)/newArticle/page.tsx.
"utiliser le client" ; importer React, { ChangeEvent, useCallback, useState } depuis "react" ; importer { useSession } depuis "next-auth/react" ; importer {redirection} depuis "suivant/navigation" ; importer postArtical depuis "@/services/postArticle" ; importer { AiOutlineFolderOpen } depuis "react-icons/ai" ; importer { RiImageEditLine } depuis "react-icons/ri" ; importer l'image depuis "suivant/image" ; importer TextEditor depuis "@/components/textEditor" ; importer l'aperçu depuis "@/components/PreviewText" ; importer { AiOutlineSend } depuis "react-icons/ai" ; importer { BsBodyText } depuis "react-icons/bs" ; fonction d'exportation par défaut NewArticle(params:any) { const { data : session } : any = useSession ({ requis : vrai, onUnauthenticated() { redirect("/connexion"); }, }); const [imageUrl, setImageUrl] = useState<object>({}); const [previewImage, setPreviewImage] = useState<string>(""); const [previewText, setPreviewText] = useState<boolean>(false); const [titre, setTitle] = useState<string>(""); const [doc, setDoc] = useState<string>("# Écrivez votre texte... n"); const handleDocChange = useCallback((newDoc: any) => { setDoc(nouveauDoc); }, []); if (!session?.user) renvoie null ; const handleArticleSubmit = async (e:any) => { e.preventDefault(); jeton const : chaîne = session.user.token ; essayer { const res = attendre postArtical({ identifiant : session.user.userId.toString(), jeton : jeton, URLimage : URLimage, titre : "titre" doc : doc, }); console.log('re--->', res); redirect('/succès'); } attraper (erreur) { console.error('Erreur lors de la soumission de l'article :', erreur); // Gérer l'erreur si nécessaire erreur de lancement ; } } ; const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => { if (e.target.files && e.target.files.length > 0) { const fichier = e.target.files[0]; const url = URL.createObjectURL(fichier); setPreviewImage(url); setImageUrl(fichier); } } ; const handleTextPreview = (e: any) => { e.preventDefault(); setPreviewText(!previewText); } ; retour ( <section classname="w-full h-full min-h-screen relatif py-8"> {previewText && ( <div classname="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100 arrondi-xl w-full max-w-[33em] z-30"> <aper doc="{doc}" titre="{titre}" aper onpreview="{()"> setPreviewText(!previewText)} /> </aper> </div> )} <form classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 arrondi-md bg-slate-50 drop-shadow-xl flex flex-col écart-2 "> {" "} <div classname="flex justifier-entre les éléments-centre"> <bouton classname="border-b-2 arrondi-md border-slate-500 p-2 éléments flexibles-center gap-2 survol :border-slate-400 survol :text-slate-800" onclick="{handleTextPreview}"> <bsbodytext></bsbodytext> Aperçu </bouton>{" "} <bouton classname="group border border-b-2 border-slate-500 arrondi-md p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800 " onclick="{handleArticleSubmit}"> Envoyer le texte <aioutlinesend classname="w-5 h-5 group-hover:text-red-500"></aioutlinesend> </bouton> </div> <div classname="header-wrapper flex flex-col gap-2 "> <div classname="image-box"> {previewImage.length === 0 && ( <div classname="select-image"> <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen> glisser et déposer l'image étiquette> <entr> <h4> Modifier l'article </h4> <p>Une page similaire au <em>Nouvel Article</em> (newArticle), avec quelques différences.</p> <p>Tout d'abord, nous définissons un itinéraire dynamique où nous recevons un identifiant comme paramètre de navigation. Ceci est très similaire à ce qui a été fait sur la page de lecture de l’article. <br> app/(pages)/(private)/editArticle/[id]/page.tsx<br> </p> <pre class="brush:php;toolbar:false">"utiliser le client" ; importer React, { useState, useEffect, useCallback, useRef, ChangeEvent } depuis « react » ; importer { useSession } depuis "next-auth/react" ; importer {redirection} depuis "suivant/navigation" ; importer l'image depuis 'suivant/image' ; importer { IArticle } depuis "@/interfaces/article.interface" ; importer { AiOutlineEdit } depuis "react-icons/ai" ; importer { BsBodyText } depuis "react-icons/bs" ; importer { AiOutlineFolderOpen } depuis "react-icons/ai" ; importer { RiImageEditLine } depuis "react-icons/ri" ; importer l'aperçu depuis "@/components/PreviewText" ; importer TextEditor depuis "@/components/textEditor" ; importer le chargement depuis '@/components/Loading' ; importer editArtical depuis "@/services/editArticle" ; fonction d'exportation par défaut EditArticle({ params }: { params: any }) { const { data : session } : any = useSession ({ requis : vrai, onUnauthenticated() { redirect("/connexion"); }, }); identifiant const : nombre = params.id ; const [article, setArticle] = useState<iarticle nul>(nul); const [imageUrl, setImageUrl] = useState<object>({}); const [previewImage, setPreviewImage] = useState<string>(""); const [previewText, setPreviewText] = useState<boolean>(false) const [titre, setTitle] = useState<string>(""); const [doc, setDoc] = useState<string>(''); const handleDocChange = useCallback((newDoc: any) => { setDoc(nouveauDoc); }, []); const inputRef= useRef<htmlinputelement>(null); const fetchArticle = async (id : numéro) => { essayer { réponse const = attendre la récupération ( `http://localhost:8080/articles/getById/${id}`, ); const jsonData = attendre réponse.json(); setArticle(jsonData); } attraper (erreur) { console.log("quelque chose s'est mal passé :", euh); } } ; useEffect(() => { if (article !== null || article !== non défini) { fetchArticle(id); } }, [identifiant]); useEffect(()=>{ si(article != null && article.content){ setDoc(article.content) } si(article !=null && article.image){ setPreviewImage(`http://localhost:8080/` article.image) } },[article]) const handleArticleSubmit = async (e:any) => { e.preventDefault(); jeton const : chaîne = session.user.token ; essayer{ const res = attendre editArtical({ je l'ai fait, jeton : jeton, URLimage :URLimage, titre : titre, doc : doc, }); console.log('re--->',res) retourner la résolution ; } capture (erreur) { console.log("Erreur :", erreur) } } ; const handleImageClick = ()=>{ console.log('hiii') si(inputRef.current){ inputRef.current.click(); } }const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => { if (e.target.files && e.target.files.length > 0) { const fichier = e.target.files[0]; const url = URL.createObjectURL(fichier); setPreviewImage(url); setImageUrl(fichier); } } ; const handleTextPreview = (e: any) => { e.preventDefault(); setPreviewText(!previewText); console.log('bonjour depuis l'aperçu !') } ; if(!article) renvoie <chargement></chargement> si(article?.contenu) retour ( <section classname="w-full h-full min-h-screen relatif py-8"> {previewText && ( <div classname="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100 arrondi-xl w-full max-w-[33em] z-30"> <aper doc="{doc}" titre="{titre}" aper onpreview="{()"> setPreviewText(!previewText)} /> </aper> </div> )} <div classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 arrondi-md bg-white drop- shadow-md flex flex-col écart-2"> <form classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 arrondi-md bg-slate-50 drop-shadow-md flex flex-col écart-2 "> {" "} <div classname="flex justifier-entre les éléments-centre"> <bouton classname="border-b-2 arrondi-md border-slate-500 p-2 flex items-center gap-2 survol :border-slate-400 survol :text-slate-800" onclick="{handleTextPreview}"> <bsbodytext></bsbodytext> Aperçu </bouton>{" "} <bouton classname="group border border-b-2 border-slate-500 arrondi-md p-2 flex items-center gap-2 hover:border-slate-400 hover:text-slate-800 " onclick="{handleArticleSubmit}"> Editer l'article <aioutlineedit classname="w-5 h-5 group-hover:text-red-500"></aioutlineedit> </bouton> </div> <div classname="header-wrapper flex flex-col gap-2 "> <div classname="image-box"> {previewImage.length === 0 && ( <div classname="select-image"> <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen> glisser et déposer l'image étiquette> <entr> <h2> Conclusion </h2> <p>Tout d'abord, je tiens à vous remercier d'avoir pris le temps de lire ce tutoriel, et je tiens également à vous féliciter de l'avoir terminé. J'espère que cela vous a bien servi et que les instructions étape par étape ont été faciles à suivre.</p> <p>Deuxièmement, j'aimerais souligner quelques points sur ce que nous venons de construire. C'est la base d'un système de blog, et il y a encore beaucoup à ajouter, comme une page publique affichant tous les articles, une page d'enregistrement des utilisateurs ou même une page d'erreur 404 personnalisée. Si, au cours du tutoriel, vous vous êtes interrogé sur ces pages et que vous les avez manquées, sachez que c'était intentionnel. Ce tutoriel vous a fourni suffisamment d'expérience pour créer vous-même ces nouvelles pages, en ajouter bien d'autres et implémenter de nouvelles fonctionnalités.</p> <p>Merci beaucoup, et à la prochaine fois. o/</p> </entr> </div> </div> </div> </form> </div></section></htmlinputelement></htmlinputelement></string></string></boolean></string></object></iarticle>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Les applications de JavaScript dans le monde réel incluent la programmation côté serveur, le développement des applications mobiles et le contrôle de l'Internet des objets: 1. La programmation côté serveur est réalisée via Node.js, adaptée au traitement de demande élevé simultané. 2. Le développement d'applications mobiles est effectué par le reactnatif et prend en charge le déploiement multiplateforme. 3. Utilisé pour le contrôle des périphériques IoT via la bibliothèque Johnny-Five, adapté à l'interaction matérielle.

J'ai construit une application SAAS multi-locataire fonctionnelle (une application EdTech) avec votre outil technologique quotidien et vous pouvez faire de même. Premièrement, qu'est-ce qu'une application SaaS multi-locataire? Les applications saas multi-locataires vous permettent de servir plusieurs clients à partir d'un chant

Cet article démontre l'intégration frontale avec un backend sécurisé par permis, construisant une application fonctionnelle EdTech SaaS en utilisant Next.js. Le frontend récupère les autorisations des utilisateurs pour contrôler la visibilité de l'interface utilisateur et garantit que les demandes d'API adhèrent à la base de rôles

JavaScript est le langage central du développement Web moderne et est largement utilisé pour sa diversité et sa flexibilité. 1) Développement frontal: construire des pages Web dynamiques et des applications à une seule page via les opérations DOM et les cadres modernes (tels que React, Vue.js, Angular). 2) Développement côté serveur: Node.js utilise un modèle d'E / S non bloquant pour gérer une concurrence élevée et des applications en temps réel. 3) Développement des applications mobiles et de bureau: le développement de la plate-forme multiplateuse est réalisé par réact noral et électron pour améliorer l'efficacité du développement.

Les dernières tendances de JavaScript incluent la montée en puissance de TypeScript, la popularité des frameworks et bibliothèques modernes et l'application de WebAssembly. Les prospects futurs couvrent des systèmes de type plus puissants, le développement du JavaScript côté serveur, l'expansion de l'intelligence artificielle et de l'apprentissage automatique, et le potentiel de l'informatique IoT et Edge.

JavaScript est la pierre angulaire du développement Web moderne, et ses principales fonctions incluent la programmation axée sur les événements, la génération de contenu dynamique et la programmation asynchrone. 1) La programmation axée sur les événements permet aux pages Web de changer dynamiquement en fonction des opérations utilisateur. 2) La génération de contenu dynamique permet d'ajuster le contenu de la page en fonction des conditions. 3) La programmation asynchrone garantit que l'interface utilisateur n'est pas bloquée. JavaScript est largement utilisé dans l'interaction Web, les applications à une page et le développement côté serveur, améliorant considérablement la flexibilité de l'expérience utilisateur et du développement multiplateforme.

Python est plus adapté à la science des données et à l'apprentissage automatique, tandis que JavaScript est plus adapté au développement frontal et complet. 1. Python est connu pour sa syntaxe concise et son écosystème de bibliothèque riche, et convient à l'analyse des données et au développement Web. 2. JavaScript est le cœur du développement frontal. Node.js prend en charge la programmation côté serveur et convient au développement complet.

JavaScript ne nécessite pas d'installation car il est déjà intégré à des navigateurs modernes. Vous n'avez besoin que d'un éditeur de texte et d'un navigateur pour commencer. 1) Dans l'environnement du navigateur, exécutez-le en intégrant le fichier HTML via des balises. 2) Dans l'environnement Node.js, après avoir téléchargé et installé Node.js, exécutez le fichier JavaScript via la ligne de commande.


Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

SublimeText3 Linux nouvelle version
Dernière version de SublimeText3 Linux

DVWA
Damn Vulnerable Web App (DVWA) est une application Web PHP/MySQL très vulnérable. Ses principaux objectifs sont d'aider les professionnels de la sécurité à tester leurs compétences et leurs outils dans un environnement juridique, d'aider les développeurs Web à mieux comprendre le processus de sécurisation des applications Web et d'aider les enseignants/étudiants à enseigner/apprendre dans un environnement de classe. Application Web sécurité. L'objectif de DVWA est de mettre en pratique certaines des vulnérabilités Web les plus courantes via une interface simple et directe, avec différents degrés de difficulté. Veuillez noter que ce logiciel

ZendStudio 13.5.1 Mac
Puissant environnement de développement intégré PHP

Listes Sec
SecLists est le compagnon ultime du testeur de sécurité. Il s'agit d'une collection de différents types de listes fréquemment utilisées lors des évaluations de sécurité, le tout en un seul endroit. SecLists contribue à rendre les tests de sécurité plus efficaces et productifs en fournissant facilement toutes les listes dont un testeur de sécurité pourrait avoir besoin. Les types de listes incluent les noms d'utilisateur, les mots de passe, les URL, les charges utiles floues, les modèles de données sensibles, les shells Web, etc. Le testeur peut simplement extraire ce référentiel sur une nouvelle machine de test et il aura accès à tous les types de listes dont il a besoin.

SublimeText3 version chinoise
Version chinoise, très simple à utiliser