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

使用 React、NextJS 和 TailwindCSS 重构落地页 NextJS Starter NextJS + TailwindCSS Starter 启用 TypeScript 页面结构 tsconfig.json 行动号召按钮 (CTA) 使用 Netlify 部署 优化(Lighthouse) 结论

使用 React、NextJS 和 TailwindCSS 重构着陆页

NextJS Starter

NextJS + TailwindCSS Starter

启用 TypeScript

页面结构

tsconfig.json

行动号召按钮(CTA)

使用 Netlify 进行部署

优化(灯塔)

结论

在这篇博文中,我将介绍我们对产品Kubernetic的落地页进行的重构过程,目的是使其界面更加简洁。整个过程历时 5 天,是对落地页的完全重写,包括试用注册表单和 Stripe 支付集成。

这次重构的主要目的是为了测试TailwindCSS框架及其“实用至上”的设计理念。我不确定这个概念是否是他们首创的,但这是我第一次接触到它,所以想尝试一下,看看它在实际应用中的优缺点。这个首页是一个小型网站,急需提升,因此完全符合“实用至上”的描述。您可以在 GitHub 上查看首页的最终代码。

在接下来的章节中,我将描述每个决策,并提供从头开始到生产部署的重现设置的操作指南。

NextJS Starter

由于我们要从头开始创建着陆页,所以是时候使用NextJS而不是Create React App (CRA) 了。

React 官网有一个推荐工具链部分,其中将 CRA 描述为学习 React 或构建单页应用程序的最佳选择,而 NextJS 则最适合使用 NodeJS 构建服务器端渲染的网站。

这个着陆页可能符合 CRA 的定义,因为我们没有使用 NodeJS 作为服务器端,但我仍然觉得 NextJS 更适合指导我以一种有主见的方式构建内容(例如页面结构),以及在部署到生产环境或与 TailWindCSS 集成时更方便,我们将在下面进一步讨论。

对于 NextJS,有一个快速入门模板,可用于引导您的代码仓库:

$ npx create-next-app nextjs-blog --use-npm --example \
https://github.com/vercel/next-learn-starter/tree/master/learn-starter
Enter fullscreen mode Exit fullscreen mode

有了存储库后,您可以使用它来运行应用程序npm run dev,并打开http://localhost:3000

替代文字

NextJS + TailwindCSS Starter

TailwindCSS 本质上是一个PostCSS插件,因此要集成它,您需要先安装 PostCSS,这是一个使用 JavaScript 转换 CSS 的工具。这里有一篇关于如何将 NextJS 与 TailwindCSS 集成的优秀指南,我强烈建议您阅读并自行练习,以便更好地理解其背后的概念和机制。不过,为了更快地启动项目,您可以使用以下已预先准备好的入门模板:

$ npx create-next-app nextjs-blog --use-npm --example \
https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss
Enter fullscreen mode Exit fullscreen mode

现在运行 npm run dev,然后在浏览器中打开http://localhost:3000 :

下一个+顺风

启用 TypeScript

关于 JavaScript 和 TypeScript 哪个更好,一直存在着激烈的争论。选择你更习惯的那​​个就好。我个人更喜欢 TypeScript 而不是 JavaScript,因为它是一个强类型超集,可以在编译时验证类型,而且 IDE(我目前最喜欢的是Visual Studio Code)在我编写代码时会提供有用的见解。

如果您想启用 TypeScript,NextJS 提供了一个不错的入门流程tsconfig.json。首先,在项目根目录创建一个空文件:

touch tsconfig.json
Enter fullscreen mode Exit fullscreen mode

创建完成后,运行npm run dev并按照说明安装依赖项:

npm run dev

# You'll see instructions like these:
#
# Please install typescript, @types/react, and @types/node by running:
#
#         yarn add --dev typescript @types/react @types/node
#
# ...
Enter fullscreen mode Exit fullscreen mode

安装完依赖项后,下次运行时会自动为您生成以下配置文件tsconfig.jsonnext-env.d.ts现在您可以开始将.js文件转换为.tsx在项目中使用 TypeScript 的格式。

页面结构

NextJS 最让我喜欢的一点就是它预定义的页面布局。

简而言之,如果您创建了pages/about.tsx一个导出 React 组件的页面,那么它就可以通过以下方式访问/about

它还支持带有动态路由的页面。例如,如果您创建一个名为 `.html` 的文件pages/posts/[id].tsx,那么它将可以通过posts/1`/ var/www/html`、 posts/2`/var/www/html` 等访问。

