欢迎光临
我们一直在努力

Next.js Web3 社交应用前端:Lens Protocol 集成、动态 Feed 流与去中心化存储

Next.js Web3 社交应用前端:Lens Protocol 集成、动态 Feed 流与去中心化存储

一、Web3 社交前端不是 Web2 套个钱包

Web3 社交应用的前端面临三个和传统社交完全不同的技术约束:数据源不是自己的服务器(来自链上 + 去中心化存储)、用户身份不是邮箱(是钱包地址和 DID)、内容分发不是算法黑箱(用户可以选择自己的推荐算法)。这三个约束不是限制,而是新架构的机会。

Lens Protocol 是目前覆盖最广的链上社交图谱标准。每一条帖子、关注关系、转发记录都以 NFT(Profile NFT 和 Publication NFT)的形式存储在 Polygon 上。前端的工作不是去爬取和索引这些数据,而是通过 Lens API(GraphQL 端点)直接查询图谱并渲染。

但这里有一个架构决策需要提前想清楚:用户的内容(帖子正文、图片、视频)并不存储在链上,而是通过 ContentURI 指向 IPFS 或 Arweave 上的文件。这意味着前端需要同时处理两个数据通道——链上交易(发帖、评论、收藏)和去中心化存储上传(内容文件 + 元数据 JSON)。两个通道的事务性不能保证:如果用户签名了链上发帖交易但 IPFS 上传失败,链上会有一条 ContentURI 指向不存在的内容;反之如果 IPFS 上传成功但链上交易被拒绝,内容文件就成了孤儿数据。

Next.js 的 App Router 模式很适合这个场景——服务器组件处理数据查询,客户端组件处理交互和钱包连接,两者通过串行数据流(从链上查到 UI 渲染)自然分工。

二、Lens 数据流与前端架构

Lens 的数据模型是 Graph + Publication 的混合结构。Profile 是图的节点,Follow 和 Collect 是边,而 Publication(Post、Comment、Mirror)是挂载在 Profile 上的内容叶子节点。

Lens API 是核心数据管道。它本质上是一个索引器——监听 Lens Hub 合约的链上事件,将图数据和内容重组为 GraphQL Schema,然后通过 CDN 分发查询结果。前端不需要跑自己的索引节点,但需要对查询做合理的分页(Cursor-based Pagination)和缓存(Apollo Client 或 SWR)。

内容存储采用两层架构:元数据(标题、摘要、标签、创建时间)存储在 IPFS 的 JSON 文件中,实质内容(图片、视频、长文)存储在 Arweave 的 permaweb 上。IPFS 的可寻址性(CID 即内容指纹)确保了元数据的完整性,Arweave 的一次付费永存确保了内容不会因为 IPFS 节点退出而丢失。

三、Next.js + Lens 的工程实现

// app/api/lens/route.ts — Lens GraphQL 代理层
import { NextRequest, NextResponse } from 'next/server';
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const LENS_API = 'https://api-v2.lens.dev/';

const client = new ApolloClient({
uri: LENS_API,
cache: new InMemoryCache(),
});

/**
* 获取用户个人 Feed 的 GraphQL 查询
*
* 设计决策:使用 forward-pagination 而非 offset-pagination,
* 因为 Lens 的出版物列表会新增内容(插入到列表头部),
* offset 分页在新增内容后会导致重复项。Cursor 分页基于时间戳,
* 天然避免了这个问题。
*/
const FEED_QUERY = gql`
query Feed($profileId: ProfileId!, $cursor: Cursor) {
feed(request: {
where: { for: $profileId }
cursor: $cursor
}) {
items {
id
root {
… on Post {
id
metadata { content locale tags }
stats { comments mirrors upvotes }
createdAt
}
}
electedMirror { id }
}
pageInfo { prev next }
}
}
`;

