发布于 2026-01-06 2 阅读
0

我如何在短短 2 小时内创建了一个极简的 Linktree 式页面。DEV 全球展示挑战赛,由 Mux 主办:展示你的项目!

我如何在短短 2 小时内创建出一个极简的 Linktree 式页面。

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

计划

我看到很多人使用 Linktree 和类似的网站来创建社交链接页面,但我需要的是一个非常简洁干净的页面,所以我决定自己制作社交链接页面!

:我已尽力解释整个过程,但我还是个博客新手,所以如果有些地方看起来很奇怪,请不要介意,也请告诉我哪些地方可以改进,我很乐意听取您的意见。

设计

设计思路很清晰:一个小头像、名字、个人简介以及所有社交链接,都以图标形式呈现,并带有炫酷的悬停效果。为了方便日后自定义,我知道必须创建一个配置文件,其中包含所有颜色图标列表名字个人简介头像链接。它看起来是这样的:

// config.js 
export const config = {
    avatar: 'https://avatars.githubusercontent.com/u/68690233',
    bgColor: '#18181b',
    textColor: '#d4d4d8',
    iconColor: '#d4d4d8',
    name: 'ashish',
    description: 'solo developer by day, overthinker by night.',
    links: [
        {
            slug: 'github',
            type: 'url',
            link: 'https://github.com/asheeeshh/'
        },
        {
            slug: 'discord',
            type: 'hover',
            text: 'asheeshh#7727'
        },
        ...
    ]
}
Enter fullscreen mode Exit fullscreen mode

请注意我如何使用type: 'hover'Discord 图标来将其与其他图标区分开来,继续阅读以了解原因。

技术栈

由于只是一个单页应用,我决定使用 NextJS,因为我目前对它非常熟悉。以下是我使用的所有框架和库:

创建应用程序

首先,我使用我喜爱的命令快速启动了下一个项目create-next-app在项目中初始化了Tailwind CSS,并安装了我需要的所有其他库。

下一步是创建我需要的所有组件,即Avatar.jsxIcon.jsxIconBar.jsx

成分

  • Avatar.jsx- 应用中的头像组件。
  • Icon.jsx- 单个图标组件。
  • IconBar.jsx- 应用程序中的水平图标栏组件。

现在,我们来讨论一下这些文件的内容。

这是我的文件代码Avatar.jsx。它是一个带有 Tailwind 类的 Next Image 组件。

// Avatar.jsx

import Image from 'next/image'

export default function Avatar() {
    return (
        <Image src="https://avatars.githubusercontent.com/u/68690233" alt="Avatar" width={100} height={100} className="rounded-full"/>
    )
}
Enter fullscreen mode Exit fullscreen mode

图标方面,我使用的是 Simple-Icons,因为他们有很多品牌图标,这正是我需要的。首先,我创建了一个文件GetIcon.js,使用别名(slug)获取 SVG 图标。它看起来大概是这样的。

// GetIcon.js

import SimpleIcons from 'simple-icons';

export default function GetIcon(slug) {
    const icon = SimpleIcons.Get(slug).svg
    return icon;
}
Enter fullscreen mode Exit fullscreen mode

如您所见,它返回的<svg></svg>是图标的标签字符串。下一步是将该字符串转换为 JSX 组件,这正是我的Icon.jsx组件的功能。

// Icon.jsx

import GetIcon from "../libs/GetIcon";
import { config } from "../config";

export default function Icon(props) {
    return (
        <div dangerouslySetInnerHTML={{__html: `${GetIcon(props.icon)}`}} className="w-[30px] h-[30px] hover:scale-[1.15]  duration-300 ease-in-out" style={{fill: `${config.iconColor}`}}></div>
    )
}
Enter fullscreen mode Exit fullscreen mode

你可以看到我使用配置来设置图标颜色。它接收图标别名作为 props,并将其传递给GetIcon()返回 svg 字符串的函数,该函数随后使用 jsx 组件将 svg 转换为 jsx 组件。dangereouslySetInnerHTML

最后一个组件IconBar.jsx会将所有图标水平堆叠起来​​,并将它们作为 jsx 组件返回。

// IconBar.jsx

