Next.js 熱更新 Markdown 文件變更

来源:https://www.cnblogs.com/kelsen/archive/2022/06/24/watch-markdown-files-and-hot-load-the-nextjs-page.html
-Advertisement-
Play Games

Next.js 提供了 Fast-Refresh 能力,它可以為您對 React 組件所做的編輯提供即時反饋。 但是,當你通過 Markdown 文件提供網站內容時,由於 Markdown 不是 React 組件,熱更新將失效。 怎麼做 解決該問題可從以下幾方面思考: 伺服器如何監控文件更新 伺服器 ...


Next.js 提供了 Fast-Refresh 能力,它可以為您對 React 組件所做的編輯提供即時反饋。
但是,當你通過 Markdown 文件提供網站內容時,由於 Markdown 不是 React 組件,熱更新將失效。

怎麼做

解決該問題可從以下幾方面思考:

  1. 伺服器如何監控文件更新
  2. 伺服器如何通知瀏覽器
  3. 瀏覽器如何更新頁面
  4. 如何拿到最新的 Markdown 內容
  5. 如何與 Next.js 開發伺服器一起啟動

監控文件更新

約定: markdown 文件存放在 Next.js 項目根目錄下的 _contents/

通過 node:fs.watch 模塊遞歸的監控 _contents 目錄,當文件發生變更,觸發 listener 執行。
新建文件 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 組件,負責監聽來自服務端的消息,並熱實現頁面更新。組件滿足以下要求:

  1. 通過單例模式維護一個與 WebSocekt Server 的連接
  2. 監聽到服務端消息後,判斷當前頁面路由是否與變更文件有關,無關則忽略
  3. 服務端消息可能會密集發送,需要在載入新版本內容時做防抖處理
  4. 載入 Markdown 文件並完成更新
  5. 該組件僅在 開發模式 下工作
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 文件 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)
    }
}

更新頁面

頁面 pages/[...id].tsx 中引入 HotLoad 組件,並傳遞 setPostData()paramsHotLoad 組件。

...
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

啟動腳本

更新 package.jsondev 腳本:

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

總結

上述內容,整體概述了大致的實現邏輯。具體項目落地時,還需考慮一些細節信息,
如:文件更新時希望能夠在命令行提示更的文件名、針對個性化的路由信息調整文件與路由的匹配邏輯等。

Next.js 博客版原文:https://gauliang.github.io/blogs/2022/watch-markdown-files-and-hot-load-the-nextjs-page/

