当前位置:网站首页>Listen to the markdown file and hot update next JS page

Listen to the markdown file and hot update next JS page

2022-06-24 23:10:00 Simple way

original text :https://gauliang.github.io/blogs/2022/watch-markdown-files-and-hot-load-the-nextjs-page/

Next.js Provides Fast-Refresh Ability , It can help you to React The edits made by the component provide immediate feedback .
however , When you pass Markdown File provides website content , because Markdown No React Components , Hot update will fail .

How do you do it?

This problem can be solved from the following aspects :

  1. How the server monitors file updates
  2. How the server notifies the browser
  3. How the browser updates the page
  4. How to get the latest Markdown Content
  5. How to communicate with Next.js Start up with the development server

Monitoring file updates

Appointment : markdown The documents are stored in Next.js In the root directory of the project _contents/ in

adopt node:fs.watch Module recursive monitoring _contents Catalog , When the document changes , Trigger listener perform .
New file scripts/watch.js monitor _contents Catalog .

const { watch } = require('node:fs');

function main(){
    watch(process.cwd() + '/_contents', { recursive: true }, (eventType, filename) => {
        console.log(eventType, filename)
    });
}

Notification browser

Server through WebSocket Connect to the browser , When the development server finds that the file changes , adopt WS Notify the browser to update the page .
The browser needs to know whether the updated file is related to the route of the current page , therefore , The message sent by the server to the browser should at least contain the current
Update the page route corresponding to the file .

WebSocket

ws It's easy to use 、 Extremely fast and fully tested WebSocket Client and server implementation . adopt ws start-up WebSocket The server .

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

The browser connects to the server

Create a new one HotLoad Components , Be responsible for listening for messages from the server , And hot page updates . The components meet the following requirements :

  1. Maintain a singleton pattern with WebSocekt Server The connection of
  2. After listening to the server message , Determine whether the current page route is related to the changed file , Ignore if irrelevant
  3. Server side messages may be sent intensively , You need to do anti shake processing when loading new version content
  4. load Markdown File and complete the update
  5. This component is only available in Development mode Work under the
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

Data preview API

Create data preview API, Read Markdown The contents of the document , And compile it into the format used for page rendering . The results here
Should and [...id].tsx On the page getStaticProps() Method returns a page with exactly the same data structure , relevant
Logic can be reused directly .

newly build API file 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)
    }
}

Update page

page pages/[...id].tsx Introduction in HotLoad Components , And transmission setPostData() And params to 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

The startup script

to update package.json Of dev Script :

"scripts": {
    "dev": "node scripts/watch.js & \n next dev"
},

summary

Above , Overall overview of the general implementation logic . When the specific project is implemented , There are some details to consider ,
Such as : When updating a file, you want to prompt for a different file name on the command line 、 Adjust the matching logic between file and route according to personalized route information .

Next.js Original blog version :https://gauliang.github.io/blogs/2022/watch-markdown-files-and-hot-load-the-nextjs-page/

原网站

版权声明
本文为[Simple way]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/175/202206241941288699.html