Next13 项目总结

部门是官网部,做 C 端项目需要 SEO,用到 Nextjs,今年上半年有两个项目使用 Next13 开发的,一个是集团官网,一个是活动 h5,使用到了 Next13 最新写法 app router,后面也会持续使用,所以做一点项目总结

项目技术栈

集团官网技术栈

  • 响应式布局
  • next13 + react18 + ts
  • pages router
  • GSAP
  • threejs
  • 多语言
  • 项目规范: Prettier Eslint Husky Lint-staged Commitlint axios tailwind
  • 其他库:swiper、vanilla-tilt

活动 h5 技术栈

  • 自适应布局
  • app router
  • next13 + react18 + ts + fetch
  • 项目规范: Prettier Eslint Husky Lint-staged Commitlint tailwind
  • 其他库:swiper、js-cookie、vconsole

收获

能 hold 住项目规范,eslint 和 prettier 有自己喜欢的一套规则,提交规范用的默认的一套,感觉是够了,后面可能会考虑 git-simple-hook

使用 hooks 越来越优雅,除了封装常用的逻辑 hook,还封装了 10+动画 hook,页面全做 hook 逻辑抽离,优雅~

熟练了 app router, 虽然刚开始有挺多约束,比如要分客户端组件和服务端组件,又比如新的一套 SSR/SSG/ISR 写法;但它提供的东西是真的香,RSC 流式渲染首屏更快;嵌套 layout 解决持久化缓存问题不用整个页面刷新以及终于可以通过_component 的方式在当前目录写组件了,不用跳来跳去;文件约束也是强大得一匹,一个 loading.tsx 就能实现页面 loading,还有 not-found.tsx,error.tsx 等

对 GSAP 更加熟练,封装了一些动画 hook;GSAP 就是一个动画库国内用得是比较多的,比 react spring 和 framer-motion 强大,也很丝滑,但是在小项目下,还是多尝试 react spring 和 framer-motion

习惯,掌握 tailwind,项目越大,减少的 css 体积越多,主题切换和响应式基本没有心智负担,太香~,推广过后,同事也都爱不释手

fetch 的使用,网上很多有说 axios 过度封装的事情,自己也封装过,感觉确实没必要,fetch 其实也没必要,另外 fetch 在 next 中可以配置些 next 能懂的配置,比如{ next: { revalidate: 10 } }

更加熟悉 Next+ts 中 ts 的写法,其实基本都是 React 的,项目也是@types/react 和@types/react-dom,一些 Next 内置类型,通过 vscode 插件 Nextjs snippets 熟悉

入门了一点点 threejs,项目里有 threejs 加模型的页面,同事开发的,通过这个案例在加上后面学习了一点 threejs,算入了一点点门吧,最近我们部门会帮别的部门做一个 XR 相关的网页可能是官网,需要 threejs,正好可以实践一下

项目规范

一开始做项目的时候,初始化完了之后,会发现默认模版是不太规范的,比如项目里没有使用的变量没有提示报错,使用了 any 没有提示报错等,这个时候就需要在原来的基础上增加一些 eslint 规则来规范项目

一些风格用 prettier 统一,因为使用的是 tailwind,所以用 prettier-plugin-tailwindcss 统一风格

使用 Husky Lint-staged Commitlint 来统一提交规范

npmrc 里指定淘宝镜像

node 的版本配置一下 engines

然后需要一份封装好的 axios,在做集团官网的时候使用的 axios 但 next 和 fetch 更契合,后面 h5 项目抛弃了 axios

这样一个项目就有了,当然,这个 stater 应该保存一份到公司仓库

响应式布局和自适应布局

现在基本都是响应式布局而不是写两套代码,不过记得要求 UI 多出个平板电脑和 h5 的设计稿 自适应布局就是 h5 使用的,手机大小不同但显示是一样的

看一下苹果的响应式布局

如果用tailwind很好实现,我们需要跟UI约定breakpoint,也就是chrome浏览器控制台这个地方,hover会给你说设备的宽度

然后配置tailwind

