Markdown如何监控文件更新服务器如何通知浏览器.js开发服务器一起

原文:

Next.js 提供 Fast-Refresh 功能,该功能可针对您对 React 组件所做的编辑提供即时反馈。

但是,当您通过 Markdown 文件提供网站内容时,由于 Markdown 不是 React 组件,因此热更新将失败。

怎么做

要解决这个问题,考虑以下几个方面:

服务器如何监控文件更新 服务器如何通知浏览器 浏览器如何更新页面 如何获取最新的 Markdown 内容 如何使用 Next.js 开发服务器开始监控文件更新

约定:markdown文件存放在Next.js项目根目录下的_contents/中

通过node:fs.watch模块递归监听_contents目录,文件变化时触发监听器执行。

新建一个文件 scripts/watch.js 来监控 _contents 目录。

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

通知浏览器

服务器通过WebSocket与浏览器建立连接。当开发服务器发现文件发生变化时,通过WS通知浏览器更新页面。

浏览器需要知道更新的文件是否与当前页面所在的路由相关。因此,服务器发送给浏览器的消息应该至少包含当前页面

更新文件对应的页面路由。

WebSocket

ws 是一个易于使用、速度极快且经过全面测试的 WebSocket 客户端和服务器实现。通过 ws 启动 WebSocket 服务器。

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

        })
    })
}

浏览器连接服务器

新建一个HotLoad组件,负责监控来自服务器的消息和热执行页面更新。该组件满足以下要求:

通过单例模式保持与 WebSocekt 服务器的连接。监听服务器消息后,判断当前页面路由是否与更改的文件有关。如果不相关,服务器消息可能会被忽略并集中发送。加载新版本内容时需要这样做。去抖动加载 Markdown 文件并完成更新此组件仅在开发模式下有效

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

数据预览 API

创建数据预览API,读取Markdown文件内容,编译成页面渲染格式。这是结果

应该和[…id].tsx页面中getStaticProps()方法返回的页面数据结构一模一样,相关

逻辑可以直接重用。

新的 API 文件页面/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
) {

图片[1]-Markdown如何监控文件更新服务器如何通知浏览器.js开发服务器一起-唐朝资源网

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

更新页面

在 pages/[…id].tsx 中引入 HotLoad 组件,并将 setPostData() 和 params 传递给 HotLoad 组件。

...
import { HotLoad } from '../../components/hot-load'
const Post = ({ params, post, prev, next }: Params) => {
    const [postData, setPostData] = useState(post)
    
    useEffect(()=>{
        setPostData(post)
    },[post])
    return (
        
            
                {postData.title} - Gauliang
            
            
            
            
        

图片[2]-Markdown如何监控文件更新服务器如何通知浏览器.js开发服务器一起-唐朝资源网

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

启动脚本

更新 package.json 中的开发脚本:

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

总结

以上内容整体概括了大致的实现逻辑。在具体项目实施时,需要考虑一些细节,

比如文件更新时,希望命令行能提示更新后的文件名,根据个性化的路由信息​​调整文件与路由的匹配逻辑。

Next.js 博客版原创:

© 版权声明
THE END
喜欢就支持一下吧
点赞263赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容