我们的落地页代码库的页面结构如下:

  • index.tsx- 登录页面。
  • enterprise/trial.tsx- Kubernetes Enterprise 试用注册表单已发送至 Netlify Forms。
  • payment/checkout.tsx- 向 Stripe 发送的 Kubernetic Desktop 付款表单。
  • payment/success.tsx- Stripe 付款成功后的重定向页面。

tsconfig.json

我不太喜欢修改 tsconfig.json 文件,tsconfig.json因为我喜欢保持它尽可能简洁,但有一个改动我很喜欢,那就是添加baseURL相应的路径(tsconfig.json 参考):

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@components/*": ["components/*"],
      "@utils/*": ["utils/*"],
      "@styles/*": ["styles/*"]
    },
    ...
  }
}
Enter fullscreen mode Exit fullscreen mode

现在你的导入语句是相对于根目录而不是相对于当前目录的。因此,index.tsx现在可以将文件更新为以下内容:

# OLD import without defined baseUrl:
# import Nav from '../components/nav'

# NEW import with defined baseUrl:
import Nav from '@components/nav'


export default function IndexPage() {
  return (
    <div>
      <Nav />
      <div className="py-20">
        <h1 className="text-5xl text-center text-accent-1">
          Next.js + Tailwind CSS
        </h1>
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

如果你厌倦了导入语句总是显示为“../”或“./”,或者需要在移动文件时进行更改,那么这是一个很好的解决方法。

行动号召按钮(CTA)

CTA按钮包含一个主要的下载链接,链接下方有一个美观的下拉菜单,显示不同操作系统的选项。我添加了鼠标悬停时的轻微阴影效果,以及0.3秒内移动1像素的过渡动画,营造出按钮弹出的效果。

行动号召按钮

使用 TailWindCSS 可以轻松实现这一切,它给了我前所未有的像素级精度自由。这正是我梦寐以求的实用至上的设计理念,而且无需深入复杂难懂的CSS 世界。诚然,它仍然可以说是 CSS,但我认为它更加抽象,介于 CSS 和 Bootstrap 或 Material UI 等预制 UI 框架之间。

实际的 CTA 按钮可以在这里找到。首先,将 CTA 按钮的 SVG 图标创建为一个单独的组件components/Icons.tsx

export function AppleIcon() {
  return (<svg className="fill-current place-self-center align-middle w-4 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 315" version="1.1" >
    <g>
      <path d="M213.803394,167.030943 C214.2452,214.609646 255.542482,230.442639 256,230.644727 C255.650812,231.761357 249.401383,253.208293 234.24263,275.361446 C221.138555,294.513969 207.538253,313.596333 186.113759,313.991545 C165.062051,314.379442 158.292752,301.507828 134.22469,301.507828 C110.163898,301.507828 102.642899,313.596301 82.7151126,314.379442 C62.0350407,315.16201 46.2873831,293.668525 33.0744079,274.586162 C6.07529317,235.552544 -14.5576169,164.286328 13.147166,116.18047 C26.9103111,92.2909053 51.5060917,77.1630356 78.2026125,76.7751096 C98.5099145,76.3877456 117.677594,90.4371851 130.091705,90.4371851 C142.497945,90.4371851 165.790755,73.5415029 190.277627,76.0228474 C200.528668,76.4495055 229.303509,80.1636878 247.780625,107.209389 C246.291825,108.132333 213.44635,127.253405 213.803394,167.030988 M174.239142,50.1987033 C185.218331,36.9088319 192.607958,18.4081019 190.591988,0 C174.766312,0.636050225 155.629514,10.5457909 144.278109,23.8283506 C134.10507,35.5906758 125.195775,54.4170275 127.599657,72.4607932 C145.239231,73.8255433 163.259413,63.4970262 174.239142,50.1987249"></path>
    </g>
  </svg>
  )
}

export function DropdownIcon() {
  return (<svg className="fill-current -mr-1 -ml-1 h-5 w-5 rounded-md" viewBox="0 0 20 20">
    <path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
  </svg>
  )
}

export function WinIcon() {
  return (
    <svg className="fill-current w-4 mr-2" xmlns="http://www.w3.org/2000/svg" version="1.1"
      viewBox="-2.61977004 -2.61977004 92.56520808 92.83416708">
      <path
        d="M 0,12.40183 35.68737,7.5416 35.70297,41.96435 0.03321,42.16748 z m 35.67037,33.52906 0.0277,34.45332 -35.66989,-4.9041 -0.002,-29.77972 z M 39.99644,6.90595 87.31462,0 l 0,41.527 -47.31818,0.37565 z M 87.32567,46.25471 87.31457,87.59463 39.9964,80.91625 39.9301,46.17767 z" />
    </svg>
  )
}

export function LinuxIcon() {
  return (
    <svg className="fill-current w-4 mr-2" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 266 312">
      <g transform="translate(-3.3359375,285.2793)">
        <path d="M132-206c0,1-1,1-1,1h-1c-1,0-1-1-2-2,0,0-1-1-1-2s0-1,1-1l2,1c1,1,2,2,2,3m-18-10c0-5-2-8-5-8,0,0,0,1-1,1v2h3c0,2,1,3,1,5h2m35-5c2,0,3,2,4,5h2c-1-1-1-2-1-3s0-2-1-3-2-2-3-2c0,0-1,1-2,1,0,1,1,1,1,2m-30,16c-1,0-1,0-1-1s0-2,1-3c2,0,3-1,3-1,1,0,1,1,1,1,0,1-1,2-3,4h-1m-11-1c-4-2-5-5-5-10,0-3,0-5,2-7,1-2,3-3,5-3s3,1,5,3c1,3,2,6,2,9v1,1h1v-1c1,0,1-2,1-6,0-3,0-6-2-9s-4-5-8-5c-3,0-6,2-7,5-2,4-2.4,7-2.4,12,0,4,1.4,8,5.4,12,1-1,2-1,3-2m125,141c1,0,1-0.4,1-1.3,0-2.2-1-4.8-4-7.7-3-3-8-4.9-14-5.7-1-0.1-2-0.1-2-0.1-1-0.2-1-0.2-2-0.2-1-0.1-3-0.3-4-0.5,3-9.3,4-17.5,4-24.7,0-10-2-17-6-23s-8-9-13-10c-1,1-1,1-1,2,5,2,10,6,13,12,3,7,4,13,4,20,0,5.6-1,13.9-5,24.5-4,1.6-8,5.3-11,11.1,0,0.9,0,1.4,1,1.4,0,0,1-0.9,2-2.6,2-1.7,3-3.4,5-5.1,3-1.7,5-2.6,8-2.6,5,0,10,0.7,13,2.1,4,1.3,6,2.7,7,4.3,1,1.5,2,2.9,3,4.2,0,1.3,1,1.9,1,1.9m-92-145c-1-1-1-3-1-5,0-4,0-6,2-9,2-2,4-3,6-3,3,0,5,2,7,4,1,3,2,5,2,8,0,5-2,8-6,9,0,0,1,1,2,1,2,0,3,1,5,2,1-6,2-10,2-15,0-6-1-10-3-13-3-3-6-4-10-4-3,0-6,1-9,3-2,3-3,5-3,8,0,5,1,9,3,13,1,0,2,1,3,1m12,16c-13,9-23,13-31,13-7,0-14-3-20-8,1,2,2,4,3,5l6,6c4,4,9,6,14,6,7,0,15-4,25-11l9-6c2-2,4-4,4-7,0-1,0-2-1-2-1-2-6-5-16-8-9-4-16-6-20-6-3,0-8,2-15,6-6,4-10,8-10,12,0,0,1,1,2,3,6,5,12,8,18,8,8,0,18-4,31-14v2c1,0,1,1,1,1m23,202c4,7.52,11,11.3,19,11.3,2,0,4-0.3,6-0.9,2-0.4,4-1.1,5-1.9,1-0.7,2-1.4,3-2.2,2-0.7,2-1.2,3-1.7l17-14.7c4-3.19,8-5.98,13-8.4,4-2.4,8-4,10-4.9,3-0.8,5-2,7-3.6,1-1.5,2-3.4,2-5.8,0-2.9-2-5.1-4-6.7s-4-2.7-6-3.4-4-2.3-7-5c-2-2.6-4-6.2-5-10.9l-1-5.8c-1-2.7-1-4.7-2-5.8,0-0.3,0-0.4-1-0.4s-3,0.9-4,2.6c-2,1.7-4,3.6-6,5.6-1,2-4,3.8-6,5.5-3,1.7-6,2.6-8,2.6-8,0-12-2.2-15-6.5-2-3.2-3-6.9-4-11.1-2-1.7-3-2.6-5-2.6-5,0-7,5.2-7,15.7v3.3,11.6,8.9,4.3,3c0,0.9-1,2.9-1,6-1,3.1-1,6.62-1,10.6l-2,11.1v0.17m-145-5.29c9.3,1.36,20,4.27,32.1,8.71,12.1,4.4,19.5,6.7,22.2,6.7,7,0,12.8-3.1,17.6-9.09,1-1.94,1-4.22,1-6.84,0-9.45-5.7-21.4-17.1-35.9l-6.8-9.1c-1.4-1.9-3.1-4.8-5.3-8.7-2.1-3.9-4-6.9-5.5-9-1.3-2.3-3.4-4.6-6.1-6.9-2.6-2.3-5.6-3.8-8.9-4.6-4.2,0.8-7.1,2.2-8.5,4.1s-2.2,4-2.4,6.2c-0.3,2.1-0.9,3.5-1.9,4.2-1,0.6-2.7,1.1-5,1.6-0.5,0-1.4,0-2.7,0.1h-2.7c-5.3,0-8.9,0.6-10.8,1.6-2.5,2.9-3.8,6.2-3.8,9.7,0,1.6,0.4,4.3,1.2,8.1,0.8,3.7,1.2,6.7,1.2,8.8,0,4.1-1.2,8.2-3.7,12.3-2.5,4.3-3.8,7.5-3.8,9.78,1,3.88,7.6,6.61,19.7,8.21m33.3-90.9c0-6.9,1.8-14.5,5.5-23.5,3.6-9,7.2-15,10.7-19-0.2-1-0.7-1-1.5-1l-1-1c-2.9,3-6.4,10-10.6,20-4.2,9-6.4,17.3-6.4,23.4,0,4.5,1.1,8.4,3.1,11.8,2.2,3.3,7.5,8.1,15.9,14.2l10.6,6.9c11.3,9.8,17.3,16.6,17.3,20.6,0,2.1-1,4.2-4,6.5-2,2.4-4.7,3.6-7,3.6-0.2,0-0.3,0.2-0.3,0.7,0,0.1,1,2.1,3.1,6,4.2,5.7,13.2,8.5,25.2,8.5,22,0,39-9,52-27,0-5,0-8.1-1-9.4v-3.7c0-6.5,1-11.4,3-14.6s4-4.7,7-4.7c2,0,4,0.7,6,2.2,1-7.7,1-14.4,1-20.4,0-9.1,0-16.6-2-23.6-1-6-3-11-5-15-2-3-4-6-6-9s-3-6-5-9c-1-4-2-7-2-12-3-5-5-10-8-15-2-5-4-10-6-14l-9,7c-10,7-18,10-25,10-6,0-11-1-14-5l-6-5c0,3-1,7-3,11l-6.3,12c-2.8,7-4.3,11-4.6,14-0.4,2-0.7,4-0.9,4l-7.5,15c-8.1,15-12.2,28.9-12.2,40.4,0,2.3,0.2,4.7,0.6,7.1-4.5-3.1-6.7-7.4-6.7-13m71.6,94.6c-13,0-23,1.76-30,5.25v-0.3c-5,6-10.6,9.1-18.4,9.1-4.9,0-12.6-1.9-23-5.7-10.5-3.6-19.8-6.36-27.9-8.18-0.8-0.23-2.6-0.57-5.5-1.03-2.8-0.45-5.4-0.91-7.7-1.37-2.1-0.45-4.5-1.13-7.1-2.05-2.5-0.79-4.5-1.82-6-3.07-1.38-1.26-2.06-2.68-2.06-4.27,0-1.6,0.34-3.31,1.02-5.13,0.64-1.1,1.34-2.2,2.04-3.2,0.7-1.1,1.3-2.1,1.7-3.1,0.6-0.9,1-1.8,1.4-2.8,0.4-0.9,0.8-1.8,1-2.9,0.2-1,0.4-2,0.4-3s-0.4-4-1.2-9.3c-0.8-5.2-1.2-8.5-1.2-9.9,0-4.4,1-7.9,3.2-10.4s4.3-3.8,6.5-3.8h11.5c0.9,0,2.3-0.5,4.4-1.7,0.7-1.6,1.3-2.9,1.7-4.1,0.5-1.2,0.7-2.1,0.9-2.5,0.2-0.6,0.4-1.2,0.6-1.7,0.4-0.7,0.9-1.5,1.6-2.3-0.8-1-1.2-2.3-1.2-3.9,0-1.1,0-2.1,0.2-2.7,0-3.6,1.7-8.7,5.3-15.4l3.5-6.3c2.9-5.4,5.1-9.4,6.7-13.4,1.7-4,3.5-10,5.5-18,1.6-7,5.4-14,11.4-21l7.5-9c5.2-6,8.6-11,10.5-15s2.9-9,2.9-13c0-2-0.5-8-1.6-18-1-10-1.5-20-1.5-29,0-7,0.6-12,1.9-17s3.6-10,7-14c3-4,7-8,13-10s13-3,21-3c3,0,6,0,9,1,3,0,7,1,12,3,4,2,8,4,11,7,4,3,7,8,10,13,2,6,4,12,5,20,1,5,1,10,2,17,0,6,1,10,1,13,1,3,1,7,2,12,1,4,2,8,4,11,2,4,4,8,7,12,3,5,7,10,11,16,9,10,16,21,20,32,5,10,8,23,8,36.9,0,6.9-1,13.6-3,20.1,2,0,3,0.8,4,2.2s2,4.4,3,9.1l1,7.4c1,2.2,2,4.3,5,6.1,2,1.8,4,3.3,7,4.5,2,1,5,2.4,7,4.2,2,2,3,4.1,3,6.3,0,3.4-1,5.9-3,7.7-2,2-4,3.4-7,4.3-2,1-6,3-12,5.82-5,2.96-10,6.55-15,10.8l-10,8.51c-4,3.9-8,6.7-11,8.4-3,1.8-7,2.7-11,2.7l-7-0.8c-8-2.1-13-6.1-16-12.2-16-1.94-29-2.9-37-2.9" />
      </g>
    </svg >
  )
}
Enter fullscreen mode Exit fullscreen mode

图标创建完成后,就可以创建 CTA 按钮了components/CTAButton.tsx

import Link from 'next/link'
import { useState } from "react"
import { AppleIcon, DropdownIcon, LinuxIcon, WinIcon } from "./Icons"

export default function CTAButton() {
  const [isOpen, updateIsOpen] = useState(false)
  return (
    <>
      <div className="inline-flex">
        <div className="relative">
          <div className="btn-popup inline-flex w-56 divide-x divide-green-600 hover:shadow-lg">
            <Link href="https://www.kubernetic.com/">
              <button className="btn btn-green inline-flex w-48 rounded-l  px-3 py-3 pl-4">
                <AppleIcon />
                <span>Download for Mac</span>
              </button>
            </Link>
            <button aria-label="choose-os" className="btn btn-green inline-flex transition rounded-r ease-in-out duration-150 px-3 py-3"
              onClick={() => updateIsOpen(!isOpen)}>
              <DropdownIcon />
            </button>
          </div>
          {isOpen && <DropdownMenu />}
        </div>
      </div>
    </>
  )
}


const DropdownMenu = () => (
  <div className="absolute">
    <ul className="w-56 ml-1 p-2 mt-2 text-gray-600 bg-white border border-gray-100 rounded-lg shadow-md min-w-max-content right-0" aria-label="submenu">
      <DropdownMenuItem icon={<WinIcon />} text="Download for Windows" to="https://www.kubernetic.com/" />
      <DropdownMenuItem icon={<LinuxIcon />} text="Download for Linux" to="https://www.kubernetic.com/" />
    </ul>
  </div>
)

type DropdownMenuProps = { icon: any, text: string, to: string }
function DropdownMenuItem({ icon, text, to }: DropdownMenuProps) {
  return (
    <Link href={to}>
      <li>
        <a className="inline-flex items-center cursor-pointer w-full px-2 py-2 text-sm font-medium transition-colors duration-150 rounded-md hover:bg-gray-200 hover:text-gray-800" type="button">
          {icon}
          <span>{text}</span>
        </a>
      </li>
    </Link>
  )
}
Enter fullscreen mode Exit fullscreen mode

最后,为了能够在其他地方重用按钮的样式,我将其定义在 `<head>` 标签内,index.css而不是在每个组件中内联定义:

@tailwind base;

/* Write your own custom base styles here */

