进入
登入你的账号看下图设置

npx create-next-app road-to-lens


提示这个就可以下一步了

cd road-to-lens

npm install @apollo/client graphql

有下方提示后即安装成功

npm run dev

有下图右上角的显示后就是验证成功接下来下一步

新建apollo-client.js
我们在当前项目新建一个apollo-client.js

创建完文件后点击打开

然后输入下方代码
// ./apollo-client.js
import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
uri: "https://api.lens.dev",
cache: new InMemoryCache(),
});
export default client;
修改我们的/pages/_app.js
先找到文件打开

然后输入下方代码:
// pages/_app.js
import '../styles/globals.css'
import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client";
function MyApp({ Component, pageProps }) {
return (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
}
export default MyApp
先找到文件打开

输入下方代码:
import { useQuery, gql } from "@apollo/client";
import recommendedProfilesQuery from '../queries/recommendedProfilesQuery.js';
import Profile from '../components/Profile.js';
export default function Home() {
const {loading, error, data} = useQuery(recommendedProfilesQuery);
if (loading) return 'Loading..';
if (error) return `Error! ${error.message}`;
return (
<div>
{data.recommendedProfiles?.map((profile, index) => {
console.log(`Profile ${index}:`, profile);
return <Profile key={profile.id} profile={profile} displayFullProfile={false} />;
})}
</div>
)
}
mkdir queries
touch queries/recommendedProfilesQuery.js
先点加号新建一个Shell.


先定位目录输入下方代码:
cd road-to-lens

然后输入下方命令一共两条,会创建一个新的文件夹和文件(如果使用VScode第二条 touch代码需要在CMD使用)
mkdir queries
touch queries/recommendedProfilesQuery.js

将下面的代码写入recommendedProfilesQuery.js
// queries/recommendedProfilesQuery.js
import {gql} from '@apollo/client';
export default gql`
query RecommendedProfiles {
recommendedProfiles {
id
name
bio
attributes {
displayType
traitType
key
value
}
followNftAddress
metadata
isDefault
picture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
url
mimeType
}
}
__typename
}
handle
coverPicture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
url
mimeType
}
}
__typename
}
ownedBy
dispatcher {
address
canUseRelay
}
stats {
totalFollowers
totalFollowing
totalPosts
totalComments
totalMirrors
totalPublications
totalCollects
}
followModule {
... on FeeFollowModuleSettings {
type
amount {
asset {
symbol
name
decimals
address
}
value
}
recipient
}
... on ProfileFollowModuleSettings {
type
}
... on RevertFollowModuleSettings {
type
}
}
}
}
`;
和上一步一样(不需要新建Shell)直接输入下方代码(如果使用VScode第二条 touch代码需要在CMD使用)
mkdir components
touch components/Profile.js
同样创建了一个文件夹和一个文件

将下面代码写入Profile.js
// components/Profile.js
import Link from "next/link";
export default function Profile(props) {
const profile = props.profile;
// When displayFullProfile is true, we show more info.
const displayFullProfile = props.displayFullProfile;
return (
<div className="p-8">
<Link href={`/profile/${profile.id}`}>
<div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
<div className="md:flex">
<div className="md:shrink-0">
{profile.picture ? (
<img
src={
profile.picture.original
? profile.picture.original.url
: profile.picture.uri
}
className="h-48 w-full object-cover md:h-full md:w-48"
/>
) : (
<div
style={{
backgrondColor: "gray",
}}
className="h-48 w-full object-cover md:h-full md:w-48"
/>
)}
</div>
<div className="p-8">
<div className="uppercase tracking-wide text-sm text-indigo-500 font-semibold">
{profile.handle}
{displayFullProfile &&
" (" + profile.name + ")"}
</div>
<div className="block mt-1 text-sm leading-tight font-medium text-black hover:underline">
{profile.bio}
</div>
<div className="mt-2 text-sm text-slate-900">{profile.ownedBy}</div>
<p className="mt-2 text-xs text-slate-500">
following: {profile.stats.totalFollowing} followers:{" "}
{profile.stats.totalFollowers}
</p>
</div>
</div>
</div>
</Link>
</div>
);
}
npm run dev

验证成功后右上角会出现一张图片随机的

我们将项目运行起来出现下面的结果说明你已经离成功不远了,但是现在页面的样式太丑了,让我们来优化下。
路径代码:
cd road-to-lens
安装 Tailwind
npm install -D tailwindcss postcss autoprefixer
当你执行完下面这个命令,系统会给你生成一个tailwind.config.js
npx tailwindcss init -p
出现选择项直接回车

出现下方提示后输入第二个代码,同样出现选项直接回车

会生成一个文件

在生成的tailwind.config.js中写入以下代码:
// tailwind.config.js
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

用以下内容替换文件内容。
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}
/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
再次验证一下大概会出现这种界面
验证代码:
npm run dev


然后打开文件输入以下代码:
import { useQuery, useMutation } from "@apollo/client";
import { useRouter } from "next/router";
import fetchProfileQuery from "../../queries/fetchProfileQuery.js";
import Profile from "../../components/Profile.js";
import Post from "../../components/Post.js";
export default function ProfilePage() {
const router = useRouter();
const { id } = router.query;
console.log("fetching profile for", id);
const { loading, error, data } = useQuery(fetchProfileQuery, {
variables: {
request: { profileId: id },
publicationsRequest: {
profileId: id,
publicationTypes: ["POST"],
},
},
});
if (loading) return "Loading..";
if (error) return `Error! ${error.message}`;
return (
<div className="flex flex-col p-8 items-center">
<Profile profile={data.profile} displayFullProfile={true} />
{data.publications.items.map((post, idx) => {
return <Post key={idx} post={post}/>;
})}
</div>
);
}

