# next-ts-seed-project **Repository Path**: akaedu2012/next-ts-seed-project ## Basic Information - **Project Name**: next-ts-seed-project - **Description**: next+ts 标准工程化项目搭建,开箱即用的一个干净的项目模版 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2025-01-06 - **Last Updated**: 2025-01-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## 项目运行 ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` 浏览器打开 [http://localhost:30001](http://localhost:3001) ## 项目介绍 ### 1. 环境准备 1. Next.js 14版本对于Node.js最低的版本要求是 18.17.0,目前我的环境为 18.19.0。 2. VSCode相关插件安装:Prettier - Code formatter,ESlint,Nextjs snippets,vscode-styled-components,Tailwind CSS IntelliSense。 ### 2. 项目创建 为了更快的安装速度,没有切换镜像的,先切换 ```javascript npm config set registry https://registry.npmmirror.com ``` 然后使用create-next-app创建项目 ```javascript npx create-next-app@latest ``` 根据提示,创建项目 ![Example Image](./mdImage/1.jpg) 1. 输入项目的名称 2. 启用Typescript 3. 启用ESLint 4. 使用Tailwind CSS 5. 将src作为目录:方便将业务文件与配置文件区分开来 6. 使用App Router:可使用 Server component 和 Streaming 等新特性 7. 不自定义别名,使用默认的@代表src目录 ### 3. 配置 Prettier ESLint 脚手架已默认安装,现需配置prettier #### 1.安装 eslint ts默认规则补充 ```javascript 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 ```javascript //允许 ESLint 报告不符合 Prettier 格式化规则的代码 npm i eslint-plugin-prettier -D //用来解决与eslint的冲突 npm i eslint-config-prettier -D //安装prettier npm i prettier -D ``` #### 4.修改 .eslintrc.json ```javascript { "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 ```javascript { "endOfLine": "auto", "printWidth": 120, "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all", "bracketSpacing": true } ``` #### 6.新建 .prettierignore ```javascript /.next/ /node_modules .env*.local ``` #### 7.修改 package.json 修改 lint 启动命令 ```javascript "scripts": { "lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx --max-warnings 0", }, ``` 重启VSCode,打开src/app/layout.tsx,可以看到报错信息 ![layout Image](./mdImage/2.jpg) 这是因为在 .prettierrc 中配置了"singleQuote": true即单引号,而文件中使用的是双引号,所以编辑器提示报错 运行 npm run lint,可自动修复上述 eslint(prettier/prettier) 问题 ### 4.配置 VSCode 每次运行 npm run lint 修复 eslint(prettier/prettier) 问题比较麻烦。可配置VSCode,在文件保存时,自动格式化文档 根目录新建.vscode文件夹,在文件夹下新建settings.json ```javascript { "editor.codeActionsOnSave": { "source.fixAll": "explicit" } } ``` 新版的vscode配置为explicit,老版的为true 在插件中选择扩展设置 ![Alt text](./mdImage/3.jpg) 将每行代码的长度限制由80改为120(因为 .prettierrc中"printWidth": 120) ![Prettier Image](./mdImage/4.jpg) 保存之后,重启VScode ### 5.配置 import 导入顺序 在开发组件时,需要引用外部资源,当import的包很多时,例如有next、react第三方包,有自定义包,有Typescript类型文件,有样式文件等等。 如果对import的顺序不进行排序的话,会显得很杂乱,比如以下导入,在引入自定义组件的时候,可能会被另一个开发者引入类型文件,这时候规范引入顺序就显得很重要了 ```javascript import Header from '@/components/header'; import type { Metadata } from 'next'; import Tabs from '@/components/tabs'; ``` #### 1.安装 ```javascript npm i eslint-plugin-import -D ``` #### 2.修改 .eslintrc.json 在rules下添加以下配置 ```javascript "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,新增 ``` javascript #lock package-lock.json yarn.lock ``` ### 7.配置 Git hooks husky:一个为git客户端增加hook的工具 lint-staged:仅对Git 代码暂存区文件进行处理,配合husky使用 @commitlint/cli:让commit信息规范化 #### 1.创建 git 仓库(如果已创建可省略) ```javascript git init ``` #### 2.安装 ```javascript //固定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.运行 ```javascript //生成 .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同级,添加 ```javascript "lint-staged": { "src/**/*.{js,jsx,ts,tsx,json}": [ "npm run lint", "prettier --write" ] } ``` #### 5.新建 commitlint.config.js ```javascript module.exports = { extends: ['@commitlint/config-conventional'], }; ``` #### 6.提交格式 ```javascript git commit -m [optional scope]: //注意冒号后面有空格 - type:提交的类型(如新增、修改、更新等) - optional scope:涉及的模块,可选 - description:任务描述 ``` type类型: |类别|含义| |---|---| |feat|新功能(feature)| |fix|修补bug| |docs|文档(documentation)| |style| 格式(不影响代码运行的变动)| |refactor|重构(即不是新增功能,也不是修改bug的代码变动)| |test|增加测试| |chore|构建过程或辅助工具的变动| |perf|优化相关,比如提升性能、体验| |build|构建系统或外部依赖项的更改| |ci|构建系统或外部依赖项的更改| |revert|回滚到上一个版本| #### 7.提交示范 ```javascript git commit -m 'feat: 增加 xxx 功能' git commit -m 'bug: 修复 xxx 功能' ``` 提示:非规范提交,将commit提交失败 ### 8.配置端口号 Next.js的默认访问端口是3000,可修改启动命令,来改变端口号 修改package.json,在启动命令后增加 " -p 3001 " ,这样开发环境与生产环境的启动端口为3001 ```javascript "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 **1.定义服务端组件使用的环境变量** ```javascript //.env PRIVATE_KEY = '12345' ``` 在服务端组件中,使用 process.env.PRIVATE_KEY 获取值,在客户端组件中访问,会得到undefined **2.定义客户端组件使用的环境变量** 使用NEXT_PUBLIC_前缀定义客户端组件的环境变量 ```javascript //.env.development NEXT_PUBLIC_BASEURL = 'http://127.0.0.1:9000/api' //.env.production NEXT_PUBLIC_BASEURL = 'https://www.test.com/api' ``` 使用 process.env. 进行访问 ```javascript process.env.NEXT_PUBLIC_BASEURL ``` **3.配置Typescript类型提示** 项目根目录新建typings.d.ts ```javascript declare namespace NodeJS { interface ProcessEnv { /** 基础路径 */ NEXT_PUBLIC_BASEURL: string; } } ``` 修改tsconfig.json,在include中将新增文件加进来 ```javascript "include": [ //新增 "typings.d.ts", ], ``` 之后再VsCode中输入process.env,将会提示出NEXT_PUBLIC_BASEURL ### 10.配置临时环境变量 配置临时环境变量 1. 安装 ```javascript npm i cross-env -D ``` 2. 修改package.json ```javascript "scripts": { "dev": "cross-env NODE_ENV_PLATFORM=window next dev -p 3001", "dev:l": "cross-env NODE_ENV_PLATFORM=linux next dev -p 3001", }, ``` 3. 读取临时环境变量 ```javascript 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包 1. 安装 ``` javascript npm i sass ``` 2. 新建src/page.module.scss ```javascript $color: red; $primary-color: #64ff00; //定义局部样式 .title { color: $color; } //在CSS Module中导出scss变量 :export { primaryColor: $primary-color; } ``` 3. 使用,修改src/app/page.tsx ```javascript import style from '../page.module.scss'; export default async function Home() { return ( <> 111 222 ); } ``` 为了避免全局样式的影响,这里先在src/app/layout.tsx中去掉import './globals.css'; ### 12.配置 Scss 全局样式文件 1. 新建src/styles/index.scss ```javascript $btn-color: red ``` 2. 修改next.config.mjs ```javascript const nextConfig = { sassOptions: { additionalData: '@import "@/styles/index.scss";', }, }; ``` 3. 在scss文件中直接使用变量 ```javascript button{ color: $btn-color; } ``` ### 13. 配置 css-in-js css-in-js有多种方案,例如@mui/material,styled-jsx,styled-components,@vanilla-extract/css 等。我这边选择下载量最高,更新频率还可以的styled-components 1. 安装 ```javascript npm i styled-components ``` 2. 配置next.config.mjs ```javascript const nextConfig = { compiler: { styledComponents: true, }, }; ``` 3. 新建全局注册组件 新建lib/registry.tsx ```javascript 'use client'; import React, { useState } from 'react'; import { useServerInsertedHTML } from 'next/navigation'; import { ServerStyleSheet, StyleSheetManager } from 'styled-components'; export default function StyledComponentsRegistry({ children }: { children: React.ReactNode }) { // Only create stylesheet once with lazy initial state // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()); useServerInsertedHTML(() => { const styles = styledComponentsStyleSheet.getStyleElement(); styledComponentsStyleSheet.instance.clearTag(); return <>{styles}; }); if (typeof window !== 'undefined') return <>{children}; return {children}; } ``` 4. 包装根布局组件 修改src/app/layout.tsx ```javascript import StyledComponentsRegistry from '@/lib/registry'; const RootLayout = ({ children }: React.PropsWithChildren) => ( {children} ); export default RootLayout; ``` 5. 使用 ```javascript 'use client'; import styled from 'styled-components'; export default function Home() { return
  • 1
  • ; } const Li = styled.p` color: red; `; ``` CSS-in-JS的样式需要在客户端进行处理,因此需使用 'use client' 声明当前组件为客户端组件。该组件及其所有子组件都会在客户端进行渲染 提示:如果不经过如上配置,安装后直接使用,会出现样式闪烁等现象 ### 14. 配置状态管理仓库 仓库不选择传统的redux,@reduxjs/toolkit。而是选择更为轻便的zustand来管理 #### 1. 安装 zustand ```javascript npm i zustand ``` #### 2. 安装 immer immer:以更方便的方式处理不可变状态 ```javascript npm i immer ``` #### 3. 创建 store 新建src/store/user.ts ```javascript 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((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 ```javascript 'use client'; import useUserStore from '@/store/user'; const Info = () => { const { userInfo, token, updateUserInfo, updateAge, updateToken } = useUserStore(); return (
    姓名:{userInfo.name} 年龄:{userInfo.age}
    token:{token}
    ); }; export default Info; ``` ### 15. 配置 UI 组件库 1. 安装 ```javascript npm i antd ``` 2. 修改src/app/page.tsx,引入 antd 的按钮组件 ```javascript import React from 'react'; import { Button } from 'antd'; const Home = () => (
    ); export default Home; ``` 3. 目前按钮样式在首屏刷新时会闪动,还需配合@ant-design/nextjs-registry包使用 ```javascript npm i @ant-design/nextjs-registry ``` 4. 在 app/layout.tsx 中使用 ```javascript import { AntdRegistry } from '@ant-design/nextjs-registry'; import StyledComponentsRegistry from '@/lib/registry'; const RootLayout = ({ children }: React.PropsWithChildren) => ( {children} ); export default RootLayout; ``` 提示:Next.js App Router 当前不支持直接使用 . 引入的子组件,如 、 等,需要从路径引入这些子组件来避免错误 提示:有些浏览器版本过低,样式会出不来,可升级浏览器版本 ### 16. 配置 Typescript 声明 在window上添加自定义属性,或者使用不支持ts的第三方包是很常见的业务场景。因为项目使用了Typescript,因此需要配置相关的声明使其不报错 修改 next-env.d.ts,新增以下内容: ```javascript //在window上添加自定义属性 declare interface Window { custom: any; } //外部模块声明,已支持未使用Typescript的第三方库 declare module 'react-beautiful-dnd'; ``` 后续需根据业务场景进行配置 ### 17. 配置图片网络资源 使用Image组件时,如果图片是网络资源,需增加 remotePatterns 配置,否则图片会加载失败 修改next.config.mjs ```javascript const nextConfig = { images: { remotePatterns: [ { protocol: 'https', //图片资源的协议 hostname: 'www.test.com', //图片资源的域名 } ], }, }; ``` 后续需根据业务场景进行配置 ### 18. 配置打包资源分析 在打包的时候,分析包资源大小以及占比 #### 1. 安装 ```javascript npm i @next/bundle-analyzer -D ``` #### 2. 配置 next.config.mjs ```javascript import bundleAnalyzer from '@next/bundle-analyzer'; const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true', //当环境变量ANALYZE为true时开启 }); const nextConfig = { compiler: { styledComponents: true, }, sassOptions: { additionalData: '@import "@/assets/styles/index.scss";', }, images: { remotePatterns: [ { protocol: 'https', hostname: 'www.test.com', }, ], }, }; export default withBundleAnalyzer(nextConfig); ``` #### 3. 修改 package.json 将启动参数build修改为: ```javascript "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. 关闭严格模式 严格模式主要用于识别不安全的生命周期、过时的API等情况。但在开发模式下,会让组件执行两次,意味着会多次调用接口,因此需关闭该模式 ```javascript // next.config.js const nextConfig = { //新增 reactStrictMode: false, }; ``` ### 20. 开发环境使用HTTPS 修改package.json,配置启动参数:--experimental-https ```javascript "dev": "next dev --experimental-https", ```