前言
先附上源码地址:next-ts-seed
服务端渲染具有诸多优点,包括优化SEO、加快首屏加载速度、增强安全性等。针对注重SEO的C端项目而言,服务端渲染是首选方式,也是每位前端工程师必备的技术栈之一。
1. 环境准备
- Next.js 14版本对于Node.js最低的版本要求是 18.17.0,目前我的环境为 18.19.0
- VSCode相关插件安装:Prettier - Code formatter,ESlint,Nextjs snippets,vscode-styled-components
2. 项目创建
为了更快的安装速度,没有切换镜像的,先切换
js
npm config set registry https://registry.npmmirror.com
运行
js
npx create-next-app@latest
根据提示,创建项目
-
输入项目的名称
-
启用Typescript
-
启用ESLint
-
不使用Tailwind CSS(根据个人需求)
-
将src作为目录:方便将业务文件与配置文件区分开来
-
使用App Router:可使用 Server component 和 Streaming 等新特性
-
不定义别名,使用默认的@代表src目录
3. 配置 Prettier
ESLint 脚手架已默认安装,现需配置prettier
1. 安装 eslint ts默认规则补充
js
npm i @typescript-eslint/eslint-plugin@6.21.0 -D
提示:@typescript-eslint/eslint-plugin目前版本已到7.1,但脚手架自带的@typescript-eslint/parser版本为6.21.0,因此安装的时候需手动指定版本。之后如果脚手架升级,只需安装对应版本即可
脚手架自带的@typescript-eslint/typescript-estree6.21.0版本不支持typescript高于5.4.0的版本,而项目中的package.json中"typescript": "^5",即安装typescript5的最新版本,目前typescript最新版本为5.4.2,会不兼容,因此需指定typescript的版本
2. 指定Typescript版本
如果已经安装了,先执行npm uninstall typescript
进行卸载,然后运行npm i typescript@^5.2.2 -D
进行安装
3. 安装 prettier
js
//允许 ESLint 报告不符合 Prettier 格式化规则的代码
npm i eslint-plugin-prettier -D
//用来解决与eslint的冲突
npm i eslint-config-prettier -D
//安装prettier
npm i prettier -D
4. 修改 .eslintrc.json
js
{
"extends": [
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"eslint-config-prettier"
],
"plugins": ["prettier"],
"rules": {
"@typescript-eslint/no-explicit-any": ["off"], //允许使用any
"@typescript-eslint/ban-ts-comment": "off", //允许使用@ts-ignore
"@typescript-eslint/no-non-null-assertion": "off", //允许使用非空断言
"@typescript-eslint/no-var-requires": "off", //允许使用CommonJS的写法
"no-console": [
//提交时不允许有console.log
"warn",
{
"allow": ["warn", "error"]
}
],
"no-debugger": "warn"
}
}
5. 新建 .prettierrc
js
{
"endOfLine": "auto",
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"bracketSpacing": true
}
6. 新建 .prettierignore
js
/.next/
/node_modules
.env*.local
7. 修改 package.json
修改 lint 启动命令
js
"scripts": {
"lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx --max-warnings 0",
},
重启VSCode,打开src/app/layout.tsx,可以看到报错信息
这是因为在 .prettierrc 中配置了"singleQuote": true
即单引号,而文件中使用的是双引号,所以编辑器提示报错
运行 npm run lint
,可自动修复上述 eslint(prettier/prettier) 问题
4. 配置 VSCode
每次运行 npm run lint 修复 eslint(prettier/prettier) 问题比较麻烦。可配置VSCode,在文件保存时,自动格式化文档
根目录新建.vscode文件夹,在文件夹下新建settings.json
js
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
}
}
新版的vscode配置为explicit,老版的为true
在插件中选择扩展设置
将每行代码的长度限制由80改为120(因为 .prettierrc中"printWidth": 120,)
保存之后,重启VScode
5. 配置 import 导入顺序
在开发组件时,需要引用外部资源,当import的包很多时,例如有next、react第三方包,有自定义包,有Typescript类型文件,有样式文件等等。
如果对import的顺序不进行排序的话,会显得很杂乱,比如以下导入,在引入自定义组件的时候,可能会被另一个开发者引入类型文件,这时候规范引入顺序就显得很重要了
js
import Header from '@/components/header';
import type { Metadata } from 'next';
import Tabs from '@/components/tabs';
1. 安装
js
npm i eslint-plugin-import -D
2. 修改 .eslintrc.json
在rules下添加以下配置
js
"import/order": [
"error",
{
//按照分组顺序进行排序
"groups": ["builtin", "external", "parent", "sibling", "index", "internal", "object", "type"],
//通过路径自定义分组
"pathGroups": [
{
"pattern": "react*",
"group": "builtin",
"position": "before"
},
{
"pattern": "@/components/**",
"group": "parent",
"position": "before"
},
{
"pattern": "@/utils/**",
"group": "parent",
"position": "after"
},
{
"pattern": "@/apis/**",
"group": "parent",
"position": "after"
}
],
"pathGroupsExcludedImportTypes": ["react"],
"newlines-between": "always", //每个分组之间换行
//根据字母顺序对每个组内的顺序进行排序
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
]
运行 npm run lint,可统一修复排序问题
在开发的过程中,因为配置了VSCode保存时自动修复,因此配置好之后,不需要我们再操心import导入顺序的问题
6. 配置 .gitignore
Next.js目前使用基于Rust的编译器SWC来编译JavaScript/TypeScript。SWC的安装受到Node.js版本等因素的影响,如果版本不一致可能导致启动失败,出现类似 'Failed to load SWC binary for win32/x64' 的错误提示。
解决方案一:package-lock.json文件不上传到GitHub上,修改.gitignore,新增
js
#lock
package-lock.json
yarn.lock
7. 配置 Git hooks
husky:一个为git客户端增加hook的工具
lint-staged:仅对Git 代码暂存区文件进行处理,配合husky使用
@commitlint/cli:让commit信息规范化
1. 创建 git 仓库
js
git init
2. 安装
js
//固定husky的版本
npm i husky@8.0.3 -D
npm i lint-staged -D
npm i @commitlint/cli -D
npm i @commitlint/config-conventional -D
3. 运行
js
//生成 .husky 的文件夹
npx husky install
// 添加 hooks,会在 .husky 目录下生成一个 pre-commit 脚本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"
// 添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
4. 修改 package.json
与devDependencies同级,添加
js
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json}": [
"npm run lint",
"prettier --write"
]
}
5. 新建 commitlint.config.js
js
module.exports = {
extends: ['@commitlint/config-conventional'],
};
6. 提交格式
js
git commit -m <type>[optional scope]: <description> //注意冒号后面有空格
- type:提交的类型(如新增、修改、更新等)
- optional scope:涉及的模块,可选
- description:任务描述
type类型:
类别 | 含义 |
---|---|
feat | 新功能 |
fix | 修复 bug |
style | 样式修改(UI校验) |
docs | 文档更新 |
refactor | 重构代码(既没有新增功能,也没有修复 bug) |
perf | 优化相关,比如提升性能、体验 |
test | 增加测试,包括单元测试、集成测试等 |
build | 构建系统或外部依赖项的更改 |
ci | 自动化流程配置或脚本修改 |
revert | 回退某个commit提交 |
7. 提交示范
js
git commit -m 'feat: 增加 xxx 功能'
git commit -m 'bug: 修复 xxx 功能'
提示:非规范提交,将commit提交失败
8. 配置端口号
Next.js的默认访问端口是3000,可修改启动命令,来改变端口号
修改package.json,在启动命令后增加 " -p 3001 " ,这样开发环境与生产环境的启动端口为3001
js
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start -p 3001"
},
9. 配置环境变量
项目根目录新建 .env、.env.development、.env.production、.env.local,分别代表着所有环境变量,开发环境变量,生产环境变量,本地环境变量
覆盖优先级为:.env.local > .env.production | .env.development > .env
使用NEXT_PUBLIC_前缀定义变量
js
//.env.development
NEXT_PUBLIC_BASEURL = 'http://127.0.0.1:9000/api'
//.env.production
NEXT_PUBLIC_BASEURL = 'https://www.test.com/api'
使用 process.env. 进行访问
js
process.env.NEXT_PUBLIC_BASEURL
如果想自定义环境变量的文件位置,或者定义多个工程的环境变量,可使用 dotenv。可参考我的另一篇文章:[Vite 进阶] 配置环境变量(包含多工程、多环境配置)
10. 配置临时环境变量
在启动或编译应用程序时,我们常常需要根据不同的参数走不同的配置
- 安装
js
npm i cross-env -D
- 修改package.json
js
"scripts": {
"dev": "cross-env NODE_ENV_PLATFORM=window next dev -p 3001",
"dev:l": "cross-env NODE_ENV_PLATFORM=linux next dev -p 3001",
},
- 读取临时环境变量
js
process.env.NODE_ENV_PLATFORM
运行 npm run dev,npm run dev:l 将会得到不同的参数配置
11. 配置 Scss
随着Next.js逐渐弃用Less,Antd5也弃用Less改用css-in-js方案,这里改用Sass
Next.js内置支持Sass,只需安装sass包
- 安装
js
npm i sass
- 新建src/page.module.scss
js
$color: red;
$primary-color: #64ff00;
//定义局部样式
.title {
color: $color;
}
//在CSS Module中导出scss变量
:export {
primaryColor: $primary-color;
}
- 使用,修改src/app/page.tsx
js
import style from './page.module.scss';
export default async function Home() {
return (
<>
<span className={style.title}>111</span>
<span style={{ color: style.primaryColor }}>222</span>
</>
);
}
为了避免全局样式的影响,这里先在src/app/layout.tsx中去掉import './globals.css';
12. 配置 Scss 全局样式文件
- 新建src/styles/index.scss
js
$btn-color: red
- 修改next.config.mjs
js
const nextConfig = {
sassOptions: {
additionalData: '@import "@/styles/index.scss";',
},
};
- 在scss文件中直接使用变量
js
button{
color: $btn-color;
}
13. 配置 css-in-js
css-in-js有多种方案,例如@mui/material,styled-jsx,styled-components,@vanilla-extract/css
等。我这边选择下载量最高,更新频率还可以的styled-components
- 安装
js
npm i styled-components
- 使用
js
'use client';
import styled from 'styled-components';
export default function Home() {
return <Li>1</Li>;
}
const Li = styled.p`
color: red;
`;
CSS-in-JS的样式需要在客户端进行处理,因此需使用 'use client' 声明当前组件为客户端组件。该组件及其所有子组件都会在客户端进行渲染
14. 配置状态管理仓库
仓库不选择传统的redux,@reduxjs/toolkit。而是选择更为轻便的zustand来管理
1. 安装 zustand
js
npm i zustand
2. 安装 immer
immer:以更方便的方式处理不可变状态
js
npm i immer
3. 创建 store
新建src/store/user.ts
js
import { produce } from 'immer';
import { create } from 'zustand';
interface UserInfo {
name: string;
age: number;
}
interface UserState {
userInfo: UserInfo;
token: string;
updateUserInfo: (parmas: UserInfo) => void;
updateAge: (params: number) => void;
updateToken: (params: string) => void;
}
// 创建状态存储
const useUserStore = create<UserState>((set) => ({
userInfo: {
name: 'zhangsan',
age: 23,
},
token: 'S1',
//更新整个对象
updateUserInfo: (userInfo) => set({ userInfo }), //合并userInfo
//更新对象中某个属性
updateAge: (age) =>
set(
produce((state) => {
state.userInfo.age = age;
}),
),
//更新原始数据类型
updateToken: (token) => set({ token }),
}));
export default useUserStore;
4. 使用 store
修改src/app/page.tsx
js
'use client';
import { useCallback } from 'react';
import useUserStore from '@/store/user';
const Info = () => {
const { userInfo, token, updateUserInfo, updateAge, updateToken } = useUserStore();
const hanlderUser = useCallback(() => {
updateUserInfo({ name: 'lisi', age: 24 });
}, [updateUserInfo]);
const handlerAge = useCallback(() => {
updateAge(userInfo.age + 1);
}, [updateAge, userInfo.age]);
const handlerToken = useCallback(() => {
updateToken('23652');
}, [updateToken]);
return (
<div className="App">
<div>
姓名:{userInfo.name} 年龄:{userInfo.age}
</div>
<div>token:{token}</div>
<button onClick={hanlderUser}>更新用户</button>
<button onClick={handlerAge}>更新年龄</button>
<button onClick={handlerToken}>更新token</button>
</div>
);
};
export default Info;
zustand更多的使用方式可参考我的另一篇文章:还在用redux?试试更方便的zustand
15. 配置 UI 组件库
- 安装
js
npm i antd
- 修改src/app/page.tsx,引入 antd 的按钮组件
js
import React from 'react';
import { Button } from 'antd';
const Home = () => (
<div className="App">
<Button type="primary">Button</Button>
</div>
);
export default Home;
- 目前按钮样式在首屏刷新时会闪动,还需配合@ant-design/nextjs-registry包使用
js
npm i @ant-design/nextjs-registry
- 在 app/layout.tsx 中使用
js
import React from 'react';
import { AntdRegistry } from '@ant-design/nextjs-registry';
const RootLayout = ({ children }: React.PropsWithChildren) => (
<html lang="en">
<body>
<AntdRegistry>{children}</AntdRegistry>
</body>
</html>
);
export default RootLayout;
提示:Next.js App Router 当前不支持直接使用 . 引入的子组件,如 <Select.Option />、<Typography.Text /> 等,需要从路径引入这些子组件来避免错误
16. 配置 Typescript 声明
在window上添加自定义属性,或者使用不支持ts的第三方包是很常见的业务场景。因为项目使用了Typescript,因此需要配置相关的声明使其不报错
修改 next-env.d.ts,新增以下内容:
js
//在window上添加自定义属性
declare interface Window {
custom: any;
}
//外部模块声明,已支持未使用Typescript的第三方库
declare module 'react-beautiful-dnd';
后续需根据业务场景进行配置
17. 配置图片网络资源
使用<Image>
组件时,如果图片是网络资源,需增加 remotePatterns 配置,否则图片会加载失败
修改next.config.mjs
js
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https', //图片资源的协议
hostname: 'www.test.com', //图片资源的域名
}
],
},
};
后续需根据业务场景进行配置
18. 配置打包资源分析
在打包的时候,分析包资源大小以及占比
1. 安装
js
npm i @next/bundle-analyzer -D
2. 配置 next.config.js
目前@next/bundle-analyzer的版本为14.1.3,只支持CommonJS的写法,而项目自带的next.config.mjs代表着ESModule写法,因此需先修改文件名,将其改为next.config.js
js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
const nextConfig = {
sassOptions: {
additionalData: '@import "@/styles/index.scss";',
},
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'www.test.com',
},
],
},
};
module.exports = withBundleAnalyzer(nextConfig);
3. 修改 package.json
将启动参数build修改为:
js
"build": "cross-env ANALYZE=true next build",
4. 打包
运行 npm run build
进行打包,.next文件夹下会生成analyze文件夹
.next/analyze/nodejs.html:显示的是服务端文件包的大小,即.next/server文件夹下的资源
.next/analyze/client.html:显示的是客户端文件包的大小,即.next/static文件夹下的资源
19. 路由
结尾
后续会持续更新本篇文章内容。如果有什么好的建议,欢迎在评论区留言