🔥🔥🔥念头通达:手把手带你学next+tailwind(一)

2,066 阅读8分钟

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项目,如下图

image.png 依次选择是否使用typescript,eslint,tailwind,src目录,app router,是否自定义默认导入路径为@开头

image.png 看到success项目已经创建完成

目录结构如下

image.png

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的代码打包体积;

和行内样式有什么区别

  1. 行内样式不能写媒体查询,但是tailwind可以,它提供了多个响应式断点,在响应式不满足现有需求时,还可以在tailwind.config.js中配置需要支持的屏幕尺寸
  2. 支持悬停等效果,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

image.png

宽度|最小宽度|最大宽度

高度|最小高度最大高度

常用宽高,支持固定宽高,百分比宽高,视口宽高等

class含义
w-0width:0px;
w-pxwidth:1px;
w-fullwidth:100%;
w-screenwidth:100vh;
w-[100px]width:100px;
w-autowidth:auto;
w-1/2width:50%;
w-1/3width:33.3333%;
w-1/4width:25%;
w-2/4width:50%;
w-3/4width: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>
  );
}

image.png

颜色

背景色

通过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;

颜色值数字越大,颜色越深

image.png

如果想要的颜色没有,可以使用中括号写具体值,比如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>
  );
}


image.png

tailwind也支持在配置文件中定义颜色并使用,建议项目用这种方式,可以实现更好的代码复用

字体颜色

具体查看tailwind.nodejs.cn/docs/text-c… 和背景颜色取值类似,只不过是text-开头

属性含义
text-blackcolor:black;
text-whitecolor:white;

间距

padding

属性含义
p-0padding:0px;
p-1padding:4px;
px-1padding-left:4px;padding-right:4px;
pl-1padding-left:4px;
pr-1padding-right:4px;
py-1padding-top:4px;padding-bottom:4px;
pt-1padding-top:4px;
pb-1padding-bottom:4px;
p-[32px]padding:32px;

margin

margin

属性含义
m-0margin:0;
mt-1margin-top:4px;
mb-1margin-bottom:4px;
ml-1margin-left:4px;
mr-1margin-right:4px;
mx-1margin-left:4px;margin-right:4px;
my-1margin-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

image.png

image.png

字体

字体大小

字体大小通过text-表示

属性含义
text-xlfont-size:20px;
text-2xlfont-size:24px;
text-3xlfont-size:30px;
text-xsfont-size:12px;
text-smfont-size:14px;
text-basefont-size:16px;
text-lgfont-size:18px;

[字重](tailwind.nodejs.cn/docs/font-w…)

属性含义
font-normalfont-weight:400;
font-mediumfont-weight:500;
font-semiboldfont-weight:600;
font-thinfont-weight:100;

响应式设计

tailwind自带了断点支持不同尺寸屏幕的响应式,这个语法类似于媒体查询,官方固定是以最小宽度为断点

image.png

修改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>
  );

查看页面

image.png

大于1024px 的页面

image.png

默认页面

vscode用户推荐安装Tailwind CSS IntelliSense插件,可以用于感知tailwind属性,属性名检查等

其他属性可以直接查官方文档

路由配置

next cli

查看package.json文件

image.png

属性含义
npm run devnext开发环境启动
npm run buildnext打包
npm run startnext生产环境启动
npm run lintnext eslint检查

执行

npm run dev

开发环境启动,打开http://localhost:3000/ 页面

app router

next13开始默认使用app router,即文件路由系统,一个文件就是一个路由系统

app下的page.js文件对应/ 路由,如下图所示根路由对应的是app/page.tsx文件

image.png

修改app/global.css文件,将body的background属性删除,修改app/page.tsx组件

export default function Home() {
  return <div>根路由</div>;
}

再回到页面,可以看到内容变了

image.png

子路由

在app下新建order/page.tsx文件

//app/order/page.tsx
export default function Page() {
  return <div>暂未找到订单</div>;
}

访问http://localhost:3000/order 可以看到我们新创建的内容

image.png

路由参数

在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 如下图

image.png

访问http://localhost:3000/order/222

image.png

访问http://localhost:3000/order/lyllovelemon

image.png

布局

布局对应的是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的样式,

image.png

image.png

image.png

在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的布局

image.png

image.png

image.png

由此我们得知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>;
}

image.png

image.png

image.png

模板和布局有什么区别,两个同时存在时,布局嵌套模板。它们最大的区别是页面切换时,布局不会刷新,但是模板会刷新

模板用途

  1. 依赖于useState和useEffect的场景
  2. 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编码

image.png

下一期手把手带你学next涉及到缓存配置、server action

参考文档

next官方文档

tailwind官方文档