next13

460 阅读2分钟

浅尝next13

官网地址
不要看那个所谓的中文网站

创建项目

npx create-next-app@latest

下一步下一步就好了 如图所示

1.png 进到项目目录

npm run dev

打开浏览器访问 http://localhost:3000/ 就可以看到页面了

项目结构

2.png

  • app目录 next13使用app目录,以前的pages目录也可以使用,但是不推荐使用了。默认使用app,我们接下来的例子都是使用app目录,注意他俩的api是不一样的。
  • page.tsx 路由文件 可以认为是默认路由
  • layout.tsx 布局文件 引入了globals.css 全局样式文件

路由

app目录下面的page.tsx就是路由文件
我们尝试修改一个app/page.tsx文件

export default function Home() {
  return <div>666</div>
}

然后刷新页面,就可以看到页面变成了666

3.png 样式太难看了,我们把globals.css里面的样式去掉 修改一下启动端口

 "scripts": {
    "dev": "next dev -p 3012",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },

4.png 接下来我们要做一个导航栏,点击导航栏可以跳转到其他页面,再首页还得送上接口数据,我们先来做导航栏 app/components/header

import Link from "next/link"
import styles from "./index.module.scss"
export default function Hader() {
  return (
    <div className={`flex justify-center gap-10 ${styles.header} `}>
      <Link className="flex-4" href="/">
        首页
      </Link>
      <Link className="flex-4" href="/about">
        关于
      </Link>
      <Link className="flex-4" href="/list">
        列表
      </Link>
    </div>
  )
}

导航组件Link,最后渲染成a标签,href是跳转的地址。 /components/header/index.module.scss

.header {
  height: 36px;
  > a {
    background-color: #909090;
    padding: 10px 20px;
    display: block;

    height: 100%;
  }
}

next里面使用样式文件和react里面一样,看个人喜好,我这里 使用了tailwindcss和scss。 需要安装依赖

npm install sass sass-loader -D

添加ts类型文件 typings.d.ts

declare module '*.css';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';

tsconfig.json 添加

 "include": [
    ...
    "**/*.d.ts"
  ],

注意:如果你把组件放到app目录下并且是page.tsx默认就是路由
layout.tsx

import "./globals.css"
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import Hader from "../components/header"
const inter = Inter({ subsets: ["latin"] })

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Hader></Hader>
        {children}
      </body>
    </html>
  )
}

我们再app目录下分别创建出来对应的页面,记住是 page.tsx

5.png

6.png 如图,导航没问题了,接下来我们要做的是接口数据。

接口数据

next13 使用fetch来获取数据,返回一个promise,我们先来看看、 假设现在有个接口地址 http://124.71.160.218:3008/tangshi

  • 封装一下fetch 具体看个人喜好和具体业务逻辑,我这里是封装了一下fetch
// 封装 HTTP 请求函数
export const BaseUrl = process.env.BASE_URL
export class HTTP {
  // 响应拦截器
  static responseInterceptors = (res: any) => {
    if (res.status === 200) {
      return res.data
    } else {
      return Promise.reject(res.message)
    }
  }

  static get(url: string, data?: any, next?: any, cache?: RequestCache) {
    let params = ""
    if (data) {
      Object.keys(data).forEach((key) => {
        params += `${key}=${data[key]}&`
      })
      params = params.slice(0, -1)
      url = `${url}?${params}`
    }
    return fetch(BaseUrl + url, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
      next,
      cache,
    })
      .then((res) => res.json())
      .then((res) => {
        return this.responseInterceptors(res)
      })
  }
  static post(url: string, data?: any, next?: any, cache?: RequestCache) {
    console.log(BaseUrl + url)
    return fetch(BaseUrl + url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data || {}),
      next,
      cache,
    })
      .then((res) => res.json())
      .then((res) => {
        return this.responseInterceptors(res)
      })
  }
}
  • api接口
import { HTTP } from "./http"

enum apiList {
  tangshi = "/tangshi",
}

export async function getTangshiList(params = {}) {
  return HTTP.post(apiList.tangshi, params)
}

// 根据id获取详情
export async function getTestDetail(id: string) {
  const { data } = await HTTP.get(`${apiList.tangshi}/${id}`)
  return data
}
  • next.config.js 我们区分了一下配置环境,开发环境和生产环境,接口地址不一样
/** @type {import('next').NextConfig} */

// 获取环境变量
const NODE_ENV = process.env.NODE_ENV
const BASE_URL = {
  development: "http://124.71.160.218:3008",
  production: "http://124.71.160.218:3008",
}
const nextConfig = {
  env: {
    BASE_URL: BASE_URL[NODE_ENV],
  },
}

module.exports = nextConfig
  • app.tsx
import { getTangshiList } from "@/http/api"
import Link from "next/link"