js 复制代码
  theme: {
    extend: {
      screens: {
        md1440: '1024px', //大于1024为电脑
        md425: '425px' //大于425为平板
        // 普通情况为手机
      }
    }
  },

在页面中这样写就可以响应式了

js 复制代码
<div className="md425:flex md1440:block hidden">

再来看看自适应布局,屏幕变大字体和内容都自适应变大

自适应布局使用vw很方便,蓝湖指定画布为1000px

然后点击页面得到的元素,比如100px,页面中就写10vw就实现的自适应布局(原理是画布为1000px,页面宽度为100vw,是10:1的关系)

强大的 app router

嵌套 layout

写 spa 的时候,页面的头部有几个切换按钮在 header 组件,从一个按钮到点击另一个按钮,我们知道 header 是不会刷新的,但是以前的 nextjs 居然会刷新,而且每个 tab 都是新页面,一句卧槽~,Next13 的 app router 就解决了这个持久化缓存问题,不刷新 layout 部分,并支持嵌套 layout

还有个问题,如果我的一个页面,有它单独的几个组件和一个 type.ts,那我肯定想它能在当前目录下,就近原则方便查找,又一句卧槽,以前的版本居然不能放当前 pages 里,因为会跟约定式路由冲突,现在 app 路由里才行,使用 pages.tsx 当页面区分,当前页面可以_component 当组件目录

RSC 流式渲染

app 目录下组件默认都是 RSC,(React Server Components),组件外可以包一层 Suspence,就能流式渲染

外层组件

js 复制代码
import { Suspense } from 'react'
import Demo from './_component/demo'

const Test = async () => {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Demo />
    </Suspense>
  )
}

export default Test

Suspence 包裹的组件

js 复制代码
const Demo: React.FC = async () => {
  const res = await fetch('http://localhost:3300/data1', { cache: 'no-store' })
  const data = await res.json()
  return (
    <div>
      <div>{data.data}</div>
    </div>
  )
}

export default Demo

这样有什么好处呢?

比如我有一个页面有 10 多个模块,每个模块通过不同接口请求数据来渲染,在以前的版本,页面会每个模块请求完全部生成后才返回页面,这样是不合理的,如果能哪个模块请求并且组装完了就显示,其他模块 loading,这样不就快很多嘛,RSC 就实现了这一点,通过 fallback 指定 loading

约束文件

整个页面在加载的时候,我们希望能 loading,这个时候,直接跟 pages 同级建一个 loading.tsx 就实现了,同理还有 not-found.tsx,error.tsx 等

SSR/SSG/ISR

SSR

组件使用 async,然后里面一个 await 请求就可以 SSR 了,build 成功后会看到页面是哪一种

js 复制代码
const Demo: React.FC = async () => {
  const res = await fetch('http://localhost:3300/data1', { cache: 'no-store' })
  const data = await res.json()
  return (
    <div>
      <div>{data.data}</div>
    </div>
  )
}

export default Demo

SSG

SSG 需要使用 generateStaticParams

js 复制代码
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}

export default async function Page({ params }: { params: { id: string } }) {
  console.log(params)
  const res = await fetch(`http://localhost:3300/data2/${params.id}`, {
    cache: 'no-store'
  })
  const data = await res.json()

  return <div>{data.data}</div>
}

ISR

ISR 比 SSG 多一个 dynamicParams

js 复制代码
export const dynamicParams = true

export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }, { id: '3' }]
}

export default async function Page({ params }: { params: { id: string } }) {
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${params.id}`,
    { next: { revalidate: 10 } }
  )
  const data = (await res.json()) as { title: string; body: string }

  return (
    <div className="grid grid-cols-6 gap-x-6 gap-y-3">
      <div className="col-span-full space-y-3 lg:col-span-4">
        <h1 className="truncate text-2xl font-medium capitalize text-gray-200">
          {data.title}
        </h1>
        <p className="font-medium text-gray-500">{data.body}</p>
      </div>
      <div className="-order-1 col-span-full lg:order-none lg:col-span-2"></div>
    </div>
  )
}
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax