next简介
next是一个react框架,支持SSR,SSG,ISR
它支持零配置,通过脚手架工具初始化项目后就可以自动编译和打包
文件系统路由,目前最新版本next15使用的是App router
支持混合模式,在一个项目中支持SSR和SSG混合使用
SSR
服务端渲染,相较于客户端渲染的优点在于首屏加载速度更快和更好的SEO
CSR
客户端渲染,传统的react应用默认就是走客户端渲染
SSG
静态渲染,适合内容为主的系统,比如CRM系统
ISR
增量静态生成
next vs nuxt vs nest
这三个框架有何区别?next是针对react的框架,nuxt是针对vue的框架,nest是一个node全栈框架
项目初始化
npx create-next-app@latest notes-app
通过npx create-next-app@latest notes-app
创建了一个项目名为notes-app的next项目,如下图
依次选择是否使用typescript,eslint,tailwind,src目录,app router,是否自定义默认导入路径为@开头
看到success项目已经创建完成
目录结构如下
notes-app
|-- app 存放路由(核心逻辑代码在这里写)
|-- components 新建目录,用于存放自定义组件
|-- public 存放公共文件,比如图片,字体等
|-- .eslintrc.json eslint配置
|-- .jsconfig.json 项目配置
|-- next.config.mjs next配置文件,mjs后缀表明为esmodule,也可以使用js后缀
|-- tailwind.config.ts tailwind配置文件
|-- tsconfig.json typescript配置文件
tailwind
tailwind是一个写原子化css的库,安装tailwindcss-animate和tailwindcss-merge库
npm install tailwindcss-animate tailwindcss-merge
why tailwind
tailwind是在html中写多个class
官方的说法是不再需要想class类名,直接使用预设的tailwind css;由于不再需要新建css类名,可以减少css的代码打包体积;
和行内样式有什么区别
- 行内样式不能写媒体查询,但是tailwind可以,它提供了多个响应式断点,在响应式不满足现有需求时,还可以在tailwind.config.js中配置需要支持的屏幕尺寸
- 支持悬停等效果,tailwind通过hover:,active:,focus:等状态变量支持悬停、激活、聚焦等效果
配置说明
修改tailwind.config.ts配置
import type { Config } from "tailwindcss";
import colors from "tailwindcss/colors"; // 导入tailwind颜色库
const config: Config = {
// contents中定义在哪些文件中tailwind会生效
// ** 代表目录 * 代表文件
// 以./pages/**/*.{js,ts,jsx,tsx,mdx}为例,说的是pages文件夹下的所有以js/ts/jsx/tsx/mdx为后缀的文件都会应用tailwind,依此类推
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
// colors定义了一些基础颜色类型,可以在项目中直接使用
colors: {
black: colors.black,
white: colors.white,
orange: colors.orange,
},
},
plugins: [require("tailwindcss-animate")],
};
export default config;
tailwind使用示例
宽高
新建路由app/demo/page.js(next14默认的文件名必须命名为page.js)
//app/demo/page.js
export default function Page() {
return <div className="w-32 h-full bg-orange-500 text-white">demo测试</div>;
}
创建了一个宽为128px,高为100%的div
常用宽高,支持固定宽高,百分比宽高,视口宽高等
class | 含义 |
---|---|
w-0 | width:0px; |
w-px | width:1px; |
w-full | width:100%; |
w-screen | width:100vh; |
w-[100px] | width:100px; |
w-auto | width:auto; |
w-1/2 | width:50%; |
w-1/3 | width:33.3333%; |
w-1/4 | width:25%; |
w-2/4 | width:50%; |
w-3/4 | width:75% |
在属性表中找不到的值,比如设置width:100px可以通过w-[100px]来表示,括号中加入具体的值,其他属性同理
w-[100px] 宽度为100px
min-w-1/2 最小宽度为50%
max-w-[200px] 最大宽度为200px
h-[32rem] 高度为32rem
min-h-[20em] 最小高度为20em
max-h-[200px] 最大高度为200px
形状
可以使用size快速创建正方形
// app/demo/page.js
export default function Page() {
return (
<div className="space-x-2 space-y-2">
<div className="size-16 bg-orange-50">size-16</div>
<div className="size-32 bg-orange-50">size-16</div>
<div className="size-64 bg-orange-50">size-64</div>
</div>
);
}
颜色
背景色
通过bg-orange等形式表示,官方支持的颜色查看文档
// app/demo/page.js
export default function Page() {
return (
<div className="space-x-2 space-y-2 flex flex-wrap">
<div className="size-16 bg-orange-50">size-16</div>
<div className="size-16 bg-orange-100">size-16</div>
<div className="size-16 bg-orange-200">size-16</div>
<div className="size-16 bg-orange-300">size-16</div>
<div className="size-16 bg-orange-400">size-16</div>
<div className="size-16 bg-orange-500">size-16</div>
<div className="size-16 bg-orange-600">size-16</div>
<div className="size-16 bg-orange-700">size-16</div>
</div>
);
}
其中flex等价于display:flex; flex-wrap等价于flex-wrap:wrap;
颜色值数字越大,颜色越深
如果想要的颜色没有,可以使用中括号写具体值,比如bg-[#ddd]表示
export default function Page() {
return (
<div className="space-x-2 space-y-2 flex flex-wrap">
<div className="size-16 bg-[#08ff9c]">size-16</div>
<div className="size-16 bg-[red]">size-16</div>
<div className="size-16 bg-[#ddd]">size-16</div>
<div className="size-16 bg-[black] text-white">size-16</div>
<div className="size-16 bg-[#086eff]">size-16</div>
<div className="size-16 bg-[rgba(255,255,255,0.2)]">size-16</div>
<div className="size-16 bg-orange-700">size-16</div>
</div>
);
}
tailwind也支持在配置文件中定义颜色并使用,建议项目用这种方式,可以实现更好的代码复用
字体颜色
具体查看tailwind.nodejs.cn/docs/text-c… 和背景颜色取值类似,只不过是text-开头
属性 | 含义 |
---|---|
text-black | color:black; |
text-white | color:white; |
间距
padding
属性 | 含义 |
---|---|
p-0 | padding:0px; |
p-1 | padding:4px; |
px-1 | padding-left:4px;padding-right:4px; |
pl-1 | padding-left:4px; |
pr-1 | padding-right:4px; |
py-1 | padding-top:4px;padding-bottom:4px; |
pt-1 | padding-top:4px; |
pb-1 | padding-bottom:4px; |
p-[32px] | padding:32px; |
margin
属性 | 含义 |
---|---|
m-0 | margin:0; |
mt-1 | margin-top:4px; |
mb-1 | margin-bottom:4px; |
ml-1 | margin-left:4px; |
mr-1 | margin-right:4px; |
mx-1 | margin-left:4px;margin-right:4px; |
my-1 | margin-top:4px;margin-bottom:4px; |
m-[20px] | margin:20px; |
修改page.js
export default function Page() {
return (
// space-x-2用于控制子元素水平距离,等价于子元素的mx-2
// space-y-2用于控制子元素的垂直距离,等于子元素的my-2
<div className="space-x-2 space-y-2 flex flex-wrap">
<div className="size-16 bg-white m-5 border border-black">size-16</div>
<div className="size-16 border border-black">size-16</div>
</div>
);
}
border等价于border-width:1px;
border-black等价于border-color:black;
border-solid等价于border-style:solid;不写默认为border-solid
字体
字体大小
字体大小通过text-表示
属性 | 含义 |
---|---|
text-xl | font-size:20px; |
text-2xl | font-size:24px; |
text-3xl | font-size:30px; |
text-xs | font-size:12px; |
text-sm | font-size:14px; |
text-base | font-size:16px; |
text-lg | font-size:18px; |
[字重](tailwind.nodejs.cn/docs/font-w…)
属性 | 含义 |
---|---|
font-normal | font-weight:400; |
font-medium | font-weight:500; |
font-semibold | font-weight:600; |
font-thin | font-weight:100; |
响应式设计
tailwind自带了断点支持不同尺寸屏幕的响应式,这个语法类似于媒体查询,官方固定是以最小宽度为断点
修改page.js,增加lg断点
//app/demo/page.js
export default function Page() {
return (
<div className="w-32 h-full lg:h-[300px] bg-orange-500 text-white border border-solid border-black">
demo测试
</div>
);
查看页面
大于1024px 的页面
默认页面
vscode用户推荐安装Tailwind CSS IntelliSense插件,可以用于感知tailwind属性,属性名检查等
其他属性可以直接查官方文档
路由配置
next cli
查看package.json文件
属性 | 含义 |
---|---|
npm run dev | next开发环境启动 |
npm run build | next打包 |
npm run start | next生产环境启动 |
npm run lint | next eslint检查 |
执行
npm run dev
开发环境启动,打开http://localhost:3000/ 页面
app router
next13开始默认使用app router,即文件路由系统,一个文件就是一个路由系统
app下的page.js文件对应/ 路由,如下图所示根路由对应的是app/page.tsx文件
修改app/global.css文件,将body的background属性删除,修改app/page.tsx组件
export default function Home() {
return <div>根路由</div>;
}
再回到页面,可以看到内容变了
子路由
在app下新建order/page.tsx文件
//app/order/page.tsx
export default function Page() {
return <div>暂未找到订单</div>;
}
访问http://localhost:3000/order 可以看到我们新创建的内容
路由参数
在order下新建[id]/page.tsx
//app/order/[id]/page.tsx
export default function Page({ params }: { params: { id: string } }) {
// 通过params参数拿到路由[id]的值
const { id } = params;
return <div>当前订单id:{id}</div>;
}
访问http://localhost:3000/order/1 如下图
访问http://localhost:3000/order/222
访问http://localhost:3000/order/lyllovelemon
布局
布局对应的是layout.tsx,它会在多个页面共享; 修改app/layout.tsx代码
// app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "柠檬酱测试",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<div className="bg-orange-400 text-white w-screen h-28 flex items-center justify-start">
header
</div>
{children}
</body>
</html>
);
}
- html和body标签是必须的
- 元数据定义后面会讲,在这里等价于在页面增加了一个meta标签,title为Create Next App,修改title为柠檬酱测试
- 在body下新增header部分
依次访问http://localhost:3000/ ,http://localhost:3000/order ,http://localhost:3000/order/1 可以看到每个页面都应用了header的样式,
在order下新建layout.tsx
//app/order/layout.tsx
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
order下的layout
{children}
</div>
);
}
依次访问http://localhost:3000/ ,http://localhost:3000/order ,http://localhost:3000/order/1 可以看到只有order目录下的页面应用了layout的布局
由此我们得知layout支持嵌套,而且只会对同一个文件夹已经同一级文件夹下的子路由生效
在layout.tsx和page.tsx同时存在时,layout嵌套page
模板
模板对应template.tsx文件,在app下新建template.tsx
//app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div>template{children}</div>;
}
模板和布局有什么区别,两个同时存在时,布局嵌套模板。它们最大的区别是页面切换时,布局不会刷新,但是模板会刷新
模板用途
- 依赖于useState和useEffect的场景
- Suspense在切换页面时需要重新加载的场景
加载状态
等价于Suspense组件,在app下新建loading.tsx
404
在app下新建not-found.tsx文件
此时的目录结构如下
└── app/
├── page.tsx // / 路由
├── layout.tsx // 根布局
├── template.tsx // 根模版
├── loading.tsx // 加载状态
├── error.tsx // 错误定义
└── not-found.tsx // 404
├── order
└── page.tsx // /order路由
└── [id]
└── page.tsx // order/[id] 路由
- app/page.tsx 对应 / 路由
- app/order/page.tsx 对应/order路由
- app/order/[id]/page.tsx 对应/order/[id] 路由,这个id可以是任意值,不过输入中文会被URL编码
下一期手把手带你学next涉及到缓存配置、server action