Skip to content

与 Next.js 集成

Velite 是一个框架无关的库,可用于任何 JavaScript 框架或库,包括 Next.js。

以下是一些方案,可帮助您更好地将 Velite 与 Next.js 集成。

🎊 使用 Next.js 配置启动 Velite 🆕

Next.js 正逐步采用 Turbopack,因为它速度显著更快。然而,Turbopack 与 Webpack 生态系统不完全兼容,这意味着启用 Turbopack 时,VeliteWebpackPlugin 无法正常工作。以下是一个全新的解决方案。

ts
import type { NextConfig } from 'next'

const isDev = process.argv.indexOf('dev') !== -1
const isBuild = process.argv.indexOf('build') !== -1
if (!process.env.VELITE_STARTED && (isDev || isBuild)) {
  process.env.VELITE_STARTED = '1'
  import('velite').then(m => m.build({ watch: isDev, clean: !isDev }))
}

const nextConfig: NextConfig = {
  /* config options here */
}

export default nextConfig
js
const isDev = process.argv.indexOf('dev') !== -1
const isBuild = process.argv.indexOf('build') !== -1
if (!process.env.VELITE_STARTED && (isDev || isBuild)) {
  process.env.VELITE_STARTED = '1'
  const { build } = await import('velite')
  await build({ watch: isDev, clean: !isDev })
}

/** @type {import('next').NextConfig} */
export default {
  // next config here...
}

请注意,此方法使用了顶级 await,因此仅支持 next.config.mjs 或启用了 ESM 的情况。

使用 Next.js Webpack 插件启动 Velite

您可以使用 Next.js 插件调用 Velite 的编程 API,以更好地集成并启动 Velite。

next.config.js 中:

js
/** @type {import('next').NextConfig} */
module.exports = {
  // othor next config here...
  webpack: config => {
    config.plugins.push(new VeliteWebpackPlugin())
    return config
  }
}

class VeliteWebpackPlugin {
  static started = false
  apply(/** @type {import('webpack').Compiler} */ compiler) {
    // executed three times in nextjs
    // twice for the server (nodejs / edge runtime) and once for the client
    compiler.hooks.beforeCompile.tapPromise('VeliteWebpackPlugin', async () => {
      if (VeliteWebpackPlugin.started) return
      VeliteWebpackPlugin.started = true
      const dev = compiler.options.mode === 'development'
      const { build } = await import('velite')
      await build({ watch: dev, clean: !dev })
    })
  }
}
js
import { build } from 'velite'

/** @type {import('next').NextConfig} */
export default {
  // othor next config here...
  webpack: config => {
    config.plugins.push(new VeliteWebpackPlugin())
    return config
  }
}

class VeliteWebpackPlugin {
  static started = false
  apply(/** @type {import('webpack').Compiler} */ compiler) {
    // executed three times in nextjs
    // twice for the server (nodejs / edge runtime) and once for the client
    compiler.hooks.beforeCompile.tapPromise('VeliteWebpackPlugin', async () => {
      if (VeliteWebpackPlugin.started) return
      VeliteWebpackPlugin.started = true
      const dev = compiler.options.mode === 'development'
      await build({ watch: dev, clean: !dev })
    })
  }
}

INFO

ESM import { build } from 'velite'next build 过程中可能会收到 [webpack.cache.PackFileCacheStrategy/webpack.FileSystemInfo] 警告,影响不大。 请参考 https://github.com/webpack/webpack/pull/15688

👆

在 npm 脚本中使用 npm-run-all 启动 Velite:

INFO

推荐使用 VeliteWebpackPlugin,但如果您的项目部署在 Vercel 上,可能会出现 free(): invalid sizemunmap_chunk(): invalid pointer 错误,这通常与 sharp 模块相关。请参考:https://github.com/zce/velite/issues/52#issuecomment-2016789204

package.json

json
{
  "scripts": {
    "dev:content": "velite --watch",
    "build:content": "velite --clean",
    "dev:next": "next dev",
    "build:next": "next build",
    "dev": "run-p dev:*",
    "build": "run-s build:*",
    "start": "next start"
  }
}

类型化路由

当您使用 typedRoutes 实验性功能时,可以在 Next.js 应用中获取类型化路由。

在这种情况下,您可以为相关模式指定更具体的类型,以便在 next/linknext/router 上更轻松地使用。

例如:

ts
import type { Route } from 'next'
import type { Schema } from 'velite'

const options = defineCollection({
  // ...
  schema: s.object({
    // ...
    link: z.string() as Schema<Route<'/posts/${string}'>>
  })
})

然后您可以像这样使用它:

tsx
import Link from 'next/link'

import { options } from '@/.velite'

const Post = async () => {
  return (
    <div>
      {/* typed route */}
      <Link href={options.link}>阅读更多</Link>
    </div>
  )
}

示例

Distributed under the MIT License.