博客 / 詳情

返回

基於 VitePress 把 Markdown 編寫的內容構建成靜態頁面並利用 iframe 集成到 Web 頁面的實踐

什麼是 VitePress ?

VitePress 是一個靜態站點生成器 (SSG),專為構建快速、以內容為中心的站點而設計。簡而言之,VitePress 獲取用 Markdown 編寫的內容,對其應用主題,並生成可以輕鬆部署到任何地方的靜態 HTML 頁面。

參考官網

⚠️因為官方網站的原因,此文所有的鏈接地址可能會進入 404 頁面,此時需要我們從官網的根目錄地址進入https://vitejs.cn/vitepress/

VitePress 的配置 ⚙️

配置需要達到的目標

  • 靜態頁面只包含內容區域和文檔側邊欄
  • 靜態頁面路由可以在Web中正常訪問 🔥
  • 根據需求進行基本配置

基本準備

  • Node.js 18 及以上版本
  • 初始準備參考
  1. 項目根目錄安裝 vitepress

    不同的管理工具,有不同的安裝命令,你只需要選擇其中一個即可。

    npm add -D vitepress
    pnpm add -D vitepress
    yarn add -D vitepress
    bun add -D vitepress
  1. vitepress 初始化及安裝嚮導

    不同的管理工具,有不同的安裝命令,你只需要選擇其中一個即可。

    npx vitepress init
    pnpm vitepress init
    yarn vitepress init
    bun vitepress init
    ┌  Welcome to VitePress!
    │
    ◇  Where should VitePress initialize the config?
    │  ./docs // [!code focus] [!code warning]
    │
    ◇  Site title:
    │  My Awesome Project
    │
    ◇  Site description:
    │  A VitePress Site
    │
    ◆  Theme:
    │  ● Default Theme (Out of the box, good-looking docs)
    │  ○ Default Theme + Customization
    │  ○ Custom Theme
    └

    在安裝嚮導過程中需要選擇 ./docs./自定義目錄 中搭建 VitePress 項目,以便與 Web 項目源碼區分開來。

文件配置

