简单来说,monorepo 就是把多个子工程放到一个 git 仓库中进行管理,各工程之间共用同一套构建流程、代码规范,各工程可以使用 link 软链接的方式实现相互引用,方便版本的统一管理
monorepo 架构的优势:
1、可以将一个大型项目,拆分成多个子项目,更容易维护和管理代码
2、提高代码共享和重用性,这些子项目可以共享代码和库,可以减少代码重复,降低维护成本
3、由于所有代码都在同一个代码库中,可以更容易地对代码进行构建和测试,有利于持续集成和持续交付
4、更方便的进行版本控制和管理,可以结合 changesets 类似的发布工具,跟踪代码的变更历史和版本变更
- name 包名
- version 版本
- description 描述
"description": "A Component Library for Vue 3",
- private 防止私有包发布到npm服务器,要发布到npm上设为false
- main 指定 CJS 模块入口
- files 指定哪些包被推送到npm服务器中
- repository 仓库地址
- keywords
"keywords": [
"element-plus",
"element",
"component library",
"ui framework",
"ui",
"vue"
],
- author
- license 指定软件的开源协议: ISC在所有副本中保留版权声明和许可证声明,使用者就可以拿你的代码做任何想做的事情,你也无需承担任何责任 MIT在所有副本或主要部分中保留版权声明和许可证声明,使用者就可以拿你的代码做任何想做的事情,你也无需承担任何责任
- bugs 项目提交问题的地址
//提交问题的地址和反馈的邮箱,url通常是Github中的issues页面
"bugs": {
"url" : "https://github.com/facebook/react/issues",
"email" : "xxxxx@xx.com"
}
- funding 指定项目的资金支持方式和链接
"funding": {
"type": "patreon",
"url": "https://www.patreon.com/my-module"
}
- engines 声明对npm或node的版本要求
"engines": {
"node": ">=18.19.1",
"pnpm": ">=9.1.3"
},
- contributors 贡献者
"contributors": [
"name <b@rubble.com> (http://barnyrubble.tumblr.com/)"
]
- homepage 仓库主页地址
"homepage": "https://element-plus.org/",
- scripts
- browser 指定浏览器入口 一般是umd
- bin 定义在全局安装时可执行的命令,例如构建脚手架
- publishConfig 在发布包时指定特定的配置
- peerDependencies 对等依赖
peerDependencies 字段用于声明一个 package 与依赖它的宿主应用程序所使用的其他 package 之间的兼容性限制。例如element-plus中的peerDependencies是依赖的vue@3.2.0的版本,也就是你当前项目的 vue 版本不能低于vue@3.2.0,否则会出现兼容性报错。
- module (wbpack rollup tree-shaking就是读取这个字段 所以treeshake只能在esm中生效)
- types 指定 TypeScript 类型声明文件(.d.ts 文件)的路径
- exports 当打包工具支持exports字段时(webpack、Rollup 等),以上main,browser,module,types四个字段都被忽略
- type 指定模块系统的使用方式,"commonjs","module","umd","json"
第三方配置
- unpkg 是一个基于 CDN 的前端包托管服务,用于在浏览器中直接引用和加载 npm 上发布的包。
- jsdelivr 是一个基于 CDN 的前端包托管服务,用于在浏览器中直接引用和加载 npm 上发布的包。
- eslintConfig eslint的配置,更推荐新建 .eslintrc 进行配置
- babel babel的配置,更推荐新建 .babelrc 进行配置
- lint-staged lint-staged是一个在Git暂存文件上运行linters的工具,通常配合gitHooks一起使用。
- browserslist 告知支持哪些浏览器及版本,
"browserslist": [
"defaults",
"not ie < 8",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
- sideEffects 指示包是否具有副作用,协助Webpack,Rollup等进行tree shaking 多数情况下可以直接设置为false,这样打包工具就会自动删除未被import的代码
....一些开发者自定义的字段,比如vue的buildOptions
- module 指定ES模块入口 mjs 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用 示例:当其他开发者在他们的项目中导入你的包时,会加载并执行包中的dist/index.esm.js
"module": "dist/index.esm.js",
- main 指定commonJS模块入口 cjs 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
示例:当其他开发者在他们的项目中导入你的包时,会加载并执行包中的dist/index.esm.js
"mian": "dist/index.cjs.js",
- browser 定义 npm 包在 browser 环境下的入口文件 ,如 umd模块
"browser" : "dist/index.umd.js"
指定 TypeScript 类型声明文件(.d.ts 文件)的路径
"types": "./dist/index.d.ts",
在 package.json 中,exports 字段是用于指定模块的导出路径,并明确哪些文件可以通过不同的导入方式进行访问。exports 字段是为了更细粒度地控制模块暴露的内容,特别是当一个包同时支持多种环境(如 CommonJS、ESM、类型定义等)时
当打包工具支持exports字段时(webpack、Rollup 等),以上main,browser,module,types四个字段都被忽略
"." 指代当前包的根目录 "import": 指定了 ES module (ESM) 规范下的导出文件路径 "require": 指定了 CommonJS 规范下的导出文件路径 "browser": 指定了用于浏览器环境的导出文件路径 "types": 指定了类型声明文件的路径
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"browser": "./dist/index.umd.js",
"types": "./dist/index.d.ts"
},
"./lib": {
"types": "./lib/index.d.ts",
"require": "./lib/index.js"
}
}
指定哪些包被推送到npm服务器中
"files": [
"index.js",
"dist"
],
在发布包时指定特定的配置
示例:指定包发布的注册表 URL,指定所有用户都可以访问(私有的会收费)
示例1:
"publishConfig": {
"registry": "https://registry.npmjs.org/", // 指定包发布的npm源
"access": "public",// 指定包发布权限,public表示公开,private表示私有
"tag": "beta",//可以指定标签 默认为latest
"main": "dist/index.cjs.js",//commonjs模块文件入口,当包被发布后,会用该字段替换掉上一级的main字段。publishConfig.main 字段需要与files字段一起使用,以确保发布的包中包含指定的入口文件。
"module": "es/index.esm.js" //es模块文件入口,同理
}
示例2:
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"main": "lib/index.js",
"module": "es/index.js"
},
"files": [ // 发布的包只包含lib,es文件夹和packgae.json和README.md文件
"lib",
"es"
],
定义了与此项目相关的版本控制系统(如 Git)的仓库 URL,以及仓库的类型。
"repository": {
"type": "git",
"url": "git+https://github.com/element-plus/element-plus.git"
},
-
type 指定使用的版本控制系统的类型。常见的类型包括 git、svn、mercurial 等。
-
url 通常以 git+<协议> 开头,后面是仓库地址。
根目录下执行:
- pnpm install -w 根目录下安装所有依赖
- 使用 pnpm install -wD xxx 根目录下
dev依赖, - pnpm -C examples/front dev 相当于 cd examples/front 目录并执行 pnpm dev
- pnpm install 子包 1name --workspace --filter/-F 子包 2name 相当于在
子包2name下安装子包1name依赖 类似这种:
"dependencies": {
"@cmonitor/common": "workspace:_",
"@cmonitor/types": "workspace:_",
"@cmonitor/utils": "workspace:\*"
},
解决 ts 报错 tsconfig.json 中添加:
"baseUrl": ".",
"paths": {
"@cmonitor/_": ["packages/_/src"]
}
- pnpm install -workspace 子包 name
给子包name模块安装某个依赖
pnpm install axios --workspace 子包name
# 也可简写为
pnpm i axios -w 子包name
给所有模块同时安装某个依赖
# 注意 workspaces 这里多个 `s`
pnpm install dayjs --workspaces
# 也可简写为
pnpm i dayjs -ws
- 1
npm install pnpm -g
- 2 在根目录下 npm init -y 生成 package.json
{
"name": "c-monitor",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"examples:vue3": "pnpm run -C examples/vue3 dev --host",
"examples:server": "pnpm run -C examples/server server",
},
"keywords": [
"web monitor",
"web monitor sdk"
],
"author": "cjw",
"license": "ISC",
"description": "前端监控SDK,可用来收集并上报:代码报错、性能数据、用户行为、加载资源、个性化指标等数据",
"dependencies": {},
"devDependencies": {},
"repository": {
"type": "git",
"url": "xxx"
},
"engines": {
"node": ">=18.19.1",
"pnpm": ">=9.1.3"
},
"packageManager": "pnpm@9.1.3"
}
- 3 新增 pnpm-workspave.yaml
packages:
- "packages/\*"
- "examples/\*"
说明 * 和 ** 区别: *匹配零个或多个字符。例如,packages/*将匹配 packages/目录下的所有直接子目录。如果 packages/目录下有子目录 A 和 B,那么 packages/*将匹配 A 和 B。 **匹配零个或多个子目录。例如,components/**将匹配 components/目录下的所有子目录,无论它们是直接子目录还是子目录的子目录。如果 components/目录下有子目录 A 和 B,而子目录 A 下又有一个子目录 C,那么 components/**将匹配 A、B 和 C。 总结:
*匹配直接子目录或文件。 **匹配所有子目录或文件。
- 4 新建两个子包
- packages 写监控体系核心 sdk
- examples 写前后端案例demo
分别在子包 packages 下 npm init -y 生成 package.json
格式如下:
{
"name": "@cmonitor/core",
"version": "1.0.0",
"main": "src/index.ts",
"module": "dist/index.esm.js",
"type": "module",
"keywords": [
"web monitor core module",
"web monitor sdk"
],
"author": "cjw",
"license": "ISC",
"description": "",
"publishConfig": {
"registry": "https://npm.xxx.io",
"main": "dist/index.js",
"module": "es/index.esm.js"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "xxx"
},
"engines": {
"node": ">=18.19.1",
"pnpm": ">=9.1.3"
},
"packageManager": "pnpm@9.1.3"
}
- 4 npx tsc --init 生成 tsconfig.json 文件
{
"include": ["packages"], // 指定要编译的文件或目录
"compilerOptions": {
"resolveJsonModule": true, // 允许从 JSON 文件导入数据
"target": "es5", // 编译目标是 ES5,提高兼容性
"module": "esnext", // 采用 ESNext 模块语法(import/export)
"lib": ["dom", "esnext"], // 允许使用 DOM API 和 ESNext 特性
"importHelpers": true, // 使用 tslib 以减少编译后的代码体积
"declaration": true, // 生成 .d.ts 类型声明文件
"sourceMap": true, // 生成 .map 文件,方便调试 TypeScript 代码
"strict": true, // 启用所有严格模式
"noImplicitAny": true, // 禁止隐式 any 类型,要求所有变量明确声明类型
"strictNullChecks": true, // 严格检查 null 和 undefined,避免空值错误
"strictFunctionTypes": true, // 严格检查函数参数和返回值类型
"strictPropertyInitialization": true, // 要求类的属性在构造函数中初始化
"noImplicitThis": true, // 禁止 this 隐式推断为 any,避免指向错误
"alwaysStrict": true, // 在编译后的代码中启用 use strict 模式
"noUnusedLocals": true, // 禁止定义未使用的局部变量
"noUnusedParameters": true, // 禁止未使用的函数参数
"noImplicitReturns": true, // 确保函数有明确的返回值
"noFallthroughCasesInSwitch": true, // switch 语句必须显式 break 或 return,避免意外逻辑错误
"moduleResolution": "node", // 采用 Node.js 方式解析模块,支持 node_modules
"baseUrl": "./", // 设置解析路径的基础目录
"paths": {
"*": ["src/*", "node_modules/*"], // 允许通配符路径简化导入
"@cmonitor/*": ["packages/*/src"] // 定义 @cmonitor/ 别名,映射到 packages/*/src
},
"jsx": "react", // 启用 JSX 语法支持(适用于 React)
"esModuleInterop": true // 允许默认导入 CommonJS 模块(import express from 'express')
}
}
-
changesets 用来进行版本控制和管理
-
安装
pnpm install @changesets/cli -wD
- 初始化
pnpm changeset init
执行完初始化命令后,会在工程的根目录下生成 .changeset 目录
- 在根目录 package.json 中配置对应的命令
"scripts": {
"changeset": "changeset",
"packages-version": "changeset version",
"publish": "changeset publish --registry=https://registry.npmjs.com/"
}
下面用两个具体的例子,来演示下 changeset 的发包流程
注意:npm 包一般的版本结构为:1.0.0,类似这样的三位数版本号,分别是对应的 changeset version 里面的:major、minor、patch
-
执行 pnpm run changeset
-
执行 pnpm run packages-version
-
执行 pnpm run publish
rollup 的特色是 ES6 模块和代码 Tree-shaking,这些 webpack 同样支持,除此之外 webpack 还支持热模块替换、代码分割、静态资源导入等更多功能。
当开发应用时当然优先选择的是 webpack,但是若你项目只需要打包出一个简单的 bundle 包,并是基于 ES6 模块开发的,可以考虑使用 rollup。
rollup 相比 webpack,它更少的功能和更简单的 api,是我们在打包类库时选择它的原因。
rollup rollup 是一款 ES Modules 打包器,相比于 Webpack,Rollup 要小巧的多,打包生成的文件更小。 优点:
- 打包比另外两个体积小,
- 原生支持 tree-shaking
- ES Modules
- 插件化机制 单个模块的 resolve/load/transform 跟打包环境解耦
缺点:
- 不支持 HMR 热更新,不适合开发环境
- 对 js 以外的模块支持度不高
- 生态较新
esbuild 基于 go 语言开发,
优点:
- 编译速度非常快,相比其他流行的 JavaScript 编译器和打包器,esbuild 基于 Go 语言编写,在编译阶段就已经将源码转译为机器码,所以速度最多可以快 100 倍。
- esbuild 支持多种模块格式,包括 CommonJS、ES6 模块、AMD 等,使得它适用于任何类型的 JavaScript 项目。
- esbuild 的配置非常简单,只需要提供一个入口文件和输出目录即可。
缺点:
- dts 文件需要自行处理
- es5 以下不支持
- 对 css 处理不是很好
- 生态较新
- 配置灵活的不高,侧重于快速且轻量级的构建,没有提供一些复杂的插件或高级配置选项。
webpack webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。 1、优势
- 支持 CommonJS、AMD、ESM 等多种模块化规范,可以将各种资源文件视为模块进行处理和打包,并自动识别依赖关系。
- 拥有强大的插件系统,可实现对代码压缩、分包 chunk、模块热更新等。
- Webpack 提供了各种插件和加载器处理各种资源文件,并支持自定义配置文件,使得开发者可以通过简单的配置调整编译过程和输出结果,实现高度可配置的构建流程。
- 有着庞大的生态系统,涵盖了众多的插件、加载器和工具,可以方便地扩展和定制。
2、劣势
- 构建速度较慢:由于 Webpack 需要将各种资源文件合并、处理和输出,因此在打包大型项目时,构建速度可能会较慢。
- 体积较大:由于 Webpack 需要将所有代码转换为模块,并引入一些必要的运行时依赖,因此输出的文件往往较大,可能会影响应用的加载速度。
- 配置复杂:尽管 Webpack 提供了很多的功能和自定义选项,但是其配置文件往往较为复杂,需要考虑到多个方面,如入口、输出、模块、插件等。
在我们这个监控系统sdk开发中,需要注意的是我们全程使用typescript进行开发,不涉及其他语言,在选用打包构建的工具时,需要选择一个适合的打包工具,这里选择了 rollup,它具有很好的兼容性,支持多种模块格式,并且支持 TypeScript。同时你也可以选择 esbuild,它具有更快的构建速度,并且支持 TypeScript,并且也能实现代码打包热更新。
一个esbuild打包配置示例:
那这个项目中,我们采用rollup,当我们配置好每个packages下子包的package.json 文件,并配置好 rollup.config.js 文件,rollup -c --watch 参数配置好实现热更新打包,就可以在开发环境进行包开发了,接下来是详细配置的步骤
这里使用 rollup 打包,安装依赖,pnpm 提供了 -w 参数,可以将依赖包安装到工程的根目录下,作为所有 package 的公共依赖
- 1
pnpm install rollup@2.78.0 rollup-plugin-typescript2@0.34.1 typescript@4.9.4 -wD
pnpm install @rollup/plugin-commonjs@23.0.3 @rollup/plugin-json@5.0.2 @rollup/plugin-node-resolve@15.0.1 rollup-plugin-dts@5.0.0 rollup-plugin-uglify@6.0.4 @rollup/plugin-terser -wD
- 2 根目录新增 rollup.config.js
/\*\*
- 用于 Rollup 打包 TypeScript 代码的配置
\*/
/_ eslint-disable no-undef _/
import typescript from "rollup-plugin-typescript2"; //支持ts导出
import resolve from "@rollup/plugin-node-resolve"; //当项目中引入外部包,rollup 不知道如何打破常规去处理这些依赖, 将我们编写的源码与依赖的第三方库进行合并
import commonjs from "@rollup/plugin-commonjs"; //rollup.js 编译源码中的模块引用默认只支持 ES6+的模块方式 import/export。然而大量的 npm 模块是基于 CommonJS 模块方式,这就导致了大量 npm 模块不能直接编译使用。
import json from "@rollup/plugin-json";
import { uglify } from "rollup-plugin-uglify"; //用于压缩 JavaScript 代码。
import { terser } from "@rollup/plugin-terser"; //比uglify更好
import dts from "rollup-plugin-dts"; //用于生成 TypeScript 类型声明文件。
import fs from "fs";
import path from "path";
const packagesDir = path.resolve(\_\_dirname, "packages");
const packageFiles = fs.readdirSync(packagesDir);
function output(path) {
return [
// 生成 JS 代码文件
{
input: [`./packages/${path}/src/index.ts`],
output: [
{
file: `./packages/${path}/dist/index.cjs.js`,
format: "cjs",
sourcemap: true,
},
{
file: `./packages/${path}/dist/index.esm.js`,
format: "esm", //不压缩,保证tree shaking
sourcemap: true,
},
// {
// file: `./packages/${path}/dist/index.js`,
// format: "umd",
// name: "web-see",
// sourcemap: true,
// },
{
file: `./packages/${path}/dist/index.min.js`,
format: "umd", //用于浏览器端
name: "c-monitor",
sourcemap: true,
// plugins: [uglify()],
plugins: [terser()],
},
],
plugins: [
typescript({
tsconfigOverride: {
compilerOptions: {
module: "ESNext",
},
},
useTsconfigDeclarationDir: true, // 让 TypeScript 生成类型声明文件
}),
resolve(), // 解析 node_modules 模块
commonjs(), // 兼容 CommonJS 依赖
json(), // 允许导入 JSON 文件
],
},
// 生成 TypeScript 类型声明文件
{
input: `./packages/${path}/src/index.ts`,
output: [
{ file: `./packages/${path}/dist/index.cjs.d.ts`, format: "cjs" },
{ file: `./packages/${path}/dist/index.esm.d.ts`, format: "esm" },
// { file: `./packages/${path}/dist/index.d.ts`, format: "umd" },
// { file: `./packages/${path}/dist/index.min.d.ts`, format: "umd" },
],
plugins: [dts()],
},
];
}
// 遍历 packageFiles,对每个子包执行 output(path) 生成配置数组。
// 使用 flat(),将所有子包的配置展平成一个数组,供 Rollup 读取。
export default [...packageFiles.map((path) => output(path)).flat()];
- 3 在根目录 package.json 中配置打包命令
"scripts": {
"build": "rollup -c --watch "
}
--watch模式支持监听代码变化,一旦修改代码后将自动执行打包
- 4 rollup 删除上次构建
pnpm install rollup-plugin-delete -wD
eslint + prettier + husky + lint-staged + commitlint + commitizen + cz-git
引入eslint对代码进行检查
引入prettier对代码进行基础格式化
pnpm install prettier -wD
根目录下新建.prettierignore
/dist/\*
.local
/node_modules/\*\*
\*_/_.svg
/public/\*
.prettierrc.cjs / .prettierrc =>json
module.exports = {
// 指定最大换行长度
printWidth: 130,
// 缩进制表符宽度 | 空格数
tabWidth: 2,
// 使用制表符而不是空格缩进行 (true:制表符,false:空格)
useTabs: false,
// 结尾不用分号 (true:有,false:没有)
semi: false,
// 使用单引号 (true:单引号,false:双引号)
singleQuote: false,
// 在对象字面量中决定是否将属性名用引号括起来 可选值 "<as-needed|consistent|preserve>"
quoteProps: "as-needed",
// 在JSX中使用单引号而不是双引号 (true:单引号,false:双引号)
jsxSingleQuote: false,
// 多行时尽可能打印尾随逗号 可选值"<none|es5|all>"
trailingComma: "none",
// 在对象,数组括号与文字之间加空格 "{ foo: bar }" (true:有,false:没有)
bracketSpacing: true,
// 将 > 多行元素放在最后一行的末尾,而不是单独放在下一行 (true:放末尾,false:单独一行)
bracketSameLine: false,
// (x) => {} 箭头函数参数只有一个时是否要有小括号 (avoid:省略括号,always:不省略括号)
arrowParens: "avoid",
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 可以在文件顶部插入一个特殊标记,指定该文件已使用 Prettier 格式化
insertPragma: false,
// 用于控制文本是否应该被换行以及如何进行换行
proseWrap: "preserve",
// 在html中空格是否是敏感的 "css" - 遵守 CSS 显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
htmlWhitespaceSensitivity: "css",
// 控制在 Vue 单文件组件中 <script> 和 <style> 标签内的代码缩进方式
vueIndentScriptAndStyle: false,
// 换行符使用 lf 结尾是 可选值 "<auto|lf|crlf|cr>"
endOfLine: "auto",
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 (rangeStart:开始,rangeEnd:结束)
rangeStart: 0,
rangeEnd: Infinity
}
目前项目使用eslint 8.57.0版本,参考了geeker-admin
eslint9版本,摒弃了 .eslintrc和eslintignore模式,而是采用eslint.config.js的形式 flat扁平化。 具体配置参照https://juejin.cn/post/7402922513888460852,或者`vue-pure-admin`项目
跳过eslint 检测,代码上方添加// eslint-disable-next-line no-undef 注释
extends: ["plugin:xxx/recommended"]
plugins: ["xxx"]
extends 是用来继承规则集(其中包含插件和规则),它会自动引入插件。 plugins 是用来显式声明插件,尤其是当你没有通过 extends 引入某个插件时,或者需要显式引用某些插件时。
- rules配置说明:
rules: {
"规则名": [规则值, 规则配置]
}
"off" 或 0 - 关闭规则
"warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
"error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
"no-alert": 0,//禁止使用alert confirm prompt
"no-array-constructor": 2,//禁止使用数组构造器
"no-bitwise": 0,//禁止使用按位运算符
"no-caller": 1,//禁止使用arguments.caller或arguments.callee
"no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名
"no-class-assign": 2,//禁止给类赋值
"no-cond-assign": 2,//禁止在条件表达式中使用赋值语句
"no-console": 0,//禁止使用console
"no-const-assign": 2,//禁止修改const声明的变量
"no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)
"no-continue": 0,//禁止使用continue
"no-control-regex": 2,//禁止在正则表达式中使用控制字符
"no-debugger": 2,//禁止使用debugger
"no-delete-var": 2,//不能对var声明的变量使用delete操作符
"no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
"no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
"no-dupe-args": 2,//函数参数不能重复
"no-duplicate-case": 2,//switch中的case标签不能重复
"no-else-return": 2,//如果if语句里面有return,后面不能跟else语句
"no-empty": 2,//块语句中的内容不能为空
"no-empty-character-class": 2,//正则表达式中的[]内容不能为空
"no-eq-null": 2,//禁止对null使用==或!=运算符
"no-eval": 1,//禁止使用eval
"no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
"no-extend-native": 2,//禁止扩展native对象
"no-extra-bind": 2,//禁止不必要的函数绑定
"no-extra-boolean-cast": 2,//禁止不必要的bool转换
"no-extra-parens": 2,//禁止非必要的括号
"no-extra-semi": 2,//禁止多余的冒号
"no-fallthrough": 1,//禁止switch穿透
"no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.
"no-func-assign": 2,//禁止重复的函数声明
"no-implicit-coercion": 1,//禁止隐式转换
"no-implied-eval": 2,//禁止使用隐式eval
"no-inline-comments": 0,//禁止行内备注
"no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
"no-invalid-regexp": 2,//禁止无效的正则表达式
"no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
"no-irregular-whitespace": 2,//不能有不规则的空格
"no-iterator": 2,//禁止使用**iterator** 属性
"no-label-var": 2,//label名不能与var声明的变量名相同
"no-labels": 2,//禁止标签声明
"no-lone-blocks": 2,//禁止不必要的嵌套块
"no-lonely-if": 2,//禁止else语句内只有if语句
"no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
"no-mixed-requires": [0, false],//声明时不能混用声明类型
"no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
"linebreak-style": [0, "windows"],//换行风格
"no-multi-spaces": 1,//不能用多余的空格
"no-multi-str": 2,//字符串不能用\换行
"no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
"no-native-reassign": 2,//不能重写native对象
"no-negated-in-lhs": 2,//in 操作符的左边不能有!
"no-nested-ternary": 0,//禁止使用嵌套的三目运算
"no-new": 1,//禁止在使用new构造一个实例后不赋值
"no-new-func": 1,//禁止使用new Function
"no-new-object": 2,//禁止使用new Object()
"no-new-require": 2,//禁止使用new require
"no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number
"no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()
"no-octal": 2,//禁止使用八进制数字
"no-octal-escape": 2,//禁止使用八进制转义序列
"no-param-reassign": 2,//禁止给参数重新赋值
"no-path-concat": 0,//node中不能使用**dirname或**filename做路径拼接
"no-plusplus": 0,//禁止使用++,--
"no-process-env": 0,//禁止使用process.env
"no-process-exit": 0,//禁止使用process.exit()
"no-proto": 2,//禁止使用**proto**属性
"no-redeclare": 2,//禁止重复声明变量
"no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/
"no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错
"no-return-assign": 1,//return 语句中不能有赋值表达式
"no-script-url": 0,//禁止使用javascript:void(0)
"no-self-compare": 2,//不能比较自身
"no-sequences": 0,//禁止使用逗号运算符
"no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
"no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用
"no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格
"no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
"no-sync": 0,//nodejs 禁止同步方法
"no-ternary": 0,//禁止使用三目运算符
"no-trailing-spaces": 1,//一行结束后面不要有空格
"no-this-before-super": 0,//在调用super()之前不能使用this或super
"no-throw-literal": 2,//禁止抛出字面量错误 throw "error";
"no-undef": 1,//不能有未定义的变量
"no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined
"no-undefined": 2,//不能使用undefined
"no-unexpected-multiline": 2,//避免多行表达式
"no-underscore-dangle": 1,//标识符不能以\_开头或结尾
"no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
"no-unreachable": 2,//不能有无法执行的代码
"no-unused-expressions": 2,//禁止无用的表达式
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
"no-use-before-define": 2,//未定义前不能使用
"no-useless-call": 2,//禁止不必要的call和apply
"no-void": 2,//禁用void操作符
"no-var": 0,//禁用var,用let和const代替
"no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注
"no-with": 2,//禁用with
"array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
"arrow-parens": 0,//箭头函数用小括号括起来
"arrow-spacing": 0,//=>的前/后括号
"accessor-pairs": 0,//在对象中使用getter/setter
"block-scoped-var": 0,//块语句中使用var
"brace-style": [1, "1tbs"],//大括号风格
"callback-return": 1,//避免多次调用回调什么的
"camelcase": 2,//强制驼峰法命名
"comma-dangle": [2, "never"],//对象字面量项尾不能有逗号
"comma-spacing": 0,//逗号前后的空格
"comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
"complexity": [0, 11],//循环复杂度
"computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的
"consistent-return": 0,//return 后面是否允许省略
"consistent-this": [2, "that"],//this别名
"constructor-super": 0,//非派生类不能调用super,派生类必须调用super
"curly": [2, "all"],//必须使用 if(){} 中的{}
"default-case": 2,//switch语句最后必须有default
"dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾
"dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号
"eol-last": 0,//文件以单一的换行符结束
"eqeqeq": 2,//必须使用全等
"func-names": 0,//函数表达式必须有名字
"func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
"generator-star-spacing": 0,//生成器函数\*的前后空格
"guard-for-in": 0,//for in循环要用if语句过滤
"handle-callback-err": 0,//nodejs 处理错误
"id-length": 0,//变量名长度
"indent": [2, 4],//缩进风格
"init-declarations": 0,//声明时必须赋初值
"key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
"lines-around-comment": 0,//行前/行后备注
"max-depth": [0, 4],//嵌套块深度
"max-len": [0, 80, 4],//字符串最大长度
"max-nested-callbacks": [0, 2],//回调嵌套深度
"max-params": [0, 3],//函数最多只能有3个参数
"max-statements": [0, 10],//函数内最多有几个声明
"new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
"new-parens": 2,//new时必须加小括号
"newline-after-var": 0,//变量声明后是否需要空一行
"object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格
"object-shorthand": 0,//强制对象字面量缩写语法
"one-var": 0,//连续声明
"operator-assignment": [0, "always"],//赋值运算符 += -=什么的
"operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首
"padded-blocks": 0,//块语句内行首行尾是否要空行
"prefer-const": 0,//首选const
"prefer-spread": 0,//首选展开运算
"prefer-reflect": 0,//首选Reflect的方法
"quotes": [1, "single"],//引号类型 `` "" ''
"quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号
"radix": 2,//parseInt必须指定第二个参数
"id-match": 0,//命名检测
"require-yield": 0,//生成器函数必须有yield
"semi": [2, "always"],//语句强制分号结尾
"semi-spacing": [0, {"before": false, "after": true}],//分号前后空格
"sort-vars": 0,//变量声明时排序
"space-after-keywords": [0, "always"],//关键字后面是否要空一格
"space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
"space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格
"space-in-parens": [0, "never"],//小括号里面要不要有空格
"space-infix-ops": 0,//中缀操作符周围要不要有空格
"keyword-spacing": 2,//return throw case后面要不要加空格
"space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格
"spaced-comment": 0,//注释风格要不要有空格什么的
"strict": 0,//使用严格模式
"use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()
"valid-jsdoc": 0,//jsdoc规则
"valid-typeof": 2,//必须使用合法的typeof的值
"vars-on-top": 2,//var必须放在作用域顶部
"wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格
"wrap-regex": 0,//正则表达式字面量用小括号包起来
"yoda": [2, "never"]//禁止尤达条件
- 安装eslint和其相关插件
pnpm install
eslint@8.57.0
eslint-define-config
eslint-config-prettier@9.1.0
eslint-plugin-prettier@5.2.3
eslint-plugin-vue@9.22.0
@typescript-eslint/eslint-plugin
@typescript-eslint/parser -wD
eslint-define-config:允许以编程方式定义 ESLint配置文件,换句话说,就是你可以自定义配置。配合eslint9使用
eslint-config-prettier:
这是一个ESLint 配置规则的包,它将禁用与Prettier 冲突的ESLint 规则。 使用eslint-config-prettier 可以确保ESLint 规则与Prettier 的代码格式化规则保持一致,避免二者之间的冲突
eslint-plugin-prettier
这是一个ESLint 插件,它将Prettier 应用到ESLint 中。 它会使用Prettier 来格式化代码,并将格式化结果作为ESLint 的一项规则来检查代码
eslint-plugin-vue:: 针对这个插件,它提供了这几个扩展 plugin:vue/base:基础 plugin:vue/essential:预防错误的(用于 Vue 2.x) plugin:vue/recommended:推荐的,最小化任意选择和认知开销(用于 Vue 2.x); plugin:vue/strongly-recommended:强烈推荐,提高可读性(用于 Vue 2.x); plugin:vue/vue3-essential:(用于 Vue 3.x) plugin:vue/vue3-strongly-recommended:(用于 Vue 3.x) plugin:vue/vue3-recommended:(用于 Vue 3.x)
_.sh
node_modules
_.md
_.woff
_.ttf
.vscode
.idea
dist
/public
.husky
.local
// @see: http://eslint.cn
module.exports = {
// 该配置项主要用于指示此.eslintrc文件是Eslint在项目内使用的根级别文件,并且 ESLint 不应在该目录之外搜索配置文件
root: true,
//指定代码运行的环境
env: {
/**
_ 启用浏览器环境,ESLint 会识别浏览器内置的全局变量,如:
window, document, navigator, localStorage, fetch, alert
这样 ESLint 就不会因为 window 或 document 未定义而报错。
_/
browser: true,
/**
_ 启用 Node.js 环境,ESLint 会识别 Node.js 相关的全局变量
_ require, module, exports, process, \_\_dirname, Buffer, global
_/
node: true,
/\*\*
_ 启用 ES6 语法支持,允许使用 ES6 的新特性,如:
let, const, arrow functions (=>), template literals (` `), classes, modules (import/export)
这样 ESLint 就不会对 ES6 语法报错。
\*/
es6: true
},
// 指定如何解析语法,eslint内置
parser: "vue-eslint-parser",
// 优先级低于 parse 的语法解析配置
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module"
},
// plugin:vue/vue3-recommended 继承某些已有的vue中rules规则 https://eslint.vuejs.org/rules
// plugin:@typescript-eslint/recommended 继承某些已有的typescript中rules规则 https://typescript-eslint.io/rules
// plugin:prettier/recommended 继承某些已有的prettier中rules规则 https://github.com/prettier/eslint-config-prettier
// 将 Prettier 规则集成到 ESLint 规则中 禁用 ESLint 可能与 Prettier 冲突的规则 例如 arrow-body-style、prefer-arrow-callback 等
extends: ["plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
// 排除文件,用于忽略eslint检查
ignorePatterns: ["examples/**"],
/\*\*
- "off" 或 0 ==> 关闭规则
- "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
- "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
\*/
rules: {
// eslint (http://eslint.cn/docs/rules)
"no-var": "error", // 要求使用 let 或 const 而不是 var
"no-multiple-empty-lines": ["error", { max: 1 }], // 不允许多个空行
"prefer-const": "off", // 使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
"no-use-before-define": "off", // 禁止在 函数/类/变量 定义之前使用它们
// typeScript (https://typescript-eslint.io/rules)
"@typescript-eslint/no-unused-vars": "error", // 禁止定义未使用的变量
"@typescript-eslint/no-empty-function": "error", // 禁止空函数
"@typescript-eslint/prefer-ts-expect-error": "error", // 禁止使用 @ts-ignore
"@typescript-eslint/ban-ts-comment": "error", // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
"@typescript-eslint/no-inferrable-types": "off", // 可以轻松推断的显式类型可能会增加不必要的冗长
"@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间
"@typescript-eslint/no-explicit-any": "off", // 禁止使用 any 类型
"@typescript-eslint/ban-types": "off", // 禁止使用特定类型
"@typescript-eslint/no-var-requires": "off", // 允许使用 require() 函数导入模块
"@typescript-eslint/no-non-null-assertion": "off", // 不允许使用后缀运算符的非空断言(!)
"@typescript-eslint/no-require-imports": "off", //允许有 require() 导入
// vue (https://eslint.vuejs.org/rules)
"vue/script-setup-uses-vars": "error", // 防止<script setup>使用的变量<template>被标记为未使用,此规则仅在启用该 no-unused-vars 规则时有效
"vue/v-slot-style": "error", // 强制执行 v-slot 指令样式
"vue/no-mutating-props": "error", // 不允许改变组件 prop
"vue/custom-event-name-casing": "error", // 为自定义事件名称强制使用特定大小写
"vue/html-closing-bracket-newline": "error", // 在标签的右括号之前要求或禁止换行
"vue/attribute-hyphenation": "error", // 对模板中的自定义组件强制执行属性命名样式:my-prop="prop"
"vue/attributes-order": "off", // vue api使用顺序,强制执行属性顺序
"vue/no-v-html": "off", // 禁止使用 v-html
"vue/require-default-prop": "off", // 此规则要求为每个 prop 为必填时,必须提供默认值
"vue/multi-word-component-names": "off", // 要求组件名称始终为 “-” 链接的单词
"vue/no-setup-props-destructure": "off", // 禁止解构 props 传递给 setup
"vue/no-undef-properties": "error" // 检测 <template> 中未定义的变量
}
}
说明
配置.eslintrc.cjs
- env 指定eslint运行的环境
- parser
指定eslint解析语法
parserOptions语法解析配置
- parser 优先级低于外层parser
- ecmaVersion 指定esm版本
- sourceType: "module"
- extends []继承哪些规则 ts vue react... , 可以直接加载插件,比如 plugin:xxx/recommended 配合rules使用
- plugins []显式使用某些三方插件/自定义插件 配合rules使用
- ignorePatterns [] 排除某些文件 "examples/**" examples下所有文件 , , "examples/* " examples第一层文件
- rules {} 规则
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.vue --fix "
## or
"lint:eslint": "eslint \"{src,mock,build}/**/\*.{vue,ts,tsx}\" --fix"
"{src,mock,build}/**/\*.{vue,ts,tsx}": 这部分使用了大括号扩展,表示匹配 src、mock 和 build 三个文件夹中的所有 .vue、.ts 和 .tsx 文件。
介绍 pnpm lint:eslint . 表示执行当前文件下所有目录, --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.vue 表示 检测的文件类型 --fix 表示自动修复一些简单的问题 比如空格 分号 符号 var变let 等
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,jsx,tsx,css,less,scss,vue,html,md}\"",
介绍 pnpm lint:prettier --write 直接修改文件(否则 Prettier 只会输出格式化后的内容)。 "*/.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\ 表示所有文件下的js,cjs,ts,json,tsx,css,less,scss,vue,html,md都会走prettier
pnpm lint:prettier/eslint区别
- pnpm lint:prettier只执行格式等检查
- pnpm lint:eslint 如果.eslintrc中extends了plugin:prettier/recommended,会走prettier的检查外还会走代码eslint检查
// javascript
{
parser: 'vue-eslint-parser', // 解析 .vue 文件
parserOptions: {
parser: 'babel-eslint',
ecmaVersion: 12,
sourceType: 'module',
},
}
// typescript
{
parser: 'vue-eslint-parser', // 解析 .vue 文件
parserOptions: {
parser: '@typescript-eslint/parser', // 解析 .ts 文件
ecmaVersion: 12,
sourceType: 'module',
extraFileExtensions: ['.vue'], // 提供@typescript-eslint/parser解析 .vue 文件中的<script>
},
}
如果公司中前端采用vscode开发,并且我们希望每个开发者的vscode中关于prettier部分保持一致,我们需要做一些.vscode配置
- 1.根目录下新建.vscode目录
-.vscode
-extensions.json
-settings.json
- 2.配置extensions.json
{
"recommendations": [
"vue.volar",
"vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode"
]
}
- 3.配置settings.json
实现
- 所有 TypeScript 文件和其他代码文件在保存时都会自动格式化,使用 Prettier 作为格式化工具。
- 自动整理导入语句并修复 ESLint 报告的可修复问题。
- 启用格式化时的实时反馈,如在输入或保存时格式化代码,确保代码风格统一。
- 禁用粘贴时自动格式化,防止在粘贴代码时意外格式化。
{
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSaveMode": "file",
"editor.formatOnType": true,
"editor.formatOnPaste": false,
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSaveMode": "file",
"editor.formatOnType": true,
"editor.formatOnPaste": false
}
}
具体解释
typescript.tsdk:
"typescript.tsdk": "node_modules/typescript/lib"
这个设置告诉 VS Code 使用项目中的 node_modules 文件夹中的 TypeScript 版本,而不是全局安装的 TypeScript。这对于确保使用项目中指定的 TypeScript 版本是很有用的,尤其是在团队协作时,每个人都应该使用相同的 TypeScript 版本。
editor.formatOnSave:
"editor.formatOnSave": true
启用自动格式化功能,当你保存文件时,VS Code 会自动格式化代码。这通常是通过安装并启用 Prettier 或 ESLint 插件来实现的,确保代码格式化一致。
editor.defaultFormatter:
"editor.defaultFormatter": "esbenp.prettier-vscode"
设置默认的格式化工具为 Prettier。这意味着每当你保存文件时,Prettier 将会自动格式化你的代码,确保代码风格的一致性。
editor.codeActionsOnSave:
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
}
在保存时自动执行的代码操作:
source.organizeImports: 自动整理导入语句(按字母顺序排序和删除未使用的导入)。explicit 表示只有当明确要求时才执行(比如文件中有 import 问题时)。
source.fixAll.eslint: 自动运行 ESLint 修复功能,修复代码中可以自动修复的 ESLint 问题。
editor.formatOnSaveMode:
"editor.formatOnSaveMode": "file"
定义格式化的应用范围。file 表示每次保存时整个文件都会被格式化,而不是仅格式化修改过的部分。这确保了文件的每次保存后都保持一致的格式。
editor.formatOnType:
"editor.formatOnType": true
启用按键输入时的格式化功能。当你输入代码时,VS Code 会根据配置的格式化工具(如 Prettier)实时格式化代码。
editor.formatOnPaste:
"editor.formatOnPaste": false
禁用粘贴时自动格式化代码。如果你粘贴代码时不希望其被自动格式化,可以将此选项设置为 false。
[typescript] (特定于 TypeScript 文件的配置):
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSaveMode": "file",
"editor.formatOnType": true,
"editor.formatOnPaste": false
}
这些配置项是为 TypeScript 文件单独设置的。它们与全局设置相同,只是专门针对 TypeScript 文件生效,确保在 TypeScript 文件中使用与其他文件相同的代码格式化和保存操作设置。
总结:
这份配置文件的主要目的是:
确保所有 TypeScript 文件和其他代码文件在保存时都会自动格式化,使用 Prettier 作为格式化工具。
自动整理导入语句并修复 ESLint 报告的可修复问题。
启用格式化时的实时反馈,如在输入或保存时格式化代码,确保代码风格统一。
禁用粘贴时自动格式化,防止在粘贴代码时意外格式化。
这些设置有助于提高代码质量和一致性,减少因格式化不同导致的代码风格差异,提升团队开发效率。
上面引入了eslint+preettier并且进行了文件配置然后实现了手动执行命令,那这种手动执行的方法太过于麻烦,我们希望在提交代码时自动执行Eslint对代码进行检查,那么我们可以使用git的hook功能,为git命令创建我们所需要的钩子。
Husky 是 Git 钩子工具,用于启动git钩子,可以设置在 git 各个阶段(pre-commit、commit-msg 等)触发。 https://typicode.github.io/husky
注意 : husky必须与.git同目录
常见的git hooks
-
pre-commit(常用)
-
prepare-commit-msg
-
commit-msg(常用)
-
post-commit
-
1安装husky
pnpm install husky@9.1.7 -wD
- 2初始化husky
# husky 8 (需要和.git同级)
npx husky install
# husky 9
npx husky init
会在根目录下生成一个.husky文件夹
--.husky
--pre-commot
- 3为了保证其他人在第一次运行代码时,也能确保 Husky 钩子被启用,我们可以修改 package.json:
# husky 8版本
"scripts": {
"prepare": "husky install"
}
# husky 9
"scripts": {
"prepare": "husky"
}
prepare是npm scripts的生命周期,
常见的有
npm publish
prepublishOnly
prepare
prepublish
publish
postpublish
npm pack
prepack
postpack
npm install
preinstall
install
postinstall
Also triggers
prepublish (when on local)
prepare (when on local)
npm start
npm run start has an npm start shorthand.
prestart
start
poststart
- 4 验证
# husky 8版本
npx husky add .husky/pre-commit "pnpm run lint:eslint"
# husky 9版本
echo "pnpm lint:eslint" > .husky/pre-commit
#或者直接手动更改.husky/pre-commit文件
lint-staged 是一个在 git add 到暂存区的文件运行 linters (ESLint/Prettier/StyleLint) 的工具,避免在 git commit 提交时在整个项目执行。
官方网站:https://github.com/okonet/lint-staged
此时我们已经成功初始化了husky,我们接下来引入lint-staged,作git commit暂存区的eslint检测
- 1 安装lint-staged
pnpm install lint-staged@15.5.0 -wD
- 2 lint-staged 添加到 pre-commit 钩子:
# husky 8
npx husky add .husky/pre-commit "npx lint-staged"
# husky 9
echo "npx lint-staged" > .husky/pre-commit
#或者直接手动更改.husky/pre-commit文件
- 3 添加package.json
"lint-staged": {
"_.{js,ts,vue}": [
"eslint --fix",
"prettier --write",
"git add"
],
"_.{cjs,json}": [
"prettier --write"
],
"_.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"_.{scss,css}": [
"stylelint --fix",
"prettier --write"
],
"\*.md": [
"prettier --write"
]
}
上述的配置实现了针对不同文件类型运行不同的工具:
eslint --fix:自动修复 ESLint 能处理的问题。 prettier --write:自动格式化代码。 stylelint --fix(可选):处理 CSS 样式问题。
- 4 添加package.json指令
"scripts": {
"lint:lint-staged": "lint-staged"
}
- 1 执行安装
pnpm -w -D install @commitlint/cli @commitlint/config-conventional
- 2 新建commitlint.config.cjs
module.exports = {
extends: ["@commitlint/config-conventional"], // 使用 @commitlint/config-conventional 的规则 https://commitlint.js.org/#/reference-configuration
rules: {
"type-enum": [
2, // 错误级别,2 表示错误级别为 "错误"
"always", // 总是要求执行这个规则
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能)
"refactor", // 代码重构
"perf", // 性能优化
"test", // 添加测试或已有测试改动
"build", // 构建过程或外部依赖变更
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore" // 辅助工具的更改
]
]
}
}
- 3 添加husky commit-msg钩子
#husky8
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
#husky 9
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
husky 9 的一个问题 git commit -m "不规范的提交"。测试错误如下:
.husky/commit-msg: .husky/commit-msg: cannot execute binary file
husky - commit-msg script failed (code 126)
解决办法:更改 .husky 目录下 commit-msg 文件的编码格式为 UTF-8 的编码格式
-
介绍 commitizen: 基于Node.js的 git commit 命令行工具,辅助生成标准化规范化的 commit message。https://github.com/commitizen/cz-cli ,可以全局安装到电脑 cz-git: 一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen 适配器, https://cz-git.qbb.sh/zh/
-
安装
pnpm install -w -D commitizen cz-git
- cz-git 配置
修改 package.json 指定使用的适配器
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
- 修改commitlint.config.cjs,添加自定义配置 可参考https://cz-git.qbb.sh/zh/config/
增加
prompt: {
messages: {
type: '选择你要提交的类型 :',
scope: '选择一个提交范围(可选):',
customScope: '请输入自定义的提交范围 :',
subject: '填写简短精炼的变更描述 :\n',
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: '选择关联issue前缀(可选):',
customFooterPrefix: '输入自定义issue前缀 :',
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
generatingByAI: '正在通过 AI 生成你的提交简短描述...',
generatedSelectByAI: '选择一个 AI 生成的简短描述:',
confirmCommit: '是否提交或修改commit ?',
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝 文档变更", emoji: ":memo:" },
{ value: "style", name: "格式: 💄 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: ":recycle:" },
{ value: "perf", name: "性能: ⚡️ 性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: 🎡 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ⏪️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
],
useEmoji: true,
emojiAlign: 'center',
useAI: false,
aiNumber: 1,
themeColorCode: '',
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: 'bottom',
customScopesAlias: 'custom',
emptyScopesAlias: 'empty',
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ['feat', 'fix'],
breaklineNumber: 100,
breaklineChar: '|',
skipQuestions: [],
issuePrefixes: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
customIssuePrefixAlign: 'top',
emptyIssuePrefixAlias: 'skip',
customIssuePrefixAlias: 'custom',
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: '',
defaultIssues: '',
defaultScope: '',
defaultSubject: '',
}
- 添加提交指令
package.json 添加 commit 指令
"scripts": {
"commit": "git add -A && git-cz && git push"
}
git add -A :对比git add .(当前目录下的更改), -A (整个项目下)
- 也可以使用czg命令
pnpm install czg -w -D
package.json 添加 commit 指令
"scripts": {
"commit": "git add -A && czg && git push"
}
更快(不依赖 commitizen,直接运行)
- 介绍
Stylelint 一个强大的 CSS linter(检查器),可帮助您避免错误并强制执行约定。官方网站: https://stylelint.io 注意官网明确指出 Stylelint 作为 CSS 代码规范检测而不作为代码格式化工具使用(Prettier 是更好的选择)
- Stylelint 安装
vscode搜索 Stylelint 安装上
pnpm install -w -D stylelint stylelint-config-standard stylelint-config-recommended-scss stylelint-config-recommended-vue postcss postcss-html postcss-scss stylelint-config-recess-order stylelint-config-html stylelint-prettier stylelint-config-standard-scss
stylelint: stylelint 核心库 https://stylelint.io/ stylelint-config-standard: Stylelint的标准共享配置 https://github.com/stylelint/stylelint-config-standard stylelint-config-recommended-scss: 扩展 stylelint-config-recommended 共享配置并为 SCSS 配置其规则 https://github.com/stylelint-scss/stylelint-config-recommended-scss stylelint-config-recommended-vue: 扩展 stylelint-config-recommended 共享配置并为 Vue 配置其规则 https://github.com/ota-meshi/stylelint-config-recommended-vue stylelint-config-recess-order: 提供优化样式顺序的配置 CSS 书写顺序规范 https://jingyan.baidu.com/article/647f0115cf48957f2148a8a3.html stylelint-config-html: 共享 HTML (类似 HTML) 配置,捆绑 postcss-html 并对其进行配置 核心库 https://github.com/gucong3000/postcss-html postcss-scss: PostCSS 的 SCSS 解析器 postcss-scss 文档,支持 CSS 行类注释 核心库 https://github.com/postcss/postcss-scss postcss: 使用 JS 插件转换样式 https://github.com/postcss stylelint-prettier 用于适配 stylelint-config-standard-scss
- 新建.stylelintrc.cjs
// @see: https://stylelint.io
module.exports = {
root: true,
// 继承推荐规范配置
extends: [
"stylelint-config-standard", // 配置 stylelint 拓展插件
"stylelint-config-recommended-scss", // 配置 stylelint scss 插件
"stylelint-config-recommended-vue/scss", // 配置 vue 中 scss 样式格式化
"stylelint-config-html/vue", // 配置 vue 中 template 样式格式化
"stylelint-config-recess-order" // 配置 stylelint css 属性书写顺序插件,
],
plugins: ["stylelint-scss", "stylelint-order", "stylelint-prettier"],
// 指定不同文件对应的解析器
overrides: [
// 扫描 .vue/html 文件中的 <style> 标签内的样式
{
files: ["**/*.{vue,html}"],
customSyntax: "postcss-html"
},
{
files: ["**/*.{css,scss}"],
customSyntax: "postcss-scss"
}
],
// 自定义规则
rules: {
// 启用 prettier 格式化规则,确保样式符合 prettier 格式化规范
"prettier/prettier": true,
// 禁用类选择器的命名模式检查,允许任意类名
"selector-class-pattern": null,
// 禁用对降序特异性的检查(CSS 选择器的权重应按升序排列)
"no-descending-specificity": null,
// 禁用 SCSS 中变量命名的规则,允许自定义变量命名方式
"scss/dollar-variable-pattern": null,
// 禁止使用未知的伪类选择器,但忽略 'deep' 和 'global' 伪类
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["deep", "global"] // 忽略 'deep' 和 'global' 伪类
}
],
// 禁止使用未知的伪元素选择器,但忽略 'v-deep'、'v-global' 和 'v-slotted'
"selector-pseudo-element-no-unknown": [
true,
{
ignorePseudoElements: ["v-deep", "v-global", "v-slotted"] // 忽略 Vue 特有的伪元素
}
],
// 禁止使用未知的 @ 规则,但允许以下 @ 规则:
// 'tailwind', 'apply', 'variants', 'responsive', 'screen', 'function', 'if', 'each', 'include', 'mixin', 'use'
"at-rule-no-unknown": [
true,
{
ignoreAtRules: [
"tailwind", // 忽略 Tailwind CSS 的规则
"apply", // 忽略 Tailwind CSS 的 @apply 规则
"variants", // 忽略 Tailwind CSS 的 @variants 规则
"responsive", // 忽略 Tailwind CSS 的 @responsive 规则
"screen", // 忽略 Tailwind CSS 的 @screen 规则
"function", // 忽略自定义的 @function 规则
"if", // 忽略 SASS 或 SCSS 的 @if 规则
"each", // 忽略 SASS 或 SCSS 的 @each 规则
"include", // 忽略 SASS 或 SCSS 的 @include 规则
"mixin", // 忽略 SASS 或 SCSS 的 @mixin 规则
"use" // 忽略 SASS 或 SCSS 的 @use 规则
]
}
],
// 在每个规则之前要求空行,除了以下两种情况:
// 1. 在注释后不需要空行
// 2. 第一个嵌套规则前不需要空行
"rule-empty-line-before": [
"always",
{
ignore: ["after-comment", "first-nested"] // 忽略注释后的空行和第一个嵌套规则前的空行
}
],
// 禁用未知的单位,但允许 'rpx' 单位
"unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],
// 强制 CSS 属性按照特定顺序排列
"order/order": [
[
"dollar-variables", // 先是 SCSS 变量
"custom-properties", // 然后是 CSS 自定义属性
"at-rules", // 接着是 @ 规则
"declarations", // 然后是声明(例如:color: red;)
{
type: "at-rule", // 接着是 'supports' 和 'media' 规则
name: "supports"
},
{
type: "at-rule",
name: "media"
},
"rules" // 最后是其他的规则
],
{ severity: "warning" } // 如果顺序不符合,显示为警告而不是错误
]
},
ignoreFiles: ["**/*.js", "**/*.jsx", "**/*.tsx", "**/*.ts"]
}
- 新建 .stylelintignore
dist
node*modules
public
.husky
.vscode
.idea
*.sh
\_.md
- package.json 添加 Stylelint 检测指令:
"scripts": {
"lint:stylelint": "stylelint --fix \"\*_/_.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"
}
解释
- --cache 启用缓存机制,避免重复检查未更改的文件,加快执行速度。
- --fix自动修复可修复的代码风格问题。
- "*/.{vue,less,postcss,css,scss}" :
需要检查的文件范围,包括:
.vue(Vue 单文件组件中的 <style> 部分)
.less(Less 样式文件)
.postcss(PostCSS 处理的样式文件)
.css(普通 CSS 文件)
.scss(Sass/SCSS 文件)
- --cache-location node_modules/.cache/stylelint/ :指定缓存文件存放位置,放在 node_modules/.cache/stylelint/ 目录下,避免污染项目目录。
# 把指定的文件添加到暂存区中
git add <文件路径>
# 添加所有修改、已删除、新增的文件到暂存区中,省略 <文件路径> 即为当前目录
git add -A
# 将当前目录提交到暂存区中
git add .
# 把暂存区中的文件提交到本地仓库中并添加描述信息
git commit -m ""
# 从远程仓库获取最新版本。
git pull
# 把本地仓库的分支推送到远程仓库的指定分支
git push origin dev
# git checkout切换分支
1.
# 基于当前本地分支创建一个新分支 branch2,并切换至 branch2
git checkout -b branch2
# 切换到已有的本地分支 branch1
git checkout branch1
# 切换到远程分支 branch1
git checkout origin/branch1
# 撤销工作区所有内容的修改。危险操作,谨慎使用
git checkout .
# git branch新建分支
# 创建新分支,新的分支基于上一次提交建立
git branch <分支名>
# 修改分支名称
# 如果不指定原分支名称则为当前所在分支
git branch -m [<原分支名称>] <新的分支名称>
# 强制修改分支名称
git branch -M [<原分支名称>] <新的分支名称>
# 删除指定的本地分支
$ git branch -d <分支名称>
# 强制删除指定的本地分支
$ git branch -D <分支名称>
#git merge 合并分支
# 把指定的分支合并到当前所在的分支下,并自动进行新的提交
git merge <分支名称>
#git reset回滚代码
# 将某个版本的 commit 从本地仓库退回到工作区(取消 commit 和 add 操 作,不改变文件内容)
## 默认不加 -- 参数时时 mixed
git reset --mixed <commit_sha>
# 将某个版本的 commit 从本地仓库退回到缓存区(取消 commit 操作,不取消 add,不改变文件内容)
git reset --soft <commit_sha>
# 取消某次 commit 的记录(取消 commit 和 add,且改变文件内容)
git reset --hard <commit_sha>
## 以上三种操作退回了 commit,都是退回本地仓库的 commit,没有改变远程仓库的 commit。通常再次修改后配合如下命令覆盖远程仓库的 commit:
git push -f
# git stash:缓存代码
# 缓存代码时添加备注,便于查找。强烈推荐
git stash save "message"
# 取出上一次缓存的代码,并删除这次缓存
git stash pop
# 取出 index 为2缓存代码,并删除这次缓存,index 为对应 git stash list 所列出来的
git stash pop stash@{2}
-
js运行报错(目前能监控): 当 JavaScript运行时产生的错误 就属于 JS运行异常 js语法错误 SyntaxError:
-
语法错误:语法错误是 JavaScript 引擎在解析代码时遇到的错误,通常发生在语法上.
HTTP行为 也是用户行为追踪的重要一环,有的时候,页面出现问题往往是 HTTP 请求了某些数据,渲染造成的;而除了是用户行为追踪的重要一环外;采集 HTTP请求 的各种信息:包括 请求地址、方法、耗时、请求时间、响应时间、响应结果等等等等...
现在异步请求的底层原理都是调用的 XMLHttpRequest 或者 Fetch,我们只需要对这两个方法都进行 劫持
白屏可能是 js语法错误 和 资源加载错误 造成
结合document去做 document.elementsFromPoint(x,y) 指定坐标点下面的所有元素,包括 html 元素,body 元素取决于其是否在指定的坐标上。
-
concurrently实现多个命令启动 pnpm install concurrently -w
-
gobal _global _support 用了ua-parser-js'识别浏览器和操作系统 实现了防止重复初始化的标识位 detFlag
-
function install
app.congig.handleError = (err, vm, info) => { }
HandleEvents.handleError(err) handleError(err) 中写了 !target的情况
-
接下来组装好数据后 需要进行breadcrumb用户行为栈的编写breadcrumb.ts 和 上报错误信息 transportData的编写 =》reportData.ts
-
然后写options封装
Navigator.sendBeacon
将数据以 POST 方法将少量数据发送到服务端
优点:
- 不受页面卸载过程的影响,确保数据可靠发送。
- 异步执行,不阻塞页面关闭或跳转。
缺点:
- fetch 和 ajax 都可以发送任意请求 而 sendBeacon 只能发送POST
- fetch 和 ajax 可以传输任意字节数据 而 sendBeacon 只能传送少量数据(64KB 以内)
- fetch 和 ajax 可以定义任意请求头 而 sendBeacon 无法自定义请求头
- sendBeacon 只能传输 ArrayBuffer、ArrayBufferView、Blob、DOMString、FormData 或 URLSearchParams 类型的数据
- 如果处于危险的网络环境,或者开启了广告屏蔽插件 此请求将无效
- 兼容性差
图片打点上报
gif 1x1 / png
问题点:
- 以GET方式请求图片资源的方式,将上报数据附在 URL 上携带到服务端,而URL地址的长度是有一定限制的
-
jest 配置比较复杂
-
vitest
语法和 jest 语法差不多 性能和速度比较 jest 更快
- mocha
- 1 安装依赖包
pnpm install jest jest-environment-jsdom @types/jest ts-jest ts-node @types/puppeteer puppeteer -w -D
puppeteer是一个无头浏览器,用于测试和抓取网页
- 2 初始化配置文件
npx jest --init
√ Would you like to use Typescript for the configuration file? ... yes
√ Choose the test environment that will be used for testing » jsdom (browser-like)
√ Do you want Jest to add coverage reports? ... yes
√ Which provider should be used to instrument code for coverage? » babel
√ Automatically clear mock calls, instances, contexts and results before every test? ... yes
📝 Configuration file created at jest.config.mjs
然后会生成一个jest.config.mjs文件 和script中的命令 "test": "jest"
- 3 修改配置文件
