引言
因公司项目需求, 开始研究 NextJS 本篇主要记录个人在搭建项目的一些记录!!
如果你用过 React
并且想尝试 NextJS, 来搭建 React
项目, 那么本篇文章一定可以给你带来些收获!
一、初始 Next
正如官网所描述的, NextJS 是一个构建于 NodeJS
之上的一个 Web
开发框架。它基于 React
特性进行了一些列的扩展!!
首先它是基于 NodeJS
环境, 所以我们在部署 NextJS 需要有 NodeJS
环境, 因为它是基于 NodeJS
环境或者说它本质上在部署时是通过 NodeJS
启动了一个服务, 它既有客户代码、又有服务端的代码, 所以基于 NodeJS
你可以做很多事情!!
目前 React
官方文档优先推荐的创建项目的方式便是使用 NextJS
。目前 NextJS 在 GitHub
已有 116k
star
, Npm
周下载量 241W
左右, 堪称最受欢迎的开发框架之一。
它主要做的事情有以下几点:
- 完善的工程化机制: 通过脚手架搭建项目, 该有的配置都给你弄好, 同时还支持扩展配置
- 基于文件、目录的路由系统, 支持动态路由、路由分组、平行路由、路由拦截等特性
- 基于
NodeJS
可配置中间件, 对用户访问的页面进行重定向、鉴权等操作 - 基于
NodeJS
可以编写简单的API
接口 - 支持服务端组件、客户端组件, 服务端组件可直接调用
NodeJS
相关API
、可直接访问数据库 - 支持各种渲染方式
CSR
(Client-side Rendering
): 客户端渲染, 简单理解就是正常的React
项目, 页面的渲染是由浏览器端通过JS
渲染出来的SSR
(Server-side Rendering
): 服务端渲染, 每次用户访问路由, 在服务端直接通过NodeJS
对页面进行渲染, 渲染成完整的页面后返回给用户, 从而提高页面访问速度和SEO
, 因为是服务端生成的页面, 所以你可以轻松访问数据、调用接口(速度更快)、或者直接调用NodeJS
API
访问服务端本地文件资源...SSG
(Static Site Generation
): 静态页面生成, 在项目编译、部署阶段, 就直接在服务端直接通过NodeJS
将相关页面进行渲染, 最后生成多个HTML
文件, 当用户请求相关路由其实就是访问这些HTML
页面ISR
(Incremental Static Regeneration
): 增量静态再生, 可以理解是SSG
静态页面生成的补充,SSG
静态页面生成的页面是在编译阶段就生成了, 后续用户的访问的页面将永远不变, 而ISR
增量静态再生的作用就是可以服务运行期间重新生成新的静态页面, 比如可以定时每隔一段时间生成新的静态页面
- 支持国际化, 可能很方便进行配置
- 提供
Image
、Font
、Link
、Script
等组件, 针对相应的资源请求做了很多优化处理, 比如Image
组件默认支持懒加载等功能 - 更多功能期待你的探索...
二、初始化项目
下面开始正式搭建项目....
2.1 初始化
这里我们直接按照 官方文档 来, 通过官方脚手架 create-next-app
来快速搭建项目
执行命令 npx create-next-app@latest
, 根据提示选择需要的配置项
好的, 项目已经起来了, 是不是很简单。
2.2 目录介绍
生成的项目配置如下, 可以简单看下注释说明:
- 同
React
项目, 一般情况下public
目录下的资源会被当做静态资源被打包, 如此在项目中我们就可以通过/
来引用里面的资源
diff
<div className="relative flex">
<Image
+ src="/next.svg"
alt="Next.js Logo"
/>
</div>
src/app
目录下的资源会根据特定的文件、目录被解析为相应的路由, 比如当我们:
- 访问
/
路径, 将访问src/app/page.tsx
页面 - 访问
/about
路径, 将访问src/app/about/page.tsx
页面 - 访问
/about/list
路径, 将访问src/app/about/list/page.tsx
页面
src/app
目录下不同文件将会有不同的作用, 比如:
page.tsx
将会被作为页面被展示layout.tsx
将会被作为当前页面、下级页面的父容器被嵌套使用- 除此之外还有很多其他特殊作用的页面, 比如:
template.js
、not-found.js
、error.tsx
、loading.tsx
、not-find
、route.js
、middleware.js
2.3 常见脚本介绍
初始化完项目, 我们肯定想启动项目, 所以我们第一件事自然是看下 package.json
中的 npm
脚本
js
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
如你所见, 这里有四个 npm
脚本 "dev": "next dev"
、"build": "next build"
、"start": "next start"
、"lint": "next lint"
, 相信你从命令名称也能猜到这几个命令的作用, 下面我们进行简单介绍:
2.3.1 next dev
开发模式下, 使用 next dev
来运行项目, 开发模式下将具有 热加载
、错误报告
等功能, 同时默认情况下, 项目运行后可通过 http://localhost:3000
进行访问, 如下可以通过执行 npm run dev
来运行 next dev
命令
当然如果你需要修改端口可通过 -p
参数来调整, 比如 next dev -p 3001
, 如下执行命令 npm run dev -- -p 3001
, 通过 --
为 npm
脚本进行传参, 所以实际上执行的就是 next dev -p 3001
如果你需要修改主机名, 可通过 -H
来实现, 比如: next dev -H 192.168.0.105
, 这样的话其他局域网内用户就可以通过 IP
来访问你的项目了, 如下执行命令 npm run dev -- -H 192.168.0.105
, 通过 --
为 npm
脚本进行传参, 所以实际上执行的就是 next dev -H 192.168.0.105
2.3.2 next build
构建项目, 将会对项目进行打包, 构建生成环境所需的产物, 如下可以通过执行 npm run build
来运行 next build
命令, build
的产物都会放在 .next
目录下
next build
执行完成会输出所有路由(页面、接口)、相关资源的路径、大小, 如下图所示:
Route (app)
: 列举出当前项目所有的路由(页面、接口), 简单理解就是列出项目中定义的所有页面、接口First Load JS shared by all
: 表示首次加载任何路由(页面、接口), 所需要加载的共同的资源路径、大小; 简单理解就是所有页面、接口都需要用到的资源Size
: 表示当前何路由(页面、接口), 在加载时所需的最小资源大小(非公共的资源大小)First Load JS
: 首次加载当前路由(页面、接口)所需的资源大小, 简单来讲就是如果访问当前页面、接口, 需要加载的资源大小, 其实就是等于Size
加上First Load JS shared by all
的总和
--debug
参数, next build
命令有个 --debug
参数, 加上该参数后, 在 build
成功后会额外输出 next.config.js 中 rewrites
、redirects
、headers
等配置
--profile
参数, next build
命令有个 --profile
参数, 默认情况下 React
的 Profiler(性能分析) 只会在开发环境下生效, 但是如果我们在 build
时加上 --profile
参数, 如此在生产环境下, 也是可以使 Profiler(性能分析) 生效
2.3.3 next start
在生产环境部署时, 当我们执行 next build
构建出生产代码之后, 就可以通过 next start
来运行生产环境项目
跟 next dev
开发模式相同, 程序默认开启在 http://localhost:3000
如果你想更改端口号, 可通过 -p
参数来调整, 例: next start -p 3001
, 如下执行命令 npm run dev -- -p 3001
, 通过 --
为 npm
脚本进行传参, 所以实际上执行的就是 next dev -p 3001
如果你需要修改主机名, 可通过 -H
来实现, 比如: next start -H 192.168.0.105
, 这样的话其他局域网内用户就可以通过 IP
来访问你的项目了, 如下执行命令 npm run dev -- -H 192.168.0.105
, 通过 --
为 npm
脚本进行传参, 所以实际上执行的就是 next dev -H 192.168.0.105
2.3.4 next lint
执行 next lint
命令, 将会针对 pages/
、app/
、components/
、lib/
、src/
等目录下的代码进行 ESLint
语法校验, 同时如果你没有安装 ESLint
, 执行该该命令会提供一个安装指导!
如果你想要指定检查的目录, 可使用 --dir
参数, 如下命名将会检测 utils
目录下的所有代码
2.3.5 next info
除了上面几个命名, 官方还提供了一个 next info
命令, 可用于打印当前系统相关的信息, 在你遇到问题, 需要请求帮助的时候就特别有用, 你可以将这些信息和你的问题一起上报给他人, 方便其他人帮忙排查问题!
三、切换 pnpm
3.1 pnpm 简介
本质上 pnpm
本质上就是一个包管理器, 从作用上来看跟 npm/yarn
其实没有区别, 但它作为杀手锏的两个优势在于:
- 包安装速度极快
- 高效利用磁盘空间:
- 不会重复安装同一个包, 用
npm/yarn
的时候, 如果100
个项目都依赖lodash
, 那么lodash
很可能就被安装了100
次, 磁盘中就有100
个地方写入了这部分代码。但在使用pnpm
只会安装一次, 磁盘中只有一个地方写入, 后面再次使用都会直接使用hardlink
(硬链接) - 即使一个包的不同版本,
pnpm
也会极大程度地复用之前版本的代码。举个例子, 比如lodash
有100
个文件, 更新版本之后多了一个文件, 那么磁盘当中并不会重新写入101
个文件, 而是保留原来的100
个文件的hardlink
(硬链接), 仅仅写入那一个新增的文件!!
- 支持
monorepo
:pnpm
与npm/yarn
另外一个很大的不同就是支持了monorepo
, 体现在各个子命令的功能上, 比如在根目录下pnpm add A -r
, 那么所有的package
中都会被添加A
这个依赖, 当然也支持--filter
字段来对package
进行过滤 - 安全性高: 之前在使用
npm/yarn
的时候, 由于node_module
的扁平结构, 如果A
依赖B
,B
依赖C
, 那么A
当中是可以直接使用C
的, 但问题是A
当中并没有声明C
这个依赖。因此会出现这种非法访问的情况。但pnpm
脑洞特别大, 自创了一套依赖管理方式, 可以很好的 规避非法访问依赖的风险的, 从而保证了安全性
3.2 迁移
- 全局安装
pnpm
这里我们直接通过npm
来进行安装
sh
npm i -g pnpm
-
删除
node_modules
目录 -
执行
pnpm import
, 该命令的作用其实就是基于现有的package-lock.json
等lock
文件生成一份pnpm-lock.yaml
sh
pnpm import
- 删除
package-lock.json
等多余的lock
文件, 然后执行pnpm i
来重新安装依赖
sh
pnpm i
- 到处迁移基本已经完毕, 但是这里我们还可以加个配置, 避免其他人通过
npm install
或yarn
来安装依赖, 从而避免项目依赖被污染!! 如下代码, 新增了一条npm
脚本preinstall
该脚本在安装依赖前会被执行, 然后通过only-allow
来限制包管理器
json
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
补充:
- 即便有
pnpm import
的帮忙, 但一切皆有可能, 所以不管如何请提前做好备份, 在迁移完成后一段时间确保在发生问题时能够及时回退- 如果可以的话, 我们完全可以在最开始时就使用
pnpm
来初始化NextJS
项目, 例如:
sh
pnpm create next-app new-next-app
# 使用 TS
pnpm create next-app ts-next-app --template typescript
到此执行 pnpm dev
将运行开发环境:
sh
$ pnpm dev
> init@0.1.0 dev /Users/qianyin/coding/next-play/init
> next dev
▲ Next.js 14.1.0
- Local: http://localhost:3000
浏览器访问 http://localhost:3000
将呈现如下画面:
四、安装 NextUI
4.1 为什么要选择 NextUI?
首先, NextUI 是 NextJS
官方出品的一个 UI
组件库, 和常规的 UI
组件库的商务风、老旧相比, NextUI
主打一个好看、现代风, 当然从官网来看, 确实挺好看的!!
NextUI
是 React
的 UI
库, 可帮助您构建美观且易于访问的用户界面, 它是在 Tailwind CSS 和 React Aria 之上创建。并且使用 Framer Motion 来对某些组件进行动画处理, 从而能够以更直接、更高效的方式处理这些动画。
NextUI
的优点如下:
- 构建于
Tailwind CSS
之上, 这意味着捆绑包中没有运行时样式, 也没有不必要的类 - 自动暗模式识别,
NextUI
在检测到HTML theme prop
变化时自动更改主题 NextUI
是完全类型化的, 可以最大限度地减少学习曲线, 并提供最佳的开发人员体验
4.2 安装
根据 官网 来, 官网提供了两种安装方式, 这里我采用的是全局安装方式
- 安装依赖包
sh
pnpm i @nextui-org/react framer-motion
Tailwind CSS
设置
NextUI
构建在Tailwind CSS
之上, 因此我们这里还需要配置下tailwind.config.ts
(Tailwind CSS
配置文件):
content
配置项, 新增@nextui-org
相关文件, 需要解析里面的Tailwind CSS
darkMode: 'selector'
, 修改Tailwind CSS
主题设置模式, 默认情况下是会根据浏览器主题, 来主动切换主题, 这里我将其修改为selector
, 如此就可以根据外层的类名来实现主题的切换, 具体参考: toggling-dark-mode-manuallynextui()
, 新增NextUI
相关插件
diff
import type { Config } from 'tailwindcss'
+ import { nextui } from '@nextui-org/react'
const config: Config = {
content: [
...
+ "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", // nextui 相关
],
theme: {
....
},
+ darkMode: 'selector', // 修改主题切换的模式, see: https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
+ plugins: [
+ nextui(), // nextui 相关
+ ],
}
export default config
上面👆🏻我们修改了, 主题切换的模式, 所以我们还需要在 app/layout.tsx
中为根元素 html
设置一个 className
, 来指定项目的主题色! 如下代码, 我设置的 className
为 dark
即暗色模式
diff
+ <html lang="en" className="dark">
....
</html>
Provider Setup
在入口处
app/layout.tsx
中使用NextUI
组件NextUIProvider
对所有页面内容进行一个全局包裹
- 设置了
use client
因为NextUIProvider
需要在客户端组件中使用- 删除
metadata
相关配置, 因为它只能在服务端组件中导出, 这里可以移到page.tsx
页面中- 使用
NextUIProvider
来包裹所有children
diff
+ 'use client'
- import type { Metadata } from "next";
import { Inter } from "next/font/google";
+ import { NextUIProvider } from "@nextui-org/react";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
- export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
- };
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
+ <NextUIProvider>
+ {children}
+ </NextUIProvider>
</body>
</html>
);
}
如果你还是想在 app/layout.tsx
中统一设置 metadata
, 那么可以将 NextUIProvider
包裹的这块相关代码挪到一个新的文件 app/Provider.tsx
中, 该文件导出一个客户端组件, 并接收一个 children
, 其完整代码如下:
js
'use client'
import { NextUIProvider } from "@nextui-org/react";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<NextUIProvider>
{children}
</NextUIProvider>
);
}
下面我们只需要在 app/layout.tsx
使用上文的 Provider
组件即可, 这样就即能保持 app/layout.tsx
服务端组件的特性, 又可以完成 NextUIProvider
组件的使用
diff
import type { Metadata } from "next";
import { Inter } from "next/font/google";
+ import Provider from './Provider'
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
+ <Provider>
+ {children}
+ </Provider>
</body>
</html>
);
}
那么问题来了 NextUIProvider
到底是啥, 我们为什么要加这个? 这里可以看 NextUI
官方的一个说明: setup-pnpm-optional
pnpm
设置(可选)
这里因为我使用的是
pnpm
, 所以还需要在项目根目录下创建一个.npmrc
文件(如果没有的话), 并添加如下内容:
text
public-hoist-pattern[]=*@nextui-org/*
根据官方提示, 修改完成 .npmrc
文件后, 需要重新 pnpm install
以确保依赖安装正确
4.3 测试
运行项目, 并访问 http://localhost:3000
, 其结果如下:
下面在 app/page.tsx
来引用个 Button
组件来测试下
js
import { Button } from "@nextui-org/react"
<Button color="primary">Button</Button>
如下, 只要按钮能够正常展示即说明 NextUI
安装成功!
4.4 一个小问题
也行你已经发现了, 相比刚初始化完项目, 安装完 NextUI
后, 首页背景图却丢失了!!!
经排查, 发现是背景图部分, DOM
节点层级太低了, 中间那个 div
的 z-index
居然是 -1
解决办法很简单, 直接将层级改为 0
就可以了
修改完成后刷新页面即可:
至于为什么最开始样式没有出现问题, 安装完 NextUI
就出现问题了? 咱们就不去深究咯!! 这也不是本文的重点, 就不管咯!!!
五、Stylelint
Stylelint
是一个强大、先进的CSS
代码检查器, 可以帮助我们规避CSS
代码中的错误并保持一致的编码风格
下面我们来配置下 Stylelint
...
- 安装所需的包
sh
pnpm i stylelint stylelint-config-standard --save-dev
- 创建配置文件文件
.stylelintrc.mjs
, 配置内容如下:
json
export default {
extends: "stylelint-config-standard",
}
- 配置完成, 打开
src/app/globals.css
不出意外, 应该会有Stylelint
报错信息(当然在这之前需要你vscode
安装了Stylelint
插件才行)
- 上面报错的部分是
Tailwind
自带的一些规则,Stylelint
是无法识别的, 这里我们可以通过配置at-rule-no-unknown
规则来忽略它们
js
export default {
extends: "stylelint-config-standard",
rules: {
"at-rule-no-unknown": [
true,
{
ignoreAtRules: [
"tailwind",
"apply",
"variants",
"responsive",
"screen"
]
}
]
}
}
- 回头看下
src/app/globals.css
,Stylelint
报错信息不再有
Stylelint
配置就到此结束, 你可以根据自己的喜好调整相关的规则配置, 具体可查阅 官网
六、Eslint
Eslint
官方脚手架 就已经做了支持, 但好像只是添加了规则集合, 好多规则都没严格设置, 所以这里还可以根据自己的习惯, 配置一些规则。当然实际上我还做了一些其他调整, 具体内容如下:
6.1 修改配置文件格式
官方默认的配置文件是 JSON
格式的即 .eslintrc.json
, 这里我为了方便添加注释所以采用的是 JS
格式, 所以这里我将配置文件名修改为 .eslintrc.js
并做了最基础的配置, 关于 Next
官方更多配置信息可查阅 官网
js
module.exports = {
extends: ['next/core-web-vitals'],
rules: {}
}
6.2 Typescript 相关
项目中启用了 Typescript
, 所以这里我配置了 Typescript
相关规则和解析器
- 先安装下所需的依赖:
sh
pnpm i @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
- 修改
ESLint
配置(.eslintrc.js
):
diff
module.exports = {
+ parser: '@typescript-eslint/parser',
extends: [
'next/core-web-vitals',
+ 'plugin:@typescript-eslint/eslint-recommended',
+ 'plugin:@typescript-eslint/recommended',
],
rules: {}
}
6.3 Tailwindcss 相关
项目中启用了 Tailwindcss
, 下面我们可以引入 Tailwindcss
相关的 ESLint
插件: eslint-plugin-tailwindcss
那么插件有什么作用呢?
- 践行 Tailwind CSS 最佳实践, 帮助我们编写一致性代码
- 可以根据最佳实践, 来排序我们的类目
- 根据我们编写的类名给出更好的推荐, 简化类名, 例如:
w-5 h-5
实际上等价于size-5
, 如果我们写了w-5 h-5
插件就可以帮我们推荐、优化为size-5
- 如果类名中存在非
Tailwind CSS
支持的类名, 会给出提示, 避免使用无效类目 - .....
- 安装依赖
sh
pnpm i eslint-plugin-tailwindcss -D
- 修改
ESLint
配置(.eslintrc.js
):
diff
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'next/core-web-vitals',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
+ 'plugin:tailwindcss/recommended',
],
rules: {}
}
6.4 一个小问题
在配置过程中如果你遇到下面 👇🏻 这个错误提示:
请不要慌张, 这大概率是因为你在 VSCode
打开的项目并不是 NextJs
项目的根目录所引起的:
所以解决办法就很简单了: VSCode
请直接打开 NextJs
项目
七、Prettier
正如上图所描述的 Prettier
是一个 代码格式化
工具, 其支持大部分的编程语言, 基本所有编辑器都是支持的!!
补充: 和
ESLint
不同,ESLint
专门做语法规则的校验的, 而Prettier
专注于格式化, 虽然在VSCode
中也是支持基于ESLint
配置来进行格式化, 但是单论格式化还是Prettier
更为强大
Prettier
安装配置还是比较简单的:
7.1 安装
首先先安装 prettier
sh
pnpm i --save-dev prettier
7.2 项目配置
项目根目录下创建 配置文件 .prettierrc.mjs
并添加如下内容(同上配置这里我还是尽可能选择使用 JS
格式, 并且尽量使用 ES
语法来编写):
js
/** @type {import("prettier").Config} */
const config = {
singleQuote: true, // 除了 jsx 属性, 其他都应该使用单引号
singleAttributePerLine: true, // 对应 eslint react/jsx-max-props-per-line
jsxBracketSameLine: true // 对应 eslint react/jsx-closing-bracket-location
};
export default config;
7.3 VSCode 格式化方式设置
- 首先先安装个 prettier-vscode 插件
- 在项目根目录下创建
VSCode
配置文件.vscode/settings.json
- 添加如下配置: 下面配置对于样式部分将根据
stylelint
进行格式化、对于JS
、TS
、JSON
部分则采用prettier
进行格式化
json
{
//#region 格式化
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode", // 默认使用 prettier 进行格式化
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, // 保存的时候自动格式化
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, // 保存的时候自动格式化
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, // 保存的时候自动格式化
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, // 保存的时候自动格式化
},
}
7.4 与 ESLint 相爱相杀
实际上 Prettier
和 ESLint
之间的冲突是难免的, 比如我们使用 Prettier
自定格式化代码后, ESLint
却不能够通过, 提示错误!
而解决办法核心就是 "协调"
:
- 同一规则, 如果
Prettier
和ESLint
都提供配置, 那么只需要保证他们配置一致即可 - 同一规则, 如果
Prettier
和ESLint
只有其中一方提供了配置, 那么退而求其次, 以Prettier
规则为准了 - 解决和
ESLint
冲突的办法还有一个, 就是使用 eslint-config-prettier 插件, 本质上就是将prettier
中的配置合并到ESLint
中, 还是以Prettier
为准, 所以我这里就没安装这个插件, 如果有冲突, 手动调整算了!
最后给出几个我自己的配置:
js
/** @type {import("prettier").Config} */
const config = {
tabWidth: 2,
printWidth: 120,
endOfLine: 'lf',
singleQuote: true, // 除了 jsx 属性, 其他都应该使用单引号
singleAttributePerLine: true, // 对应 eslint react/jsx-max-props-per-line
jsxBracketSameLine: true, // 对应 eslint react/jsx-closing-bracket-location
};
export default config;
八、最后的防线: lint-staged + husky
上文我们配置了 ESLint
和 Stylelint
它们的作用主要就是对我们项目的代码格式、语法进行了一个校验, 但这里我们也只是做了校验, 如果校验不通过也仅仅只会在 VSCode
会进行提示...
也就是说, 即便 ESLint
和 Stylelint
校验不通过, 你的小伙伴也可以不调整代码, 系统不会有任何问题, 代码依然能够进行提交!!
所以这一步, 我们要做的其实就是避免你的小伙伴, 将不符合 Lint
规则的代码进行提交....
大体思路就是借助 git
的 hooks
在提交代码时, 对要提交的代码进行 Lint
规则校验, 只有校验通过才允许提交代码!
8.1 lint-staged
lint-staged 可以帮助我们针对暂存的
git
文件运行相应的linter
, 避免让糟糕的 💩 混入你的仓库中!!!
- 安装依赖包
sh
pnpm i -D lint-staged
- 添加
lint-staged
配置文件.lintstagedrc.mjs
, 配置内容如下:
- 配置, 导出的就是一个对象
- 对象的
key
用于限制文件格式 - 对象的
value
是将要对匹配到的文件执行的命令
js
export default {
'**.{js,ts,jsx,tsx,mjs,cjs}': 'eslint',
'**/*.{css,scss,sass,less}': 'stylelint',
};
- 测试: 上面我们完成了
lint-staged
安装和配置, 配置规则则会对暂存中的相关文件执行eslint
或stylelint
命令
下面我们可以随便写点 ESLint
校验不通过的代码:
将修改内容添加到暂存区 git add .
, 然后执行 lint-staged
命令即 npx lint-staged
, 这时将会根据配置文件 .lintstagedrc.mjs
对暂存的文件执行相应的命令
如上图, 执行 lint-staged
终端中 ESLint
校验未通过, 成功抛出错误!!!
8.2 husky
husky 主要作用就是帮助我们, 方便且优雅的为项目设置
git hooks
- 安装依赖包
sh
pnpm i husky -D
- 执行
npx husky init
来初始化项目, 该命令做了两件事:
- 新增
npm
脚本,prepare
是npm
的一个hooks
它会执行husky
命令 - 项目下新增
.husky
目录, 该目录下用于保存我们要设置的git hooks
, 该目录一个文件映射对应的git hooks
文件内则是对应hooks
被触发时会执行的命令 - 补充: 当时实际上,
husky
肯定也对项目下的.git
中的相关git
配置做了调整
8.3 结合
在上文我们只是简单安装、配置了下 husky
以及 lint-staged
, 下面我们需要将它们结合起来使用!!
也就是说我们需要在 .husky
中配置下 git hooks
作用就是在执行 git commit
之前, 对暂存的内容部分通过 lint-staged
来进行一个校验, 从而确保上传到仓库的代码都是符合我们设置的 linter
- 修改
.husky/pre-commit
配置, 新增一行, 内容为npx lint-staged
- 这样的话, 当我们执行
git commit
(触发pre-commit
hooks
) 时就会执行lint-staged
命令, 即开启linter
校验
sh
git add .
git commit -m 'feat: add lint-staged'
如上图所示, 我在执行 git commit
时, ESLint
校验没有通过, 则 git commit
失败!!!
九、Git Commit 校验
逛 Github
你可能会看到如下图所示的提交信息
这种提交格式, 是目前比较流行的一种「Git
提供规范」了, 具体可参考 Commitlint, 后面我们也会使用到该工具, 实现 Git Commit
校验
补: 该规范,
Git Commit
提交信息规范格式如下, 更多详见 Conventionalcommits
js
type(scope?): subject
body?
footer?
9.1 安装依赖
首先先安装下相关的依赖:
- @commitlint/config-conventional: 配置文件, 预定义好目前一套比较流行的规则配置, 省去自己配置
- @commitlint/cli: 提供
CLI
可用于检查commit
信息是否符合特定格式, 配置简单、可自定义规则、可共享配置规则..
sh
pnpm i -D @commitlint/config-conventional @commitlint/cli
9.2 添加配置文件
项目根目录下, 执行下面 👇🏻 命令: 将会创建配置文件 commitlint.config.js
并添加配置(使用 extends
关键词继承 @commitlint/config-conventional
导出的规则)
sh
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
生成的配置文件内容如下(做了格式化):
9.3 添加 Git Hooks
到此我们已经可以使用 commitlint
来检验 Git Commit
, 下面我们可以简单做个测试: 我们使用 echo
输出一个文本信息, 通过管道作为 npx commitlint
输入, 执行 commitlint
对文本内容进行校验
新增配置文件 .husky/commit-msg
, 并添加内容 npx --no -- commitlint --edit ${1}
9.4 测试
下面我们使用 init project
作为提交信息进行提交, 不出意外 commitlint
抛出错误, 代码提交失败
下面我们使用 feat: init project
作为提交信息进行提交, commitlint
校验通过, 代码提交成功
9.5 czg: 交互式 CLI
czg, 一个交互式 CLI
命令, 帮助我们快速生成标准化提交消息
- 安装依赖包: 这里我只在项目下进行安装, 当然你也可以全局安装使用
sh
pnpm i -D czg
- 安装完成即可使用
- 当然如果你不想使用
npx
, 我们可以额外加个npm
脚本来调用czg
diff
// package.json
{
"name": "next-play",
"version": "0.1.0",
"private": true,
"scripts": {
...
+ "cz": "czg"
},
下面我们就可以通过 pnpm cz
来调用(其实也没少打多少 🐶)
更多内容可以参考 《git commit 规范校验配置和版本发布配置》
十、版本发布
在我们发布一个正式版本时, 我们可能需要做以下几件事:
- 生成
CHANGELOG
, 其包含本次版本新增的主要内容 - 为我们的
Git
仓库, 打标签(Tag
) - 修改当前项目的版本号
- ...
这些步骤如果全部我们人工处理, 相对来说还是比较繁琐的, 好在社区就有提供一些自动化工具, 可以帮助我们完成部分的工作!
这里我推荐使用 standard-version, 虽然目前作者已不再维护, 但还是可以正常使用的。该工具可以帮助我们完成以下几件事:
- 自动生成
CHANGELOG
, 如何我们遵守上文配置的提交规范, 它就会从我们的提交记录提取出关键
的提交记录, 并生成当前版本的CHANGELOG
- 帮我们快速修改项目版本号
- 帮我们生成
Git Tag
下面我们开始配置....
10.1 安装依赖
首先我们先安装下依赖
sh
pnpm i -D standard-version
10.2 配置「npm scripts」
安装完依赖, 我们可以直接通过 npx standard-version
来调用依赖包里提供的 CLI
工具, 但为了方便使用, 这里我还配置了几个 npm
脚本:
diff
{
"scripts": {
+ "release": "standard-version",
+ "release:100": "npm run release -- major",
+ "release:010": "npm run release -- minor",
+ "release:001": "npm run release -- patch",
}
}
release
脚本: 将直接执行standard-version
脚本, 在使用时需要通过--
来传参, 比如我们需要指定修改一个版本号(1.1.0
)、并生成对应CHANGELOG
和Git Tag
则可以执行npm run release -- --release-as 1.1.0
release:100
脚本: 其实执行的就是standard-version major
, 它会基于当前版本升级主版本号, 比如如果当前版本为1.1.1
, 执行npm run release:100
那么将发布2.0.0
版本release:010
脚本: 其实执行的就是standard-version minor
, 它会基于当前版本升级次要版本号, 比如如果当前版本为1.1.1
, 执行npm run release:010
那么将发布1.2.0
版本release:001
脚本: 其实执行的就是standard-version patch
, 它会基于当前版本升级补丁版本号, 比如如果当前版本为1.1.1
, 执行npm run release:001
那么将发布1.1.2
版本
脚本 | 示例 | 效果 |
---|---|---|
release |
npm run release -- --release-as 1.1.0 |
不管当前项目版本号是多少, 直接升级为 1.1.0 |
release:100 |
npm run release:100 |
升级主版本, 如果当前版本号为 1.1.1 则发布 2.0.0 |
release:010 |
standard-version minor |
升级次要版本, 如果当前版本号为 1.1.1 则发布 1.2.0 |
release:001 |
npm run release:001 |
升级补丁版本, 如果当前版本号为 1.1.1 则发布 1.1.2 |
10.3 测试
下面我们尝试执行 release:001
来看下效果:
上图是执行 npm run release:001
的结果(图中我用的是 pnpm
但结果是一样的), 最后我们看一眼 CHANGELOG
生成的内容
十一、遇到的一些问题
记录搭建过程中踩到的坑....
11.1 TS 解析错误
在 Typescript
定义 interface
时, 定义函数 ESLint
报如下错误:
问题原因是因为 ESLint
将其视为普通函数进行解析
解决也简单, 为 ESLint
配置正确的解析器, 即 @typescript-eslint/parser
- 安装相关依赖
sh
pnpm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
- 修改
.eslintrc.js
配置
diff
module.exports = {
+ parser: '@typescript-eslint/parser',
extends: {
+ 'plugin:@typescript-eslint/eslint-recommended',
+ 'plugin:@typescript-eslint/recommended',
},
rules: {
+ 'no-unused-vars': 'off',
+ '@typescript-eslint/no-unused-vars': ['error'],
}
}
11.2 ESLint 报错: 字符串不允许使用 ...
如题, 当我们尝试对字符串使用 ...
将其展开为字符数组, 会报如下错误:
解决办法, 修改 tsconfig.json
的 target
字段(如果没有就加一个), 将其设置为 es6
即可
js
{
"compilerOptions": {
+ "target": "es6",
...
},
...
}