速来,美女官网搭建

183 阅读4分钟

出发点缘由:
需实:码字一切的根源来源于寂寞孤独,为什么YOU不给我介绍OBJECT
实:目前需求不太饱和,自娱自乐,牛马命😭

效果图

20251115-143226.gif

一、项目初始化

首先创建项目:

npx create-next-app@latest

会提示你一堆问题,全都用推荐配置就好👇

img_v3_02s0_fab4e2d7-7fc2-41bc-9971-7b3bcd1a51hu.jpg

Next.js 官方模板已经帮你装好 TypeScript、ESLint、Tailwind、App Router 了。
剩下的依赖库我们后面慢慢加。然后目前项目基本情况就是无需任何配置,即可使用tailwindcss来快速构建你心心念念的页面了🍭。


二、先搞个 favicon 图标

网站没图标那肯定不得行。
可以上 realfavicongenerator.net
把你的 logo 转成 favicon.ico

然后把它丢到 app/ 下。
浏览器自动识别。

浏览器在标签页上显示网站的小图标(favicon),无需在head进行配置

image.png


三、安装运行依赖 & 配置环境变量

说白了就是:
开发依赖 (-D) 是你写代码时用的,
运行依赖 是线上跑项目时用的。

npm install -D cross-env

我没选 dotenv / dotenv-cli(ps:我觉得安装两个包不如安装一个包🤣),
因为 cross-env 已经够用了。


新建环境变量文件

在根目录新建:

.env.development
.env.production

文件名nextjs文档有说这个它的环境变量规范

内容如下:

NEXT_PUBLIC_APP_ENV=
NEXT_PUBLIC_PACKAGE_NAME=
NEXT_PUBLIC_EMAIL=
NEXT_PUBLIC_CDN=/
NEXT_PUBLIC_BASE_URL=
NEXT_PUBLIC_GOOGLE_URL=

四、修改 package.json 脚本

改成下面这样:

"scripts": {
  "clean": "rmdir /s /q .next || rm -rf .next",
  "dev:dev": "cross-env NODE_ENV=development npm run clean && next dev",
  "dev:prod": "cross-env NODE_ENV=production npm run clean && next dev",
  "build:dev": "cross-env NODE_ENV=development npm run clean && next build",
  "build:prod": "cross-env NODE_ENV=production npm run clean && next build",
  "start": "next start",
  "lint": "eslint"
}

"clean": "rmdir /s /q .next || rm -rf .next",运行项目的时候next缓存报错,进行一个clean清除next缓存异常问题。


五、工具函数 utils/env.ts

把环境变量统一导出:

export const ENV = process.env.NEXT_PUBLIC_APP_ENV;
export const PACKAGE_NAME = process.env.NEXT_PUBLIC_PACKAGE_NAME;
export const EMAIL = process.env.NEXT_PUBLIC_EMAIL;
export const CDN = process.env.NEXT_PUBLIC_CDN;
export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
export const GOOGLE_URL = process.env.NEXT_PUBLIC_GOOGLE_URL;

六、验证环境变量

app/page.tsx 中写个简单页面:

import Image from "next/image";
import { PACKAGE_NAME, EMAIL } from "@/utils/env";

export default function Home() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
      <main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
        <Image
          className="dark:invert"
          src="/images/logo.png"
          alt="Next.js logo"
          width={100}
          height={100}
          priority
        />

        <h1 className="text-4xl font-bold text-zinc-900 dark:text-white">
          {PACKAGE_NAME}
        </h1>
        <p className="text-lg text-zinc-700 dark:text-zinc-300">{EMAIL}</p>
      </main>
    </div>
  );
}

打开页面 → 能正常显示出变量值说明环境变量配置成功!

恭喜,你也太棒了,孩纸 👏👏👏

img_v3_02s0_2ab0446e-4d49-4e48-8ab3-932c585a19hu.jpg


七、开整首页轮播

一个像样的官网首页,少不了轮播图,用来播放美女😘。
我选的是 Swiper —— 稳健。

npm i swiper

八、目录结构 & Header 组件

项目结构如图:

image.png

Header

用 Tailwind 写得干净利落,天然响应式支持:

"use client";
import { CDN, PACKAGE_NAME } from "@/utils/env";
import { useState } from "react";
import { useRouter, usePathname } from "next/navigation";