export default async function page() {
  const { items }: any = await getTangshiList({})

  return (
    <div className="text-center mt-2">
      <h1 className="text-[20px]">唐诗三百首</h1>
      <div className="flex flex-wrap p-2 mt-4">
        {items.map((item: any) => {
          return (
            <div
              key={item.id}
              className="w-1/6 border text-center hover:bg-gray-200"
            >
              <Link href={`/tangshi/${item.id}`} className="block p-2">
                <h1 className="text-[16px] font-bold">{item.title}</h1>
                <h3 className="text-[14px] mt-2">{item.auth}</h3>
              </Link>
            </div>
          )
        })}
      </div>
    </div>
  )
}

7.png

我们可以看到返回的html,已经是后端渲染好的了。next13默认ssr服务端渲染
控制台我们能看到 8.png 这个是我们直接把唐诗三百首全部请求下来了,真实情况应该是分页的,下拉刷新加载更多,接口是支持的。我们来试试,第一次加载10条,那我们把内容也展开一下,加个序号,方便查看。 我们再list页面里实现这个功能.。。。。。 我不想写了。。。。烂尾了。。。。。。。。 大概思路就是你封装个组件,默认加载第一页的。然后。。。。 记得看英文官网就可以了。也挺简单的。

其他

数据缓存

import Http from "@/app/utils/http"

export async function getTangshiList(params = {}) {
  const { data } = await Http.post(
    "tangshi",
    { ...params },
    {
      revalidate: 1000, // 请求结果缓存*s
      tags: ["refresh"], // 缓存标签
    }
  )
  return data
}

// 根据id获取详情
export async function getTestDetail(id: string) {
  const { data } = await Http.get(`tangshi/${id}`)
  return data
}

刷新缓存

路径 src\app\api\revalidate

import { NextRequest, NextResponse } from "next/server"
import { revalidateTag } from "next/cache"

// 请求路径为 http://localhost:4002/api/revalidate?tag=refresh&_t=23
// tag=refresh 时,强制刷新
export async function GET(request: NextRequest) {
  const tag = request.nextUrl.searchParams.get("tag")!
  console.log("11111111", request.nextUrl.searchParams.get("tag"))
  revalidateTag(tag)
  return NextResponse.json({
    refresh: tag === "refresh",
    now: Date.now(),
    message: tag === "refresh" ? "刷新" : "没有刷新",
  })
}

获取参数

[id]/page.tsx 中口号【】就表示参数

import { getTestDetail } from "@/api"
import React from "react"

export default async function page({ params }: { params: { id: string } }) {
  const { id } = params
  const data: any = await getTestDetail(id)
  console.log("data", data)
  return (
    <div className="text-center mt-6">
      <h1 className="text-[20px] font-bold">{data.title}</h1>
      <h3 className="text-[16px] mt-2">{data.auth}</h3>
      <div className="mt-4 ">
        {data.content.map((item: string, index: number) => {
          return (
            <p key={index} className="text-[20px] font-bold">
              {item}
            </p>
          )
        })}
      </div>
      {/* desc 内容回显到html里面 */}
      <div
        className="m-auto text-center mt-12 w-6/12 "
        dangerouslySetInnerHTML={{ __html: data.desc }}
      ></div>

      <div>
        <button className="mt-4 bg-blue-500 text-white p-2 pl-6 pr-6 rounded ">
          <a href="/">首页</a>
        </button>
      </div>
    </div>
  )
}

打包

默认的是ssr服务器渲染

打包成静态

我的特殊,我打包成静态资源了。就不能使用服务器组件这些了。

/** @type {import('next').NextConfig} */

// 获取环境变量
const NODE_ENV = process.env.NODE_ENV;

const nextConfig = {
  // output: "export", // 导出静态的html。但是不能使用服务端渲染
  // Optional: 修改路由,路径后面添加/  `/about` -> `/about/`
  // trailingSlash: true,
  // Optional: Change the output directory `out` -> `dist`
  distDir: 'dist',

  // Image 不支持 export 导出。。。
  images: {
    // Optional: 修改图片域名
    // domains: ["localhost"], // 本地开发
    domains: ['localhost'],
  },
};

// 生产环境 配置导出静态html
if (NODE_ENV === 'production') {
  // 不能和代理同时使用Error: Specified "rewrites" cannot be used with "output: export".
  nextConfig.output = 'export';
} else {
  // 本地开发环境 配置代理
  nextConfig.rewrites = async () => {
    return [
      {
        source: '/api/:path*',
        destination: 'http://192.168.10.238:8020/:path*',
      },
    ];
  };
}

module.exports = nextConfig;

dist目录里面每个路由都生成了html文件。
所以是多页面。记得修改一下nginx


location / {
  # 将以.html结尾的请求添加Cache-Control头信息
  if ($uri ~* \.(html)$) {
      add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
  }

  # 默认情况下,根据路径转发,找不到加载index.html
  try_files $uri /$uri.html /index.html;
}

根据路径加载不同的html页面