Original: ANGLAIS:https://gauliang.github.io/blogs/2022/watch-markdown-files-and-hot-load-the-nextjs-page/
Next.js Offre Fast-Refresh Capacité,Ça peut vous aider à React Les modifications apportées par le composant fournissent une rétroaction immédiate.
Mais,Quand tu passes Markdown Lorsque le document fournit le contenu du site,Parce que Markdown Non, pas du tout. React Components,La mise à jour à chaud échouera.
Comment?
La solution à ce problème peut être envisagée sous les aspects suivants:
- Comment le serveur surveille les mises à jour de fichiers
- Comment le serveur informe le navigateur
- Comment le navigateur met à jour la page
- Comment obtenir les dernières Markdown Contenu
- Comment Next.js Les serveurs de développement démarrent ensemble
Surveiller les mises à jour des fichiers
Accord: markdown Les documents sont conservés dans Next.js Sous la racine du projet
_contents/
Moyenne
Adoption node:fs.watch
Surveillance récursive des modules _contents
Table des matières,Quand le document change,Déclencheur listener Mise en œuvre.
Nouveau fichier scripts/watch.js
Surveillance _contents
Table des matières.
const { watch } = require('node:fs');
function main(){
watch(process.cwd() + '/_contents', { recursive: true }, (eventType, filename) => {
console.log(eventType, filename)
});
}
Informez le navigateur
Le serveur passe WebSocket Connexion au navigateur , Lorsque le serveur de développement découvre un changement de fichier ,Adoption WS Aviser le navigateur de mettre à jour la page .
Le navigateur doit savoir si le fichier mis à jour est lié au routage de la page courante. ,Donc,, Le message envoyé par le serveur au navigateur doit contenir au moins le courant
Mettre à jour le routage de la page pour le fichier .
WebSocket
ws
Est simple et facile à utiliser、 Très rapide et entièrement testé WebSocket Mise en œuvre du client et du serveur.Adoption ws
Démarrage WebSocket Serveur.
const { watch } = require('node:fs');
const { WebSocketServer } = require('ws')
function main() {
const wss = new WebSocketServer({ port: 80 })
wss.on('connection', (ws, req) => {
watch(process.cwd() + '/_contents', { recursive: true }, (eventType, filename) => {
const path = filename.replace(/\.md/, '/')
ws.send(JSON.stringify({ event: 'markdown-changed', path }))
})
})
}
Serveur de connexion du Navigateur
Créer un nouveau HotLoad
Components, Responsable de l'écoute des messages du serveur , Mise à jour de la page . Le composant satisfait aux exigences suivantes: :
- Maintenir un lien avec WebSocekt Server Connexion à
- Après avoir écouté les messages du serveur , Déterminer si le routage actuel de la page est lié au changement de fichier , Non pertinent ignoré
- Les messages du serveur peuvent être envoyés intensivement , Nécessite un traitement anti - bavardage lors du chargement d'une nouvelle version du contenu
- Chargement Markdown Fichier et mise à jour complète
- Ce composant n'est disponible qu'en
Modèle de développement
Au travail.
import { useRouter } from "next/router"
import { useEffect } from "react"
interface Instance {
ws: WebSocket
timer: any
}
let instance: Instance = {
ws: null as any,
timer: null as any
}
function getInstance() {
if (instance.ws === null) {
instance.ws = new WebSocket('ws://localhost')
}
return instance
}
function _HotLoad({ setPost, params }: any) {
const { asPath } = useRouter()
useEffect(() => {
const instance = getInstance()
instance.ws.onmessage = async (res: any) => {
const data = JSON.parse(res.data)
if (data.event === 'markdown-changed') {
if (data.path === asPath) {
const post = await getPreviewData(params)
setPost(post)
}
}
}
return () => {
instance.ws.CONNECTING && instance.ws.close(4001, asPath)
}
}, [])
return null
}
export function getPreviewData(params: {id:string[]}) {
if (instance.timer) {
clearTimeout(instance.timer)
}
return new Promise((resolve) => {
instance.timer = setTimeout(async () => {
const res = await fetch('http://localhost:3000/api/preview/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
})
resolve(res.json())
}, 200)
})
}
let core = ({ setPost, params }: any)=>null
if(process.env.NODE_ENV === 'development'){
console.log('development hot load');
core = _HotLoad
}
export const HotLoad = core
Aperçu des données API
Créer un aperçu des données API,Lire Markdown Contenu du fichier, Et compilé dans le format utilisé pour le rendu de page .Les résultats ici
Doit être compatible avec [...id].tsx
Dans la page getStaticProps()
La méthode renvoie une structure de données de page entièrement cohérente ,Autres
La logique peut être directement réutilisée .
Nouveau API Documentation pages/api/preview.ts
,
import type { NextApiRequest, NextApiResponse } from 'next'
import { getPostData } from '../../lib/posts'
type Data = {
name: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
if (process.env.NODE_ENV === 'development') {
const params = req.body
const post = await getPostData(['posts', ...params.id])
return res.status(200).json(post)
} else {
return res.status(200)
}
}
Mise à jour de la page
Page pages/[...id].tsx
Introduction de HotLoad
Components,Et passer setPostData()
Et params
Voilà. HotLoad
Components.
...
import { HotLoad } from '../../components/hot-load'
const Post = ({ params, post, prev, next }: Params) => {
const [postData, setPostData] = useState(post)
useEffect(()=>{
setPostData(post)
},[post])
return (
<Layout>
<Head>
<title>{postData.title} - Gauliang</title>
</Head>
<PostContent post={postData} prev={prev} next={next} />
<BackToTop />
<HotLoad setPost={setPostData} params={params} />
</Layout>
)
}
export async function getStaticProps({ params }: Params) {
return {
props: {
params,
post:await getPostData(['posts', ...params.id])
}
}
}
export async function getStaticPaths() {
const paths = getAllPostIdByType()
return {
paths,
fallback: false
}
}
export default Post
Démarrer le script
Mise à jour package.json
De dev
Script:
"scripts": {
"dev": "node scripts/watch.js & \n next dev"
},
Résumé
Ce qui précède, Aperçu général de la logique de mise en oeuvre . Lors de l'atterrissage d'un projet spécifique , Quelques détails à considérer ,
Par exemple:: Vous souhaitez pouvoir demander un nom de fichier plus élevé sur la ligne de commande lors de la mise à jour du fichier 、 Ajuster la logique de correspondance entre le fichier et le routage en fonction de l'information de routage personnalisée .
Next.js Version originale du blog :https://gauliang.github.io/blogs/2022/watch-markdown-files-and-hot-load-the-nextjs-page/