Skip to content
    在 Opshell 的 Blog 中:
  • 目前已分享 62 篇文章
  • 還有 67 個坑正在填補中
  • 已有: Loading 次觀看
  • 已有: Loading 個人來過
✍️ Opshell
📆 Last Updated:9/17/2024Created:2024/09/17
👀 已被閱讀: Loading

banner16

履歷表最重要的還是工作經歷吧,普遍的操作都是後端 API 拉資料,前端迴圈渲染出來,但是我們 VitePress 專案沒有後端阿~ 總不可能寫死一大片資料在 vue 裡面吧? 也太醜了。 這個部分, VitePress 提供的 Build-Time Data Loading 功能剛好可以解決這個問題:

Build-Time Data Loading

Build-Time Data LoadingVitePress 提供的資料載入功能,他允許載入任意資料,並在 .mdVue Component 引用他,資料的載入只在 build 的時候執行,資料將被序列化成 JSON 放在 Javascript 包中。

資料載入可以被用來接 API 回傳的資料,也可以基於本地文件中生成。

基本用法

一個用於資料載入的文件必須以 .data.js.data.ts 結尾。且必須包含一個 export default 物件,該物件具有 load() 方法:

ts
export default {
    load() {
        return {
            hello: 'world'
        };
    }
};

資料載入功能只在 Node.js 中執行,因此可以按需導入 Node APInpm 依賴。

然後,可以在 .mdVue Component 中使用 data 具名導出從該文件中導入資料:

vue
<script setup>
    import { data } from './example.data.js';
</script>

<pre>
{{ data }}
</pre>

輸出:

json
{
  "hello": "world"
}

不是 export default 了嗎? 那這個 data 是哪來的? 是 VitePress 背後呼叫了 load() 並用名為 data 的具名export 隱式地暴露了結果。

就算是異步進行,也是可以的:

ts
export default {
    async load() {
        // 透過 API 取的資料
        return (await fetch('...')).json();
    }
};

實際使用

是的各位,基本用法已經滿足了我們的使用需求,首先我們在 docs/data/ 中建立檔案 works.data.ts,然後輸入內容:

ts
export default {
    load() {
        return [
            {
                compImg: 'images/resume/ai4dt.png',
                company: '不正常人類研究中心',
                location: 'Tainan, Taiwan',
                jobTitle: 'Front-End Developer',
                period: '2023.09 - Now',
                description: `一、工作內容概述:<br />......`
            }
        ];
    }
};

然後在 resume.vue 中使用他:

vue
<script setup lang="ts">
    import { data as workExperienceData } from '@/data/works.data';
</script>

<template>
    <div class="work-experience-block">
        <MoleWorkExperience
            v-for="work in workExperienceData"
            :key="work.company"
            :comp-img="withBase(work.compImg)"
            :company="work.company"
            :location="work.location"
            :job-title="work.jobTitle"
            :period="work.period"
            :description="work.description"
        />
    </div>
</template>

會發現 import { data as workExperienceData } from '@/data/works.data'; 報錯了:

模組 '"@/data/skills.data"' 沒有匯出的成員 'data'。您是要改用 'import data from "@/data/skills.data"' 嗎?ts-plugin(2614)

這是因為我們沒有為 works.data 設定 loaderdataexport 類型,依照官網範例我們改寫成這樣:

ts
import { defineLoader } from 'vitepress'

export interface Data {
    compImg: string
    company: string
    location: string
    jobTitle: string
    period: string
    description: string
}

declare const data: Data[]
export { data }

export default defineLoader({
    load() {
        ...
    }
});

恩很棒,最基本的資料引用就完成囉~ 一個簡單的 resume 也完成了~

備註

Resume 製作的這三天幾乎沒有看見任何的 CSS 程式碼,這不意味著我們是使用 VitePress 的預設主題,而是 Opshell 覺得排版非常的簡單,所以就不把 CSS 貼出來水版面了。

Data from Local Files(本地文件資料生成)

前面提到的方式,基本上是靜態資料,只有在 build 的時候把資料灌進來,而且 build 完之後,是不會看到 .data.ts 的原始檔案的,這樣的話每次要改資料都要重新 build 一次,有沒有其他的方式可以隨著文件的修改來渲染新的資料呢?

有的 就是我們現在要用到 Data from Local Files

當需要基於本地文件生成資料時,需要在 data loader 中使用 watch 選項,以便這些文件修改時可以觸發熱更新。

watch 選項也很方便,因為可以使用 glob 模式 匹配多個文件。模式可以相對於資料加載文件本身,load() 函數將接收匹配文件的絕對路徑。

下面的例子展示如何使用 csv-parse 加載 CSV 文件並將其轉換為 JSON。因為此文件僅在 build 時執行,因此不會將 CSV 解析器發送到客户端。

js
import fs from 'node:fs'
import { parse } from 'csv-parse/sync'

export default {
  watch: ['./data/*.csv'],
  load(watchedFiles) {
    // watchFiles 是一个所匹配文件的绝对路径的数组。
    // 生成一个博客文章元数据数组
    // 可用于在主题布局中呈现列表。
    return watchedFiles.map((file) => {
      return parse(fs.readFileSync(file, 'utf-8'), {
        columns: true,
        skip_empty_lines: true
      })
    })
  }
}

Released under the MIT License.