識微見遠 格物致知
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 四、Nginx 4.1、概述 4.1.1、介紹 Nginx是一款輕量級的Web伺服器/反向代理伺服器/電子右鍵(IMAP/POP3)代理伺服器。其特點是占有記憶體少,併發能力強,事實上Nginx的併發能力在同類型的網頁伺服器中表現較好,中國大陸使用Nginx的網站有:百度、就京東、新浪、網易、騰訊、淘 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 CentOS 1.備份原來的源 在控制台輸入mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup備份原本的源 2、下載新的 CentOS-Bas ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 一、環境要求 系統版本:CentOS7.x版本 硬體配置:記憶體2GB以上 cpu2核以上 硬碟大於30G 集群網路配置:集群中所有伺服器內網必須互通,並且需要訪問外網來拉取鏡像 禁用swap分區 二、k8s基礎環境操作: 1、關閉防火牆: [ro ...
  • 程式包編譯安裝的步驟: 源代碼-->預處理-->編譯-->彙編-->鏈接-->執行 多文件:文件中的代碼之間,很可能存在跨文件依賴關係 編譯源碼的項目工具 使用相關的項目管理工具可以大大減少編譯過程的複雜度 根據源碼類型來對這些工具進行分類: C、C++的源碼編譯:使用 make 項目管理器 con ...
  • 1、簡述 binlog 二進位日誌文件,這個文件記錄了MySQL所有的DML操作。通過binlog日誌我們可以做數據恢復,增量備份,主主複製和主從複製等等。 2、Docker中無法使用vim問題解決 https://blog.csdn.net/Tomwildboar/article/details/ ...
  • Android multiple back stacks導航 談談android中多棧導航的幾種實現. 什麼是multiple stacks 當用戶在app里切換頁面時, 會需要向後回退到上一個頁面, 頁面歷史被保存在一個棧里. 在Android里我們經常說"back stack". 有時候在app ...
  • 隨著人們對生活的儀式感的追求,移動設備、可穿戴設備、智能家居設備、車載信息娛樂系統也變得越來越流行。在這些應用上,滑鼠、鍵盤這樣的交互方式不再便捷,而語音作為人類之間最自然的交流方式,語音識別技術儼然已成為各大應用的“標配”。語音識別場景應用十分廣泛,如語音輸入法、語音搜索、實時字幕、游戲娛樂、社交 ...
  • 首先網上很多教程是關於使用swift SMAMB2包的,大部分都是 pod "SMAMB2" 這種方式還是非常簡單的,如果僅僅是這樣還不至於專門寫一篇文章來紀念他。問題就出在我的項目需要用到SMB協議去讀取文件,但是並不是直接讀取! 通俗一點來講就是我的用法和 SMAMB2包提供的用法不同! 那就意 ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
  • 本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。 突然就發現問題了,源碼也不看了,開始改代碼,改了好久。 測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的Lite ...
  • 需求如下,在DEV框架項目中,需要在表格中增加一列顯示圖片,並且能編輯該列圖片,然後進行保存等操作,最終效果如下 這裡使用的是PictureEdit控制項來實現,打開DEV GridControl設計器,在ColumnEdit選擇PictureEdit: 綁定圖片代碼如下: DataTable dtO ...
  • 前兩天微軟偷偷更新了Visual Studio 2022 正式版版本 17.3 發佈,發佈摘要: MAUI 工作負荷 GA 生成 MAUI/Blazor CSS 熱重載支持 現在,你將能夠使用我們的新增功能在 Visual Studio 中使用每個更新試用一系列新功能。 選擇每個功能以瞭解有關特定功 ...
  • 航天和軍工領域的數字化轉型和建設正在積極推進,在與航天二院、航天三院、航天六院、航天九院、無線電廠、兵工廠等單位交流的過程中,用戶更聚焦試驗和生產過程中的痛點,迫切需要解決軟體平臺統一監測和控制設備及軟體與設備協同的問題。 ...
  • .NET 項目預設情況下 日誌是使用的 ILogger 介面,預設提供一下四種日誌記錄程式: 控制台 調試 EventSource EventLog 這四種記錄程式都是預設包含在 .NET 運行時庫中。關於這四種記錄程式的詳細介紹可以直接查看微軟的官方文檔 https://docs.microsof ...
  • 一:背景 上一篇我們聊到瞭如何去找 熱點函數,這一篇我們來看下當你的程式出現了 非托管記憶體泄漏 時如何去尋找可疑的代碼源頭,其實思路很簡單,就是在 HeapAlloc 或者 VirtualAlloc 時做 Hook 攔截,記錄它的調用棧以及分配的記憶體量, PerfView 會將這個 分配量 做成一個 ...
  • 背景 在 CI/CD 流程當中,測試是 CI 中很重要的部分。跟開發人員關係最大的就是單元測試,單元測試編寫完成之後,我們可以使用 IDE 或者 dot cover 等工具獲得單元測試對於業務代碼的覆蓋率。不過我們需要一個獨立的 CLI 工具,這樣我們才能夠在 Jenkins 的 CI 流程集成。 ...
  • 一、應用場景 大家在使用Mybatis進行開發的時候,經常會遇到一種情況:按照月份month將數據放在不同的表裡面,查詢數據的時候需要跟不同的月份month去查詢不同的表。 但是我們都知道,Mybatis是ORM持久層框架,即:實體關係映射,實體Object與資料庫表之間是存在一一對應的映射關係。比 ...
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 深圳市快鴿互聯網科技有限公司 2 ...