You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13组件中无需useEffect实现服务端图片模糊占位符

解决方案:在服务端生成模糊占位符(无需useEffect)

针对Next.js 13的场景,推荐两种无需客户端useEffect、完全在服务端处理的方案,既保留SSR优势,又避免plaiceholder增加客户端包体积。


方案一:将StrapiImage改为Async Server Component(App Router 默认支持)

Next.js 13 App Router下的组件默认是Server Component,支持异步写法,可直接在组件内完成模糊占位符的生成,所有逻辑在服务端执行,不会打包到客户端。

import Image from "next/image";
import { getStrapiMedia } from "../../lib/media";
import { getPlaiceholder } from "plaiceholder";

// 注意:不要加 'use client',保持为Server Component
export default async function StrapiImage({ image, ...props }) {
  const imageUrl = getStrapiMedia(image);
  
  // 服务端请求图片二进制数据
  const res = await fetch(imageUrl);
  const buffer = await res.arrayBuffer();
  
  // 生成模糊占位符的base64字符串
  const { base64 } = await getPlaiceholder(Buffer.from(buffer));

  return (
    <Image
      src={imageUrl}
      width={image.attributes.width}
      height={image.attributes.height}
      alt={image.attributes.alternativeText || ""}
      placeholder="blur"
      blurDataURL={base64}
      {...props}
    />
  );
}

注意事项

  • 如果Strapi图片需要权限验证,需在fetch中添加对应凭证(如credentials: 'include'或自定义请求头)
  • 该组件完全在服务端渲染,plaiceholder的代码不会进入客户端包

方案二:在数据获取阶段预生成blurDataURL(适配App/Page Router)

如果你的StrapiImage需要作为客户端组件(比如要添加交互逻辑),可以在页面的异步数据获取环节提前生成所有图片的模糊占位符,再传递给组件使用。

1. 页面端预处理数据(Server Component)

// app/posts/page.js(App Router示例)
import StrapiImage from "@/components/StrapiImage";
import { getStrapiMedia } from "@/lib/media";
import { getPlaiceholder } from "plaiceholder";

export default async function PostsPage() {
  // 从Strapi获取原始数据
  const strapiResponse = await fetch("https://your-strapi-api/posts", { cache: 'force-cache' });
  const postsData = await strapiResponse.json();

  // 批量生成图片的blurDataURL
  const processedPosts = await Promise.all(
    postsData.data.map(async (post) => {
      const image = post.attributes.image;
      const imageUrl = getStrapiMedia(image);
      
      const res = await fetch(imageUrl);
      const buffer = await res.arrayBuffer();
      const { base64 } = await getPlaiceholder(Buffer.from(buffer));

      // 把blurDataURL附加到图片对象上
      return {
        ...post,
        attributes: {
          ...post.attributes,
          image: {
            ...image,
            blurDataURL: base64
          }
        }
      };
    })
  );

  return (
    <div className="post-list">
      {processedPosts.map(post => (
        <StrapiImage key={post.id} image={post.attributes.image} />
      ))}
    </div>
  );
}

2. 简化后的StrapiImage组件(可作为客户端组件)

// components/StrapiImage.jsx
'use client'; // 若需客户端交互则添加,否则可省略
import Image from "next/image";
import { getStrapiMedia } from "../../lib/media";

export default function StrapiImage({ image, ...props }) {
  return (
    <Image
      src={getStrapiMedia(image)}
      width={image.attributes.width}
      height={image.attributes.height}
      alt={image.attributes.alternativeText || ""}
      placeholder="blur"
      blurDataURL={image.attributes.blurDataURL}
      {...props}
    />
  );
}

额外优化建议

  • 封装生成blurDataURL的工具函数,比如async function getBlurData(imageUrl) { ... },避免重复代码
  • 若长期使用,可考虑在Strapi后端提前生成模糊占位符并存储到数据库,减少前端服务端的计算开销

内容的提问来源于stack exchange,提问作者Confuze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:30:57