我們要知道 config 文件中包含了所需的大部分配置,.md 文件頂部包含部分配置和需要覆蓋 config 文件的配置,也就是説 .md 文件顯示可以做定製化配置。配置參考

  1. config.mtsconfig.tsconfig.js:尤其要關注代碼高亮部分的註釋🔥

    import { defineConfig } from 'vitepress'
    
    // https://vitepress.dev/reference/site-config
    export default defineConfig({
        title: "My Awesome Project",
        description: "A VitePress Site",
        lang: 'zh-CN', // 站點的 lang 屬性
        base: '/markdown/', // 🔥基於主路徑的站點訪問路徑
        outDir: '../public/markdown', // 🔥項目的構建輸出位置,相對於 Vitepress 項目的根目錄
        lastUpdated: true, // 🔥啓用 Git 獲取每個頁面的最後更新時間戳
        markdown: { // 🔥markdown 擴展
            lineNumbers: true, // 代碼塊啓用行號
            math: true, // 支持數學方程,需要下載 markdown-it-mathjax3 插件
            image: { // 默認禁用圖片懶加載
            lazyLoading: true
            },
            // config: (md) =>{
            //   // 使用更多的 Markdown-it 插件!
            //   // md.use(?)
            // }
        },
        themeConfig: {
            outline: { // 🔥文檔側邊欄配置
            level: [2, 6],
            label: '頁面導航'
            },
            // https://vitepress.dev/reference/default-theme-config
            nav: [ // 可忽略配置
            { text: 'Home', link: '/' },
            { text: 'Examples', link: '/markdown-examples' }
            ],
    
            sidebar: [ // 🔥如果需要上下翻頁,則必須把所有的文件配置在裏面,但不拘泥於層級結構
            {
                text: 'Examples',
                items: [
                { text: 'Markdown Examples', link: '/markdown-examples' },
                { text: 'Runtime API Examples', link: '/api-examples' }
                ]
            }
            ],
    
            socialLinks: [
            { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
            ],
    
            docFooter: { // 🔥用於自定義出現在上一頁和下一頁鏈接上方的文本
              prev: '上一篇',
              next: '下一篇'
            }
        }
    })
    
  2. xxx.mdnavbar: false 隱藏導航欄,sidebar: false 隱藏項目側邊欄

    ---
    navbar: false
    sidebar: false
    ---
  3. package.json

    • 在執行初始化 npx vitepress init 命令後,腳本中會自動添加三條 docs 命令。
    • 如果 Web 與 Vitepress 相應程序已配置好,只需要驅動數據就可以更新視圖,則項目打包時可以定義一個複合指令 mdbuild;如果 md 內容發生變化時需要先 yarn docs:build,則不需要複合指令。
    • 相應的指令可根據項目靈活配置,目前配置已滿足此實踐的需求。
    {
     "scripts": {
         "docs:dev": "vitepress dev docs",
         "docs:build": "vitepress build docs",
         "docs:preview": "vitepress preview docs",
         "mdbuild": "yarn docs:build && yarn build"
     }
    }

Web 頁面組件配置 ⚙️

配置需要達到的目標

  • 定義一個展示 Markdown 內容的容器組件
  • 防止 Vitepress 頁面不存在時,iframe 嵌套死循環
  • 響應 Web 頁面主題切換

組件不拘泥於 Vue、React 亦或者原生 html 寫法,以下是以 Vue 組件為例。

假設定義一個 MarkdownViewer.vue 組件:

  • checkUrlExists 方法:判斷頁面地址是否可訪問,以此來防止 iframe 嵌套死循環。
  • changeTheme 方法:通過增刪 dark 類名來實現 Vitepress 頁面的主題切換。
  • 🚀 如果還有展示 wordpdf 等靜態文件可把 iframe 替換或結合 object 標籤使用。
<template>
  <div>
    <button @click="changeTheme">切換主題</button>
    <div v-if="checking" class="loading">檢查頁面是否存在...</div>
    <div v-else-if="pageExists">
      <iframe
        :src="url"
        @load="onLoad"
        @error="onError"
        width="100%"
        height="800"
        frameborder="0"
        ref="iframe"
      ></iframe>
    </div>
    <div v-else class="not-found">
      <h3>頁面不存在</h3>
      <p>請求的頁面 <code>{{ url }}</code> 不存在</p>
      <button @click="$emit('close')">關閉</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'PreCheckedIframe',
  props: {
    url: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      checking: true,
      pageExists: false,
      loadError: false,
      initClassName: '',
    }
  },
  async mounted() {
    await this.checkUrlExists();
  },
  methods: {
    changeTheme() {
      const iframeHtml = this.$refs.iframe?.contentDocument?.querySelector('html')
      if (!iframeHtml) return
      if (!this.initClassName) this.initClassName = iframeHtml.className
      if (iframeHtml.className?.includes('dark')) {
        iframeHtml.className = this.initClassName
      } else {
        iframeHtml.className += ' dark'
      }
      console.log(iframeHtml.className)
    },
    async checkUrlExists() {
      try {
        const response = await fetch(this.url, {
          method: 'HEAD',
          mode: 'no-cors', // 如果是跨域請求
          cache: 'no-cache'
        });
        this.pageExists = response.ok;
      } catch (error) {
        console.error('檢查 URL 失敗:', error);
        this.pageExists = false;
      } finally {
        this.checking = false;
      }
    },
    
    onLoad() {
      console.log('iframe 加載成功');
      this.loadError = false;
    },
    
    onError() {
      console.error('iframe 加載失敗');
      this.loadError = true;
      this.pageExists = false;
    }
  },
  
  watch: {
    async url() {
      this.checking = true;
      await this.checkUrlExists();
    }
  }
}
</script>

到此為止,項目所需的功能已經實現。👇Vitepress 中 Markdown 語法的擴展可可直接參考官網。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.