import Icon from "./Icon";
import { config } from "../config";
import ReactTooltip from 'react-tooltip';
import { useEffect, useState } from "react";
import toast, { Toaster } from 'react-hot-toast';

export default function IconBar() {
    const [isMounted, setIsMounted] = useState(false)
    useEffect(() => {
        setIsMounted(true)
    }, [])
    const handleClick = (e) => {
        navigator.clipboard.writeText(e.target.closest('[data-tip]').dataset.tip)
        toast.success("Copied to clipboard!", {
            duration: 2000,
        })
    }
    const icons = config.links.map(
        (icon) => {
            if (icon.type == "url") {
                return (
                    <div className="text-center items-center cursor-pointer" key={icon.slug}>
                        <a href={icon.link} target="_blank" rel="noopener noreferrer" >
                            <Icon icon={icon.slug} />
                        </a>
                    </div>
                );
            } else if (icon.type == "hover") {
                return (
                    <div className="text-center items-center cursor-pointer" key={icon.slug}> 
                        <a data-tip={icon.text} key={icon.slug} onClick={handleClick}>
                            <Icon icon={icon.slug} />
                        </a>
                        {isMounted && <ReactTooltip place="top" type="dark" effect="float"/>}
                    </div>
                )
            } else {
                return;
            }
        }
    )
    return (
        <div className="flex flex-wrap w-full h-full gap-5 justify-center items-top">
            <Toaster 
                toastOptions={{
                    style: {
                        background: `${config.textColor}`
                    }
                }}
            />
            {icons}
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

config.js我正在将文件中的数组映射icons<div></div>组件,最终这些组件会被用到返回的 div 元素中。另外,由于 Discord 没有 URL,但有一个标签,所以我用它来为 Discord 图标创建工具提示。这就是我上面提到的React-Tooltip添加 Discord 图标的原因。type: 'hover'

为了显示 Discord 标签已被复制的通知,我使用了该React-Hot-Toast库。

组装部件

最后一步是将文件中的所有组件组装起来,index.js完成应用程序的开发。成品如下:

// index.js

import Avatar from "../components/Avatar"
import IconBar from "../components/IconBar"
import { config } from "../config"
import Head from "next/head"

export default function Home() {
  return (
    <div className="flex flex-col justify-center items-center w-screen h-screen p-6" style={{backgroundColor: `${config.bgColor}`}}>
      <Head>
        <title>{config.name}</title>
        <meta name="description" content={config.description} />
        <link rel="icon" href={(process.env.NEXT_PUBLIC_CLOUDIMG_TOKEN) ? `https://${process.env.NEXT_PUBLIC_CLOUDIMG_TOKEN}.cloudimg.io/${config.avatar}?radius=500` : `${config.avatar}`} />
      </Head>
      <div className="flex flex-col justify-center align-center w-full lg:w-1/2 md:w-1/3 h-[80%] lg:h-1/2 md:h-1/2 items-center">
        <div className="w-full h-full flex flex-col justify-center items-center">
          <Avatar />
          <h1 className="text-center text-xl font-[600] mt-3" style={{color: `${config.textColor}`}}>{config.name}</h1>
          <h1 className="text-[${config.textColor}] text-center text-md font-normal mt-5" style={{color: `${config.textColor}`}}>{config.description}</h1>
          <div className="w-full h-1/4 mt-5 lg:mt-3 md:mt-3">
            <IconBar />
          </div>
        </div>
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

组装完成后,使用 Tailwind 进行一些样式设置,App 的外观如下:

ss

部署应用程序

我使用 Vercel 来部署应用程序,因为它与 NextJS 的兼容性最佳,并且我还为其添加了一个自定义子域名。目前,该网站已上线,网址为https://ayyy.vercel.app/https://ayyy.asheeshh.ninja/

结论

这就是制作这款应用的整个过程,大约花了2个小时,我现在已经把它用作我的社交链接页面了。

您可以随意使用它来创建自己的页面,源代码以 MIT 许可证发布在此处

感谢阅读 <3

文章来源:https://dev.to/asheeshh/how-i-created-a-minimal-linktree-like-page-for-me-in-just-2-hours-ncp