/* Start purging... */
@tailwind components;
/* Stop purging. */

/* Write your own custom component styles here */
.btn-blue {
  @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}

/* Start purging... */
@tailwind utilities;
/* Stop purging. */

/* Your own custom utilities */

/* Button */
.btn {
  @apply whitespace-no-wrap text-base font-medium items-center justify-center cursor-pointer;
}
.btn:focus {
  @apply outline-none shadow-outline;
}
.btn:hover {
  @apply shadow-lg;
}

/* Button Popup */
.btn-popup {
  @apply transition duration-300 ease-in-out transform;
}
.btn-popup:hover {
  @apply -translate-y-px;
}

/* Button Green */
.btn-green {
  @apply text-white bg-green-500;
}
.btn-green:focus {
  @apply border-green-700;
}
.btn-green:hover {
  @apply text-white bg-green-400;
}
.btn-green:active {
  @apply bg-green-700;
}
Enter fullscreen mode Exit fullscreen mode

更新您的pages/ndex.tsx代码,添加 CTAButton:

import CTAButton from '@components/CTAButton'
import Nav from '@components/nav'

export default function IndexPage() {
  return (
    <div>
      <Nav />
      <div className="py-20">
        <h1 className="text-center">
          <CTAButton/>
        </h1>
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

现在你应该能看到你自己的CTA按钮了:

新CTA

使用 Netlify 进行部署

之前我们的落地页部署在 Google Storage Bucket 上,并通过 Google Cloud CDN 提供服务。部署是手动完成的,现在我们使用 Netlify,再也回不去了。以下是我们目前使用的一些功能:

  • 代码库中的更新master会自动发布到网站上。
  • 使用Let's Encrypt自动获取 HTTPS 证书。
  • 分支上的推送develop会自动发布到专用 URL。
  • 其他分支和 GitHub Pull Request 也可以获得专用 URL。

Netlify 开箱即用地支持 NextJS,因此配置起来非常简单。所有内容都可以在文件中进行版本控制netlify.toml(我个人不太喜欢 TOML 格式,更偏爱 YAML,虽然我知道很多人也不喜欢它,但这只是个人偏好)。如果您有敏感的环境变量,也可以在 Netlify 的用户界面中进行配置。在我们的案例中,所有内容都是可公开的(是的,Stripe 密钥是公开的——别耍花招):

[build]
  command = "npm run build && npm run export"
  publish = "out"

[[plugins]]
package = "netlify-plugin-cache-nextjs"

[context.production.environment]
  NEXT_PUBLIC_LICENSESERVER_URL = ...
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY = ...

[context.staging.environment]
  NEXT_PUBLIC_LICENSESERVER_URL = ...
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY = ...

[context.branch-deploy.environment]
  NEXT_PUBLIC_LICENSESERVER_URL = ...
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY = ...
Enter fullscreen mode Exit fullscreen mode
  • build节定义了构建命令和输出目录。
  • 我们plugins已经为 NetxJS 设置了缓存,以加快构建速度。
  • 这些environment部分定义了在构建过程中根据分支注入的不同变量。在master分支 1 中,我们使用生产环境变量;在develop分支 2 中,我们使用 Stripe 的测试环境来测试 Stripe 集成。这些变量也在env.[local|production|development|test]文件中进行了设置,以便在 Netlify 之外(例如本地运行实例)使用它们。这些变量env.local没有添加到版本控制系统中,以便每个开发人员都可以在本地进行配置。这些变量需要以特定前缀开头,NEXT_PUBLIC_以便可以从浏览器访问(参见 NextJS 文档)。

当然,也可以使用NextJS 的开发商Vercel进行部署。我选择 Netlify 而不是 Vercel 并没有什么特别的原因,在做决定之前一定要了解一下两者。我个人对 Netlify 很满意,所以目前没有理由切换到 Netlify。

优化(灯塔)

网站部署完成后,我花了一些时间使用Lighthouse对网站进行优化,确保其针对 Web 进行了适当的优化。优化完成后,结果相当不错:

灯塔

我注意到的一点是,如果将首页上使用的图片以新一代格式提供,可以更好地压缩图片。然后我使用cwebp CLI 处理静态 PNG 图片,使用ezgif.com处理动态 PNG 图片。

结论

如果你对 React 感兴趣,那么 NextJS 绝对值得一看,即使你只是构建一个简单的落地页,它也能提供一种很好的、​​有条理的方式来组织页面和组件。

TailwindCSS 的实用性优先设计理念非常棒,现在我可以针对特定组件进行个性化设计,而无需在 CSS 中为每个元素类命名。虽然在大型项目中应谨慎使用,以避免设计重复,但值得庆幸的是,自定义组件可以轻松创建和重用,正如 CSS 文件中所示。

最后,通过图像优化,我的首次内容绘制时间缩短了 1 秒,这可以提高销量。

文章来源:https://dev.to/dkapanidis/refactoring-landing-page-with-react-nextjs-tailwindcss-2hk8