打开文件后输入以下代码:
import { gql } from "@apollo/client";
export default gql`
query (
$request: SingleProfileQueryRequest!
$publicationsRequest: PublicationsQueryRequest!
) {
publications( request: $publicationsRequest) {
items {
__typename
... on Post {
...PostFields
}
... on Comment {
...CommentFields
}
... on Mirror {
...MirrorFields
}
}
pageInfo {
prev
next
totalCount
}
}
profile(request: $request) {
id
name
bio
attributes {
displayType
traitType
key
value
}
followNftAddress
metadata
isDefault
picture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
url
mimeType
}
}
__typename
}
handle
coverPicture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
url
mimeType
}
}
__typename
}
ownedBy
dispatcher {
address
canUseRelay
}
stats {
totalFollowers
totalFollowing
totalPosts
totalComments
totalMirrors
totalPublications
totalCollects
}
followModule {
... on FeeFollowModuleSettings {
type
amount {
asset {
symbol
name
decimals
address
}
value
}
recipient
}
... on ProfileFollowModuleSettings {
type
}
... on RevertFollowModuleSettings {
type
}
}
}
}
fragment MediaFields on Media {
url
mimeType
}
fragment ProfileFields on Profile {
id
name
bio
attributes {
displayType
traitType
key
value
}
isFollowedByMe
isFollowing(who: null)
followNftAddress
metadata
isDefault
handle
picture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
...MediaFields
}
}
}
coverPicture {
... on NftImage {
contractAddress
tokenId
uri
verified
}
... on MediaSet {
original {
...MediaFields
}
}
}
ownedBy
dispatcher {
address
}
stats {
totalFollowers
totalFollowing
totalPosts
totalComments
totalMirrors
totalPublications
totalCollects
}
followModule {
... on FeeFollowModuleSettings {
type
amount {
asset {
name
symbol
decimals
address
}
value
}
recipient
}
... on ProfileFollowModuleSettings {
type
}
... on RevertFollowModuleSettings {
type
}
}
}
fragment PublicationStatsFields on PublicationStats {
totalAmountOfMirrors
totalAmountOfCollects
totalAmountOfComments
}
fragment MetadataOutputFields on MetadataOutput {
name
description
content
media {
original {
...MediaFields
}
}
attributes {
displayType
traitType
value
}
}
fragment Erc20Fields on Erc20 {
name
symbol
decimals
address
}
fragment CollectModuleFields on CollectModule {
__typename
... on FreeCollectModuleSettings {
type
followerOnly
contractAddress
}
... on FeeCollectModuleSettings {
type
amount {
asset {
...Erc20Fields
}
value
}
recipient
referralFee
}
... on LimitedFeeCollectModuleSettings {
type
collectLimit
amount {
asset {
...Erc20Fields
}
value
}
recipient
referralFee
}
... on LimitedTimedFeeCollectModuleSettings {
type
collectLimit
amount {
asset {
...Erc20Fields
}
value
}
recipient
referralFee
endTimestamp
}
... on RevertCollectModuleSettings {
type
}
... on TimedFeeCollectModuleSettings {
type
amount {
asset {
...Erc20Fields
}
value
}
recipient
referralFee
endTimestamp
}
}
fragment PostFields on Post {
id
profile {
...ProfileFields
}
stats {
...PublicationStatsFields
}
metadata {
...MetadataOutputFields
}
createdAt
collectModule {
...CollectModuleFields
}
referenceModule {
... on FollowOnlyReferenceModuleSettings {
type
}
}
appId
hidden
mirrors(by: null)
hasCollectedByMe
}
fragment MirrorBaseFields on Mirror {
id
profile {
...ProfileFields
}
stats {
...PublicationStatsFields
}
metadata {
...MetadataOutputFields
}
createdAt
collectModule {
...CollectModuleFields
}
referenceModule {
... on FollowOnlyReferenceModuleSettings {
type
}
}
appId
hidden
hasCollectedByMe
}
fragment MirrorFields on Mirror {
...MirrorBaseFields
mirrorOf {
... on Post {
...PostFields
}
... on Comment {
...CommentFields
}
}
}
fragment CommentBaseFields on Comment {
id
profile {
...ProfileFields
}
stats {
...PublicationStatsFields
}
metadata {
...MetadataOutputFields
}
createdAt
collectModule {
...CollectModuleFields
}
referenceModule {
... on FollowOnlyReferenceModuleSettings {
type
}
}
appId
hidden
mirrors(by: null)
hasCollectedByMe
}
fragment CommentFields on Comment {
...CommentBaseFields
mainPost {
... on Post {
...PostFields
}
... on Mirror {
...MirrorBaseFields
mirrorOf {
... on Post {
...PostFields
}
... on Comment {
...CommentMirrorOfFields
}
}
}
}
}
fragment CommentMirrorOfFields on Comment {
...CommentBaseFields
mainPost {
... on Post {
...PostFields
}
... on Mirror {
...MirrorBaseFields
}
}
}
`;

打开文件后输入下方代码
// components/Post.js
export default function Post(props) {
const post = props.post;
return (
<div className="p-8">
<div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
<div className="md:flex">
<div className="p-8">
<p className="mt-2 text-xs text-slate-500 whitespace-pre-line">
{post.metadata.content}
</p>
</div>
</div>
</div>
</div>
);
}
在浏览器打开下方框选的链接

打开后如下显示.(图片加载不出来是正常的)

然后随便点一个人的资料进入. 类似下方显示就可以了