/**
* API Route: 代理 Lens 查询并添加服务端缓存
*
* 设计决策:不直接从客户端调用 Lens API,而是通过 Next.js API Route 代理。
* 理由有三:1) 可以在服务端缓存热门查询,减少 API 调用;
* 2) 可以在代理层添加速率限制和滥用防护;
* 3) 客户端的 API key 不会暴露。
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const profileId = searchParams.get('profileId');
const cursor = searchParams.get('cursor');

if (!profileId) {
return NextResponse.json({ error: 'profileId required' }, { status: 400 });
}

try {
const { data } = await client.query({
query: FEED_QUERY,
variables: { profileId, cursor },
});
return NextResponse.json(data.feed);
} catch (error) {
return NextResponse.json(
{ error: 'Lens API query failed' },
{ status: 502 }
);
}
}

// components/FeedList.tsx — 客户端 Feed 渲染组件
'use client';

import { useInfiniteQuery } from '@tanstack/react-query';
import { useAccount } from 'wagmi';
import { LensPublication } from '@/types/lens';

async function fetchFeed(pageParam: string | undefined): Promise<{
items: LensPublication[];
pageInfo: { next: string | null };
}> {
const params = new URLSearchParams();
if (pageParam) params.set('cursor', pageParam);
// profileId 从钱包上下文传入
const res = await fetch(`/api/lens?profileId=0xabcd&${params}`);
if (!res.ok) throw new Error('Feed fetch failed');
return res.json();
}

/**
* Feed 无限滚动组件
*
* 设计决策:使用 @tanstack/react-query 的 useInfiniteQuery 而非
* 自己管理分页状态。它的 getNextPageParam 配合 cursor 分页天然适配
* 动态更新的 Feed 场景——每次加载数据都基于上一页的末尾游标。
* SWR 也可以做类似的事情,但 useInfiniteQuery 的双向缓存策略
* 对社交 Feed 的滚动体验更友好。
*/
export function FeedList() {
const { address } = useAccount();

const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({
queryKey: ['feed', address],
queryFn: ({ pageParam }) => fetchFeed(pageParam as string | undefined),
getNextPageParam: (lastPage) => lastPage.pageInfo.next ?? undefined,
initialPageParam: undefined,
staleTime: 30_000, // 30秒内不重新获取,减少 API 调用
});

return (
<div className="feed-container">
{data?.pages.map((page) =>
page.items.map((pub) => (
<PublicationCard key={pub.id} publication={pub} />
))
)}
{hasNextPage && (
<button
onClick={() => fetchNextPage()}
disabled={isFetching}
className="load-more-btn"
>
{isFetching ? '加载中…' : '加载更多'}
</button>
)}
</div>
);
}

// utils/storage.ts — 去中心化存储工具
import { Web3Storage } from 'web3.storage';

const storageClient = new Web3Storage({ token: process.env.WEB3_STORAGE_TOKEN! });

/**
* 将发布内容上传到 IPFS
*
* 设计决策:只上传文本内容的 JSON 到 IPFS,图片和视频走 Arweave。
* IPFS 的主要优势是 CIDs 作为内容哈希的完整性保证,
* Arweave 的主要优势是永久存储不需要 pinning 服务维护。
* 两层分离避免了单一存储方案的弱点。
*/
export async function uploadToDecentralizedStorage(
content: { text: string; tags: string[]; locale: string }
): Promise<string> {
const metadata = JSON.stringify({
version: '2.0.0',
mainContentFocus: 'TEXT_ONLY',
metadata_id: crypto.randomUUID(),
description: 'Lens Publication',
content: content.text,
locale: content.locale,
tags: content.tags,
appId: 'your-app-id',
});

const blob = new Blob([metadata], { type: 'application/json' });
const file = new File([blob], 'metadata.json');
const cid = await storageClient.put([file], {
wrapWithDirectory: false,
});

return `ipfs://${cid}`;
}

四、这个架构的边界

Lens API 的依赖风险:Lens API 目前由 Lens 团队维护,是一个中心化的查询端点。虽然链上数据本身是去中心化的,但索引和查询服务是中心化的。如果 API 下线,前端需要回退到直接扫描链上事件 + 本地索引的方案——技术可行但工程量大。

Feed 的个性化不够:当前的 Lens Feed 只展示关注者的帖子,按时间排序。它没有"你可能感兴趣"的推荐层。扩展方向是在 Feed 查询之外接入 GNN 推荐模型(如第 1 篇所述),将链上行为图谱的嵌入向量作为 Feed 排序的权重因子。

钱包体验的摩擦:Web3 社交的每次发帖、评论、转发都是一笔链上交易,需要钱包签名和 Gas 确认。高频社交场景下,每 30 秒弹一次签名确认是不可接受的用户体验。解决方案是使用 Session Key + 无 Gas 中继器(Gasless Relayer),让用户在首次授权后在后台完成交易。

数据主权与内容审核的矛盾:去中心化存储保证了用户数据的不可篡改性和抗审查性,但也意味着平台无法删除用户上传的违规内容(恶意代码、非法信息、侵权材料)。Lens Protocol 的做法是前端层面的"内容过滤"(通过 Momoka 的 DA 层做内容校验),但这实际上在应用层引入了一个中心化的审查节点。这是 Web3 社交的一个根本矛盾——绝对的去中心化数据主权与最小必要的内容审核无法兼得。目前的最佳实践是:链上存储内容指针和权限,链下存储可变的 moderation 标记(不可见的"隐藏"标记),让审核不意味着数据删除。

五、总结

Next.js + Lens Protocol 的前端架构核心是把"数据查询"和"数据写入"拆到两个通道:查询走 Lens API 的 GraphQL 端点(服务端代理 + 缓存),写入走钱包签名的链上交易(客户端交互流)。去中心化存储作为内容层,IPFS 保证完整性,Arweave 保证持久性。

这个架构的精髓在于:前端不是在做 Web2 的"展示层",而是在做 Web3 的"状态转换层"。每一个 UI 操作对应一个链上状态变化,前端负责把状态变化翻译成用户能理解的交互,而不是把用户困在复杂的链上操作中。

赞(0)
未经允许不得转载:171主机测评 » Next.js Web3 社交应用前端:Lens Protocol 集成、动态 Feed 流与去中心化存储
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址