Skip to content

在应用中使用内容集合

Velite 会将您的内容构建为 JSON 文件,并为 TypeScript 生成类型推断,您可以放心地在应用中使用输出的数据。

输出结构

diff
 root
+├── .velite
+│   ├── posts.json                  # posts 集合输出
+│   └── others.json                 # others 集合输出
 ├── content
 │   ├── posts
 │   │   ├── hello-world.md
 │   │   └── hello-world-2.md
 │   └── others
 ├── public
+│   └── static
+│       ├── cover-2a4138dh.jpg      # 来自 frontmatter 引用
+│       ├── img-2hd8f3sd.jpg        # 来自内容引用
+│       ├── plain-37d62h1s.txt      # 来自内容引用
+│       └── video-72hhd9f.mp4       # 来自 frontmatter 引用
 ├── package.json
 └── velite.config.js

.velite 目录中,Velite 会为每个集合生成输出文件,以及供您应用程序使用的 index.jsindex.d.ts 文件。

js
export { default as posts } from './posts.json'
export { default as others } from './others.json'
js
import type __vc from '../velite.config.js'

type Collections = typeof __vc.collections

export type Post = Collections['posts']['schema']['_output']
export declare const posts: Post[]

export type Other = Collections['others']['schema']['_output']
export declare const others: Other[]
json
[
  {
    "title": "Hello world",
    "slug": "hello-world",
    "date": "1992-02-25T13:22:00.000Z",
    "cover": {
      "src": "/static/cover-2a4138dh.jpg",
      "height": 1100,
      "width": 1650,
      "blurDataURL": "data:image/webp;base64,UklGRjwAAABXRUJQVlA4IDAAAACwAQCdASoIAAUADMDOJbACdADWaUXAAMltC0BZxTv24bHUX8EibgVs/sPiTqq6QAA=",
      "blurWidth": 8,
      "blurHeight": 5
    },
    "video": "/static/video-72hhd9f.mp4",
    "metadata": {
      "readingTime": 1,
      "wordCount": 1
    },
    "excerpt": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse",
    "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse</p>\n<p><img src=\"/static/img-2hd8f3sd.jpg\" alt=\"some image\" /></p>\n<p><a href=\"/static/plain-37d62h1s.txt\">link to file</a></p>\n",
    "permalink": "/blog/hello-world"
  }
]
json
[
  ...
]

TIP

如果您使用 Git 进行版本控制,我们建议通过将 .velite 添加到您的 .gitignore 中来忽略该目录。这会告诉 Git 忽略此目录及其中的任何文件。

sh
echo '\n.velite' >> .gitignore

在项目中使用

以下是在 Next.js 项目中使用输出数据的示例。

tsx
import { notFound } from 'next/navigation'

import { posts } from './.velite'

interface PostProps {
  params: {
    slug: string
  }
}

function getPostBySlug(slug: string) {
  return posts.find(post => post.slug === slug)
}

export default function PostPage({ params }: PostProps) {
  const post = getPostBySlug(params.slug)
  if (post == null) notFound()
  return (
    <article className="prose dark:prose-invert py-6">
      <h1 className="mb-2">{post.title}</h1>
      {post.description && <p className="mt-0 text-xl text-slate-700 dark:text-slate-200">{post.description}</p>}
      <hr className="my-4" />
      <div className="prose" dangerouslySetInnerHTML={{ __html: post.content }}></div>
    </article>
  )
}

export function generateMetadata({ params }: PostProps) {
  const post = getPostBySlug(params.slug)
  if (post == null) return {}
  return { title: post.title, description: post.description }
}

export function generateStaticParams() {
  return posts.map(({ slug }) => ({ slug }))
}

数据访问器

由于每个用户的使用场景不同,Velite 是与框架无关的,并且不希望规定用户内容的结构或如何使用它生成的输出。因此,Velite 没有内置与数据访问相关的 API。

您可以按自己喜欢的方式在应用程序中使用输出数据,例如使用函数通过 slug 获取单篇文章,或使用函数通过类别获取文章列表。

ts
import { authors, posts } from '../.velite'

import type { Author, Post } from '../.velite'

export const getPostBySlug = (slug: string) => {
  return posts.find(post => post.slug === slug)
}

export const getPostsByCategory = (category: string) => {
  return posts.filter(post => post.category === category)
}

export const getAuthors = async <F extends keyof Author>(
  filter: Filter<Author>,
  fields?: F[],
  limit: number = Infinity,
  offset: number = 0
): Promise<Pick<Author, F>[]> => {
  return authors
    .filter(filter)
    .sort((a, b) => (a.name > b.name ? -1 : 1))
    .slice(offset, offset + limit)
    .map(author => pick(author, fields))
}

export const getAuthorsCount = async (filter: Filter<Author> = filters.none): Promise<number> => {
  return authors.filter(filter).length
}

export const getAuthor = async <F extends keyof Author>(filter: Filter<Author>, fields?: F[]): Promise<Pick<Author, F> | undefined> => {
  const author = authors.find(filter)
  return author && pick(author, fields)
}

export const getAuthorByName = async <F extends keyof Author>(name: string, fields?: F[]): Promise<Pick<Author, F> | undefined> => {
  return getAuthor(i => i.name === name, fields)
}

export const getAuthorBySlug = async <F extends keyof Author>(slug: string, fields?: F[]): Promise<Pick<Author, F> | undefined> => {
  return getAuthor(i => i.slug === slug, fields)
}

简而言之,它只是原始的 JSON 数据,您可以用任何您想要的方式使用它。

路径别名

您可以在 tsconfig.json 中定义路径别名:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "#site/content": ["./.velite"]
    }
  }
}

然后您就可以在项目中导入输出文件:

tsx
import { posts } from '#site/content'

// ...

Distributed under the MIT License.