export default function Header() {
  const [open, setOpen] = useState(false);
  const router = useRouter();
  const pathname = usePathname();

  return (
    <>
      <div className="h-[64px]" />
      <header
        className={`flex justify-between fixed left-0 top-0 w-full h-[64px] px-[20px] z-10 lg:px-[60px] ${
          ["/protocol", "/contact"].includes(pathname) &&
          "bg-slate-950 lg:bg-white"
        }`}
      >
        <span className="flex items-center">
          <img src={`${CDN}images/logo.png`} className="w-[32px] h-[32px]" />
          <span
            className={`text-white font-[700] text-[15px] ml-[5px] ${
              ["/protocol", "/contact"].includes(pathname) && "lg:text-black"
            }`}
          >
            {PACKAGE_NAME}
          </span>
        </span>

        {open ? (
          <img
            src={`${CDN}images/close.svg`}
            className="w-[30px] lg:hidden"
            onClick={() => setOpen(false)}
          />
        ) : (
          <img
            src={`${CDN}images/menu.svg`}
            className="w-[30px] lg:hidden"
            onClick={() => setOpen(true)}
          />
        )}

        {/* 菜单 */}
        <ul
          className={`absolute left-0 w-full top-[64px] bg-white shadow-lg rounded-b-[10px] ${
            open ? "block" : "hidden"
          } lg:static lg:bg-transparent lg:inline-flex lg:items-center`}
        >
          {[
            { path: "/", label: "Home" },
            { path: "/protocol", label: "Privacy Policy" },
            { path: "/contact", label: "Contact us" },
          ].map((item) => (
            <li
              key={item.path}
              onClick={() => router.push(item.path)}
              className={`px-[20px] py-[10px] text-[15px] cursor-pointer ${
                pathname === item.path && "text-black"
              }`}
            >
              {item.label}
            </li>
          ))}
        </ul>
      </header>
    </>
  );
}

九、Home 页面布局

home/Panel.tsx

import { CDN } from "@/utils/env";
import Header from "../../Header";
import Main from "../Main";

export default function Panel() {
  return (
    <main
      style={{ "--bg": `url('${CDN}images/bg.png')` } as React.CSSProperties}
      className="w-screen h-screen overflow-auto bg-no-repeat bg-cover bg-[image:--bg] font-[Roboto]"
    >
      <Header />
      <Main />
    </main>
  );
}

十、Swiper 主内容

home/Main.tsx

"use client";

import { CDN, PACKAGE_NAME, GOOGLE_URL } from "@/utils/env";
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Autoplay, Pagination } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";

export default function Main() {
  return (
    <main className="lg:flex lg:flex-row-reverse lg:items-center lg:justify-center lg:h-[calc(100%-64px)] px-4 lg:px-0">
      <div className="relative mt-[20px] max-w-[700px] w-full mx-auto lg:w-[700px]">
        <Swiper
          className="w-full"
          loop
          slidesPerView={1}
          modules={[Navigation, Autoplay, Pagination]}
          autoplay={{ delay: 2000 }}
          pagination={{ clickable: true }}
          navigation
          autoHeight
        >
          {[1, 2, 3, 4, 5].map((i) => (
            <SwiperSlide key={i} className="flex justify-center items-center py-4">
              <img
                src={`${CDN}images/swiper${i}.png`}
                className="w-[60%] h-full object-contain rounded-[10px] mx-auto mb-[10px]"
              />
            </SwiperSlide>
          ))}
        </Swiper>
      </div>

      <div className="text-center mt-[20px] lg:text-left lg:mr-[20px] lg:mt-0 xl:mr-[40px] lg:pl-[20px]">
        <img
          src={`${CDN}images/logo.png`}
          className="w-[72px] h-[72px] inline-block rounded-[10px]"
        />
        <h2 className="text-white text-[30px] font-[900] my-[10px] lg:text-[64px] max-w-[600px] mx-auto lg:mx-0">
          {PACKAGE_NAME}
        </h2>
        <img
          src={`${CDN}images/google.png`}
          className="w-[194px] inline-block active:scale-95 mt-4 cursor-pointer"
          onClick={() => (window.location.href = `${GOOGLE_URL}`)}
        />
      </div>
    </main>
  );
}

Swiper 轮播 + 响应式布局,官网质感直接拉满。
到这里,你的官网首页就 **基本完工 **。\

去动物园玩,猴哥问我说香蕉哪去了,我给猴哥一耳屎,我TM装起来了🤦‍♂️🤦‍♂️🤦‍♂️🤦‍♂️。

image.png


十一、几点踩坑分享

  • Next.js 的 <Image fill> 虽然自动拉伸很好用,但有时会因为父容器没设置 position 导致错位;
    所以我还是倾向自己给宽高。
  • Swiper 的样式记得全局引入,否则分页点和箭头样式不生效。
  • 线上要用 CDN 时,NEXT_PUBLIC_CDN 改为你自己的资源地址。

✨ 写在最后

至此,我们的 Next.js 官网首页已经跑起来了。
从环境变量到轮播图,全程无黑魔法,纯手工搭建。

这样一个轻量级,落落大方,seo完美,性能优秀,天然无添加的nextjs官网,难道你就没有一丝丝行动?

不心动,自行捏爆魔丸。

fc1575f3eaab95e9d7a7171593c211ad.png