iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
自我挑戰組

30 天 vueuse 原始碼閱讀與實作系列 第 29

[Day 29] 使用 VitePress 生成 VueUse 文件頁面 - Part 2

  • 分享至 

  • xImage
  •  

昨天主要提到一些共用的頁面和模組,今天來看看 VueUse API 頁面的文件是怎麼透過 VitePress 設定的。

style

樣式設定的部分,主要是 import 到 .vitepress/theme/index.ts,原始碼位置:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/theme/index.ts

其中一個程式碼片段:

import './styles/main.css'
import './styles/demo.css'
import './styles/utils.css'
import './styles/vars.css'
import './styles/overrides.css'
import 'uno.css'

除了 uno.css,引入的 css 檔案都放在 .vitepress/theme/styles 這個目錄中。跟 VitePress 文件中的說明一樣。

另外 VueUse 的 Demo 有用到 UnoCSS 的 class,所以來看一下要怎麼在 VitePress 中設定 UnoCSS。

VitePress 的設定檔:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/config.ts#L250

vite: viteConfig

其中有這個跟 Vite 相關的設定,接著找到 viteConfig:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/vite.config.ts

設定的方式跟一般在 Vue 專案中使用 UnoCSS 是一樣的。

因為我的目標是要復刻 VueUse 文件,所以實作就直接複製需要的樣式與設定,像是 unocss.config.ts 就直接搬來用,還有 .vitepress/theme/styles 裡面的幾支檔案也是。

這邊放上我實作的簡易版本,因為沒有要用到 PWA 跟其他設定,所以單純很多:

// projects/.vitepress/config.mjs

import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vitepress'

export default defineConfig({
  base: '/rhino-notebook/',
  title: 'Rhino Notebook',
  description: 'Rhino Notebook',
  themeConfig: {
    // ...略
  },
  vite: {
    plugins: [
      UnoCSS(),
    ],
  },
})

theme component

昨天最後有提到 VueUse Functions 頁面是由 FunctionsList 這個組件做呈現的,這個組件跟其他 theme component 一樣都放在這個位置:https://github.com/vueuse/vueuse/tree/main/packages/.vitepress/theme/components

我目前有用到 BooleanDisplay.vueDemoContainer.vueNote.vue 這三個。

VueUse 有針對 theme component 做 auto import 的設定,舉例來說,我們來看 useScreenOrientation 的 Demo:

// packages/core/useScreenOrientation/demo.vue

<script setup lang="ts">
import { useScreenOrientation } from '.'

const { isSupported, orientation, angle } = useScreenOrientation()
</script>

<template>
  <note class="mb-2">
    For best results, please use a mobile or tablet device (or use your browser's native inspector to simulate an
    orientation change)
  </note>
  <div>
    isSupported: <boolean-display :value="isSupported">
      {{ isSupported }}
    </boolean-display>
  </div>
  <div>Orientation Type: <b>{{ orientation }}</b></div>
  <div>Orientation Angle: <b>{{ angle }}</b></div>
</template>

會發現 note 這個組件在最上方沒有 import,可以直接使用。
auto import 跟之前提到的 UnoCSS 一樣,都是用 vite plugin 的方式做設定:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/vite.config.ts#L36 ,看一下原始碼的這個片段:

import Components from 'unplugin-vue-components/vite'
import { resolve } from 'node:path'

// ... 略

// plugins
Components({
  dirs: resolve(__dirname, 'theme/components'),
  include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
  resolvers: [
    IconsResolver({
      componentPrefix: '',
    }),
  ],
  dts: resolve(__dirname, 'components.d.ts'),
  transformer: 'vue3',
}),

這邊需要額外安裝 unplugin-vue-components 這個套件。

YAML

以 useMouse 的官方 Demo 為例,畫面上有 xysourceType,這些在原始碼中,本來是一個 reactive 的物件,最後被轉換為 YAML 格式,變成我們看到的樣子,來看一下官方 Demo 的程式碼片段:

<script setup lang="ts">
import type { UseMouseEventExtractor } from '@vueuse/core'
import { useMouse, useParentElement } from '@vueuse/core'
import { stringify } from '@vueuse/docs-utils'
import { reactive } from 'vue'

const parentEl = useParentElement()

const mouseDefault = reactive(useMouse())
const textDefault = stringify(mouseDefault)

const extractor: UseMouseEventExtractor = (event) => {
  if (typeof Touch !== 'undefined' && event instanceof Touch)
    return null
  else
    return [event.offsetX, event.offsetY]
}

const mouseWithExtractor = reactive(useMouse({ target: parentEl, type: extractor }))
const textWithExtractor = stringify(mouseWithExtractor)
</script>

<template>
  <p>Basic Usage</p>
  <pre lang="yaml">{{ textDefault }}</pre>
  <p>Extractor Usage</p>
  <pre lang="yaml">{{ textWithExtractor }}</pre>
</template>

裡面有用到 reactify 這個方法,這個跟 VitePress 比較無關就先不細看,看起來是在轉換成 YAML 的同時,也必須保持響應性,這樣我們在跟 Demo 畫面互動的時候,才能看到即時更新的數值。

Demo.vue 組件在哪裡被引入?

先貼上我實作的版本,一樣用 useMouse 當範例,以下是 useMouse 的 index.md:

---
category: Sensors
---

# useMouse

Reactive mouse position

## Demo

<script setup>
import Demo from './demo.vue'
</script>

This is a .md using a custom component

<DemoContainer>
  <Demo />
</DemoContainer>

## Basic Usage

// ... 略

部署到線上的部落格:https://rhinolee.github.io/rhino-notebook/front-end/vueuse/core/useMouse/

畫面上 Demo 的部分跟 VueUse 長得差不多。
接著來看 VueUse useMouse index.md 的原始碼:

---
category: Sensors
---

# useMouse

Reactive mouse position

## Basic Usage

// ... 略

這裡並沒有引入 Demo.vue,也沒有使用 <Demo /> 把 Demo 組件放到畫面上,那為什麼畫面上一樣會出現 Demo 區塊?

後來看原始碼發現這邊客製化了一個 Plugin,先看設定的最源頭:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/vite.config.ts#L31

可以看到在 Vite plugins 中設定了 MarkdownTransform()MarkdownTransform 的原始碼位置:https://github.com/vueuse/vueuse/blob/main/packages/.vitepress/plugins/markdownTransform.ts

其中用到了 getFunctionMarkdown 這個 function,其中有一段就包含剛剛在找的 Demo Section。

如果使用我的版本,那每個文件檔的 Markdown 都需要引入 Demo 並使用它,這部分不像其他描述部分會變動,Demo 這個區塊的寫法都一樣,很適合做自動化。看原始碼也會發現 VueUse API 文件下方的 ContributorsChangelog 也都是用自動化的方式實現,不會出在每個文件 Markdown 中。


我的 VitePress GitHub repository:https://github.com/RhinoLee/rhino-notebook

我的 VitePress Demo:https://rhinolee.github.io/rhino-notebook/front-end/vueuse/core/useMouseInElement/

這兩天大概看了一下 VueUse 是怎麼透過 VitePress 這套工具生成官方文件的,並實作了一個自己的版本,但現在上面只有復刻 VueUse 的東西,連主題色都一起仿冒過去了 XD,這部分之後會做一些調整。

最後面有看到 VueUse 是怎麼透過客製 Plugin ,來處理一些適合被自動化的區塊,這個未來應該會用到,到時候再回來更詳細的研究。另外 YAML 那招滿好用的,比起直接顯示物件格式,用 YAML 格式好看很多。

VitePress 相關的原始碼就到這邊告一段落~


上一篇
[Day 28] 使用 VitePress 生成 VueUse 文件頁面 - Part 1
下一篇
[Day 30] 結語
系列文
30 天 vueuse 原始碼閱讀與實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言