让前端再次伟大(未完成)
前言
MFEGA——Make Front End Great Again
写这篇文章主要目的是总结一套完整的、可复用的前端架构,实现拿来即用、拿来即开发。
2024.10.25 - ~
前置条件
开发工具:VS Code
、Git
开发框架:Node.js
、PNPM
、Vue3
、Vite
、JS
项目构建
包管理器选择
参考:Npm / Yarn / Pnpm 前端包管理工具对比
**运行速度对比:**pnpm > yarn > npm
Npm
Node.js官方的包管理工具。
依赖管理方式:
-
在
npm v1
npm v2
版本中,依赖包的管理是树结构嵌套组成的node_modules └─ foo ├─ index.js ├─ package.json └─ node_modules └─ bar ├─ index.js └─ package.json
-
npm v3
版本之后使用扁平化管理
**优缺点:**其是官方支持的包管理器,拥有广泛的社区支持;但其安装速度很慢,并且占用空间大。
Yarn
为了解决 npm 的性能和安全性问题,通过并行下载和本地缓存来提高性能。
**依赖管理方式:**默认使用扁平化依赖管理
**优缺点:**其安装速度较快,支持原生的monorepo;但其占用空间相对较大。
Pnpm
由npm和yarn衍生而来,解决了 npm 和 yarn 在磁盘空间和依赖管理方面的一些问题。
**依赖管理方式:**软硬链接形式管理
**优缺点:**其安装速度最快,占用空间最小;但可能存在依赖的兼容性问题。
##代码组织形式
monorepo => 单仓库存储,以下两条引言非常有深意:
虽然拆分子仓库、拆分子 NPM 包(For web)是进行项目隔离的天然方案,但当仓库内容出现关联时,没有任何一种调试方式比源码放在一起更高效。——精读《Monorepo的优势》
工程化的最终目的是让业务开发可以 100% 聚焦在业务逻辑上。——MonoRepo 前端项目实操
Pnpm Workspace实现Monorepo架构
Monorepo架构是什么
顾名思义,即是“单一仓库”的含义,它是一种风格。它使不同的前端框架能够存在于同一个大型项目中,并实现了代码的共享和重用,解决了多仓库下团队协作的滞后性、前端架构的复杂性等问题。
# monorepo目录架构形式——https://wangtunan.github.io/blog/vueNextAnalysis/monorepo/
my-monorepo/
├── packages/ # 存放各个子包,通常是独立的模块
│ ├── package-a/ # 子包 A
│ ├── package-b/ # 子包 B
│ └── package-c/ # 子包 C
├── apps/ # 存放具体的应用,通常是最终用户使用的产品
│ ├── app-1/ # 应用 1
│ ├── app-2/ # 应用 2
│ └── app-3/ # 应用 3
├── libs/ # 存放共享的库或通用代码,通常是工具
│ ├── lib-1/ # 库 1
│ ├── lib-2/ # 库 2
│ └── lib-3/ # 库 3
├── node_modules/ # Node.js 依赖文件夹
├── package.json
├── pnpm-workspace.yaml # pnpm工作空间配置
**为什么要使用monorepo:**笔者的目标是构建一款 拿来即用、拿来即开发
的前端框架,它的便捷性应该是首位。综合看了monorepo的文章后,我确信这就是我需要的架构;它不仅让团队协作变得简单,而且实现了高内聚。
当然,这可能不是最好的架构,但是没有银弹,适合自己的才是最好的。
如何使用Pnpm Workspace创建项目
-
初始化Monorepo框架
# 全局安装pnpm npm i -g pnpm # 初始化package.json mkdir VEXIOS && cd VEXIOS pnpm init
-
添加
pnpm-workspace.yaml
文件:参考:pnpm中文网
添加内容:
packages: - packages/* # 包含所有 packages 目录下的子包 - apps/* # 包含所有 apps 目录下的应用 - libs/* # 包含所有 libs 目录下的库
接着我们创建这两个文件夹:
-
在
apps
文件夹中创建项目:# 创建测试项目 cd apps pnpm create vite test-app
创建过程中,会让你选择使用什么框架,你可以根据自己需要选择:
接着,我们可以在
apps
文件夹中找到test-app
项目: -
运行项目:
我们使用
pnpm i
来安装项目所需依赖,如果需要单独安装指定项目的依赖,请使用pnpm --filter:成功安装依赖后,我们就可以运行项目了:
如何在monorepo中使用公共组件
在我使用的目录中,
libs
是用来存放工具、服务等,代码通常不包含业务逻辑,是为了提高代码复用性;packages
是可以单独发布的模块或包,通常它们的功能较为独立,能被单独使用。
-
创建
utils
工具文件夹:cd libs mkdir utils && cd utils pnpm init
在
utils
文件夹添加以下文件: -
导出工具:
在
libs/utils/log.js
中添加内容:function log() { console.log("你成功调用了公共工具"); } export { log };
在
libs/utils/index.js
中添加内容:export { log } from "./log";
在
libs/utils/package.json
修改字段name
:{ "name": "@rom/utils", "version": "1.0.1", "description": "", "main": "index.js", "private": true, "scripts": {}, "keywords": [], "author": "", "license": "ISC" }
字段解释:
name
字段:最好改为有区别性的名称,避免重复(name
的命名方式参考:Anatomy of a package)version
字段:最好每次修改后给它+1main
字段:控制出口文件,必须要有相对应文件才能正常导出
-
在
test-app
项目中安装公共utils:在命令行中使用下述命令:
# -F是--filter、i是install、-D是开发环境 pnpm -F=test-app i -D @rom/utils
成功运行后,你可能会遇到以下错误信息:
放轻松,这是
pnpm9
带来的新问题,我们需要再配置一下.npmrc
文件,添加link-workspace-packages=true
,可以将本地可用的包将链接到node_modules
,而不是从注册表下载:再次安装即可正常运行,我们可以进入
apps/test-app/package.json
中查看: -
在
test-app
项目中,使用公共utils:我们可以进入
apps/test-app/src/App.vue
中,在script中添加如下内容:import {log} from '@rom/utils' log()
此时,我们可以运行项目:
pnpm -F=test-app run dev
在页面的控制台中,成功显示了我们的公共utils:
ESLint v9.x 的使用方法
ESLint 是一个开源项目,可帮助您查找并修复 JavaScript 代码的问题。无论您是在浏览器中还是在服务器上编写 JavaScript,无论是否有框架,ESLint 都可以帮助您的代码发挥最佳作用。—— ESLint
[
平面配置(flat config)推出计划
](https://eslint.org/blog/2023/10/flat-config-rollout-plans/)
浅析ESLint v9版本的新特性
- 默认情况下,ESLint CLI 将搜索
eslint.config.js
,而不是.eslintrc.*
文件。 - 弃用核心格式化规则。
- ESLint 现在正式支持 JSON 和 Markdown 的 linting。
- ESLint 配置检查器,即配置可视化工具——可帮助理解和检查 ESLint 平面配置(flat config)文件。
配置eslint.config.js
-
如果你还没安装eslint v9.x,请使用下述命令安装或升级:
# -w是指在工作区(workspace)根目录安装、一般使用monorepo的情况下,只需在根目录安装一次eslint,在具体项目中引用即可 pnpm add -D eslint@9.15.0 -w
安装成功后,你可以在根目录的
package.json
中看到新增依赖: -
在根目录新建一个
eslint.config.js
,并写入下列内容:// eslint.config.js export default [ { // 排除的文件(不检查的文件) ignores: ["node_modules", "dist", "public"], }, { rules: { // 配置:禁止使用console.xxx()命令 "no-console": "error", }, }, ];
**需要注意的是:**你需要在
package.json
中配置:{ "type": "module" }
以使用ES模块。否则你需要使用
module.exports=[]
来使用默认的CommonJS模块。 -
接着你可以在项目的任意js文件中写一个
console.log("xxx")
,在终端中输入pnpx eslint
来检查项目:pnpx eslint
运行成功后,会显示有问题的文件:
推荐的配置方式:
ESLint v9版本有推荐的规则依赖:语言插件-@eslint/js,这里将使用
@eslint/js
来配置推荐规则。
# 安装@eslint/js
pnpm add -D @eslint/js -w
安装成功后,我们在 eslint.config.js
中配置:
// eslint.config.js
import js from "@eslint/js";
export default [
{
// 排除的文件(不检查的文件)
ignores: ["node_modules", "dist", "public"],
},
// 配置:@eslint/js推荐的配置
js.configs.recommended,
];
再次运行 pnpx eslint
即可看到检查结果。
启用globals依赖
在你运行
eslint
检查项目时,会遇到:'console' is not defined
,这是因为在推荐的配置中有一条规则"no-undef": "error"
。
console
是在 JavaScript 环境的全局变量,默认就可以使用。但是对于eslint
,任何变量都需要定义,这可以避免我们在项目中使用一些真的不存在的变量。
安装globals
依赖:
pnpm add -D globals -w
在 eslint.config.js
中配置:
// eslint.config.js
import js from "@eslint/js";
import globals from "globals";
export default [
{
// 排除的文件(不检查的文件)
ignores: ["node_modules", "dist", "public"],
},
// 配置:@eslint/js 推荐的配置
js.configs.recommended,
/* JavaScript规则 */
{
rules: {},
},
/* 全局变量规则 */
{
languageOptions: {
globals: {
...globals.browser,
// 追加一些其他自定义全局规则
},
},
},
];
启用Vue规则检查
ESLint
本身只支持识别JavaScript
,所以对于vue
文件,我们需要安装eslint-plugin-vue
pnpm add -D eslint-plugin-vue -w
在 eslint.config.js
中配置:
// eslint.config.js
import js from "@eslint/js";
import globals from "globals";
import pluginVue from "eslint-plugin-vue";
export default [
{
// 排除的文件(不检查的文件)
ignores: ["node_modules", "dist", "public"],
},
// 配置:@eslint/js 推荐的配置
js.configs.recommended,
// 配置:eslint-plugin-vue 推荐的配置
...pluginVue.configs["flat/recommended"],
/* JavaScript规则 */
{
rules: {},
},
/* 全局变量规则 */
{
languageOptions: {
globals: {
...globals.browser,
// 追加一些其他自定义全局规则
},
},
},
/* vue 拓展规则 */
{
files: ["**/*.vue"],
languageOptions: {
parserOptions: {
ecmaVersion: "latest",
// 允许在.vue 文件中使用 JSX
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// 避免在组件内部修改 props
"vue/no-mutating-props": [
"error",
{
shallowOnly: true,
},
],
},
},
];
**需要注意的是:**规则的优先级问题,后面的规则会覆盖前面的规则。所以一般会把 recommended
写在最前面,然后后面再去关掉/启用一些其他规则。
ESLint扩展安装
ESLint扩展是指在VS Code中的
ESLint
。因为在实际编写代码时,不能实时看到不符合ESLint规则的错误。而VS Code中的ESLint
,本质是在你开发时可以立马找到错误的代码位置进行改正修复。参考:ESLint扩展配置介绍
-
在VS Code的扩展中搜索
ESLint
,并点击安装,然后重启VS Code: -
配置需要校验的文件:
ESLint
扩展会优先查找项目根目录的eslint.config.js
配置文件。同时ESLint
扩展并不会对所有文件生效,需要我们手动在.vscode
目录中,新建settings,json
文件,这样VS Code会优先读取该设置。在
settings.json
中添加如下内容:{ "eslint.validate": [ "javascript", "vue", "vue-html", "typescript", "typescriptreact", "html", "css", "scss", "less", "json", "jsonc", "json5", "markdown" ] }
这样,
ESLint
扩展就可以识别配置所提到的文件。
Prettier的使用方法
Prettier
是用于格式化代码的。Prettier依赖
是专注于代码格式化,需要手动输入命令才能格式化文件;Prettier扩展
是帮助我们Ctrl+S
就可以自动格式化代码,不需要额外执行。
-
在项目中安装
prettier
依赖:pnpm add -D prettier -w
-
接着在任意文件中书写不规范代码,例如:
<template> <h1>Named Views</h1> <!-- nav --> <ul> <li> <router-link to="/dashboard/test">First page</router-link> </li> <li> <router-link to="/dashboard/one">Second page</router-link> </li> </ul> <!-- show --> <router-view></router-view> </template>
接着我们,可以指定对该文件进行格式化:
pnpx prettier --write apps/opus-mart/src/views/samples/TestView.vue # 或者使用该命令对所有文件格式化 pnpx prettier --write .
这时你会发现,文件似乎没有什么变化,而是发生了闪烁(如果报错说明出现了问题)。这是因为
prettier
和eslint
发生了冲突,我们可以使用eslint-plugin-prettier
依赖来解决,这在后文中会说明。 -
安装VS Code扩展
prettier
:在搜索栏搜索Prettier,出现如下图的扩展安装即可。
-
接着我们对
prettier
进行配置:添加
.prettierignore
文件:该文件是prettier格式化时要忽略的文件。
# prettier忽略格式化的文件 dist *.md *.html **/.git **/.svn **/.hg **/node_modules
添加
prettier.config.js
文件:该文件是prettier格式化时采用的规则。
// prettier.config.js export default { singleQuote: false, printWidth: 120, semi: true, jsxSingleQuote: true, useTabs: true, tabWidth: 2, endOfLine: 'auto', 'space-around-alphabet': true, 'space-around-number': true, 'no-empty-code-lang': false, 'no-empty-code': false, };
配置自动保存的首选项:
我们可以在
.vscode/setting.json
中,配置默认格式化的选项。{ // 配置要格式化的文件类型 "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } }
需要注意的是:
- 需要在设置界面搜索“格式化”,以开启保存时的格式化功能。
- 如果使用
prettier.config.js
配置,则需要重启VS Code才能生效,或者使用.prettierrc
来即时生效
-
解决
eslint
和prettier
冲突问题,安装eslint-plugin-prettier
和eslint-config-prettier
:eslint-plugin-prettier
:这是一个 ESLint 插件,它会使用 Prettier 来格式化代码,并将格式化结果作为 ESLint 的一项规则来检查代码可以在代码检查的同时,自动格式化代码,使其符合 Prettier 的规则。
eslint-config-prettier
:这是一个 ESLint 配置规则的包,它将禁用与 Prettier 冲突的 ESLint 规则。可以确保 ESLint 规则与 Prettier 的代码格式化规则保持一致,避免二者之间的冲突。pnpm add -D eslint-plugin-prettier eslint-config-prettier -w
安装后,在
eslint.config.js
中添加配置,以达到优先使用prettier
的目的:import eslintConfigPrettier from "eslint-config-prettier" import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended" export default [ /* prettier 配置:会合并根目录下的prettier.config.js 文件 */ eslintConfigPrettier, eslintPluginPrettierRecommended, ]
格式化代码补充说明
-
至此,你已经完成
eslint
+prettier
的配置了。值得注意的是,在格式化代码时,请确保格式化程序为Prettier,否则,格式化将不生效。 -
由于每个人的VS Code配置不同,我们需要
.editorconfig
来统一编辑器配置,具体操作如下:-
安装VS Code拓展:
EditorConfig for VS Code
-
添加文件
.editorconfig
,并添加下述内容:# EditorConfig is awesome: https://EditorConfig.org root = true [*] indent_style = tab indent_size = 2 tab_width = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = false insert_final_newline = false
上述内容可以统一团队成员的VS Code部分配置。例如tab的宽度被设置成2格,而不是默认的4格。
-
ES Module与CommonJS模块的选择
ES Module | CommonJS | |
---|---|---|
导出方式 | 使用 import 和 export 关键字 |
使用 require 和 module.exports |
动态导入 | 支持,在代码运行时导入模块 | 不支持 |
作用域 | 每个文件是独立的作用域 | 全局的作用域 |
异步加载 | 支持,可以提高性能和减少启动时间 | 不支持 |
循环依赖 | 支持 | 不支持 |
浏览器兼容性 | 现代浏览器 | 现代浏览器和旧版浏览器 |
可以从表格中看出,ES Module
更趋近我们高内聚性,在本项目中,将使用 ES Module
来导入导出模块。
ES Module使用方法
导出方法:
// 导出变量
export const name = "Rom"
export const age = 21
// 导出函数也可以
export function fn() {}
export const test = () => {}
// 如果有多个的话
const name = "Rom"
const age = 21
export { name, age }
默认导出:
export default {
msg: "hello Rom"
}
导入:
// 导入变量
import { name, age } from './index.js'
// 如果里面全是单个导出,我们就想全部直接导入则可以这样写
import * as all from './index.js'
默认导入:
// 可以直接作为变量导入,不用加{}
import msg from './index.js'
package.json部分字段解释
参考资料:package.json配置详解
####version字段
实际开发中,建议将
0.1.0
作为第一个开发版本号。灵感来源:语义化版本规范
官方文档:语义化版本 2.0.0
1、规则
版本号可按以下规则递增(主版本号.次版本号.修订号):
v
:所有版本号都以v
开头。MAJOR
主版本号:意味着有大的版本更新,第一次发布时递增。MINOR
次版本号:当你做了向下兼容的功能性新增。PATCH
修订版本号:用户做了向下兼容的 bug 修复。
2、版本号递增规则
如果我们严格按照 Angular commit message
规范提交代码时,版本号可以这样确定:
fix
类型的commit
可以将修订号+1。feat
类型的commit
可以将次版本号+1,并将修订号清零。- 带有
BREAKING CHANGE
的commit
可以将主版本号 +1,并将次版本号、修订号清零。
3、伪版本
当我们需要使用一个特殊的 commit
快照进行测试时,此时将是伪版本,例如 v0.0.0-20191109021931-daa7c04131f5
。其包含3个部分:
- 基本版本前缀:通常为
vX.Y.Z
,表面该commit
快照派生自某个语义版本。 - 时间戳:格式为"yyyymmddhhmmss",他是创建
commit
的UTC时间。 - 最后是长度为12的
commit
号。
engines字段
此字段用于指定项目所需工具版本,例如node、npm、pnpm。
但通常只是建议,会在不满足版本时弹出警告。只有在
.npmrc
中配置engine-strict = true
启用严格模式,才会强制要求。
我希望团队成员只使用 pnpm
,避免使用 npm
;通过多次测试,发现根据pnpm官方文档即使设置 preinstall
也没有生效。
同时也参考了一些朋友的建议,使用 corepack
直接帮助安装 pnpm
,但考虑到每个人的需求不同,于是不使用该设置帮助下载pnpm。
限制使用pnpm方式:
- (推荐)采用
engine-strict
模式,在engines
中配置"npm": ">=999.0.0"
,来达到限制使用npm的目的。经过测试,发现其从根源上解决了只使用pnpm问题,但代码实现略不优雅。 - 不使用
only-allow
,使用另一个工具npm-only-allow
,它解决了only-allow
不生效的问题。但是它无法解决当使用npm install
安装时,会生成node_moules
问题(其文件夹结构还是npm
使用的)。经过初步测试,发现其原理是在等待npm install
预安装后,删除生成的package-lock.json
,但这无法从根源解决限制团队成员使用npm问题。
.npmrc配置文件解释
npm、pnpm运行时的配置文件
由于我们使用 monorepo
架构,为了避免安装自己的包时从 npm
官网下载,需要添加 link-workspace-packages=true
,让其优先从本地包链接。
由于项目强制要求使用 pnpm
作为包管理器,使用传统的only-allow
无法限制使用 pnpm
,于是使用npm
版本限制的方法实现,需要添加 engine-strict = true
强制指定版本。
这是我使用的 .npmrc
配置:
# 将本地可用的包将链接到 node_modules,而不是从注册表下载
link-workspace-packages=true
# 强制指定包管理工具与版本
engine-strict = true
依赖包的使用
代码依赖
plugin-vue:pnpm add -D @vitejs/plugin-vue
。当使用 Vite
构建 Vue
项目时会自动安装,提供单文件组件支持。
eslint:pnpm add -D eslint eslint-plugin-vue @vue/eslint-config-prettier @antfu/eslint-config eslint-define-config eslint-plugin-format
。著名的代码检查工具,用于提高代码质量。
eslint-plugin-vue
:专为 Vue
设计的 ESLint
插件,能够解析和检查 .vue
文件中的模板和脚本部分。(将vue的建议变为规则)
@vue/eslint-config-prettier
:与 Prettier
一起使用,确保 ESLint 和 Prettier 可以无缝协作。
@antfu/eslint-config
:知名贡献者 antfu 的 ESLint 配置包,可以快速地在项目中设置 ESLint。
eslint-define-config
:配置文件编写工具,可以将常用规则的具体配置提示出来。
eslint-plugin-format
:一款集成于ESLint的插件,实现对多种编程语言的格式化支持。
prettier:pnpm add -D prettier
。一款代码格式化工具,保存时格式化,以强制执行一致的代码风格。
(未采用)
babel:pnpm add -D @babel/core @babel/eslint-parser
。使用最新 ECMAScript 语法的代码转换为向后兼容的版本,以支持较旧的运行环境或浏览器。
@babel/eslint-parser
:允许 ESLint 解析 Babel 能够理解的最新 JavaScript 语法。
CSS依赖
(已采用)
postcss-pxtorem:pnpm add -D postcss-pxtorem
。用于将CSS的像素单位 px
转化为 rem
的依赖包,达到自适应网页效果。
(未采用)
autoprefixer:pnpm add -D autoprefixer
。(生产环境生效)后置处理器,在代码打包生成后,为适配不同浏览器,将css样式添加前缀。(Vite默认包括)
cssnano:pnpm add -D cssnano
。(生产环境生效)用于压缩和优化 CSS,减少 CSS 文件体积。
windicss: pnpm add -D vite-plugin-windicss windicss
。相比 TailwindCSS
,使用 WindiCSS
可能更符合该项目,因为它是按需使用,速度更快。(unocss) 原子化CSS
purgecss:pnpm add -D @fullhuman/postcss-purgecss
。purgecss
用来删除未使用的 CSS 代码的工具。
.gitignore忽略提交文件
该文件用于指定哪些文件或目录应该被Git忽略,不纳入版本控制。
参考:Git常见操作之忽略文件
推荐的配置:
# git忽略格式化的文件
# 忽略Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# 忽略编辑器目录和文件
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
# 忽略仓库依赖包
node_modules
# 忽略依赖锁文件
pnpm-lock.yaml
package-lock.json
# 忽略生成的构建缓存
.vite
忽略已提交文件
在需要忽略的文件已经提交到版本库的情况下,直接配置
.gitignore
文件,将不生效,需要清除缓存
# 从git仓库中删除debug.log、如果希望同时删除debug.log,可以不加--cached
git rm --cached debug.log
Git使用规范化
Angular提交信息规范
Angular Commit Messages规范
是指由Angular框架提出的git提交规范。
基本结构
Commit Messages
被分为三个部分:header
(MUST) +body
+footer
,即 页眉
+正文
+页脚
。
header
分为三个部分:type
(MUST) +scope
+subject
(MUST) ,即 类型
+范围
+描述
。提交信息结构是:type(scope): subject
,例如:fix(src/login): 修复 login 页面弹窗问题
。
type
的类型
type
是必须的,它是你做的事的概述。
type | 描述 |
---|---|
feat |
为应用或类库实现了新功能时(新增功能时) |
fix |
为应用修复了bug时(修复bug时) |
pref |
包含优化相关,如提升性能、优化用户体验时(优化性能时) |
build |
修改了编译相关内容,如发布版本、对项目构建或者依赖的改动、包括对npm、pnpm等文件的修改时(构建依赖时) |
chore |
对非业务性代码进行修改,如修改构建流程或者工具配置(配置工具时) |
refactor |
仅修改代码结构、变量名、函数名等,不修改功能逻辑 |
style |
仅修改了代码格式或不影响代码逻辑本身的修改时 |
docs |
仅修改了md文件或其他阅读性文件时 |
ci |
修改了持续集成,如修改 Travis、Jenkins 等工作流配置 |
test |
包含测试用例的修改时 |
revert |
回退版本时 |
scope
和 subject
scope
是可选的,它指修改文件的路径。在项目较为庞大的情况下,可以很好的知道改动位置。subject
是必须的,只需简单描述事件即可。例如“修复xxxx”、“删除xxxxx”。
多行正文和多行脚注的示例
header
+body
+footer
fix: 总结内容
新增了xxx
删除了xxx
审核人-by:xxx
参考:xxx
对 header
的信息规范
- 使用命令式,现在时态:“改变”不是“已改变”、不是“改变了”。
- 不要大写首字母。
- 不在末尾添加句号。
注意点
- 在
subject
中,有包含英文的地方,需要在两边加空格。如:fix: 修复 login 页面弹窗问题
。 type
后的冒号,应为英文半角和空格组成。如:fix: xxxx
。- 如果在一次更改中,不能用一句话概括所有更改时,需要将各类型文件进行分批提交。
scope
必须是一个描述某部分代码的名词。如:fix(parser): xxxx
。footer
必须使用-
作为连字符,有助于区分脚注和多行正文。- 可以在
type
后加**!
,表示其中包含破坏性更改**。如:feat!: xxxx
。还可以在footer
中,包含BREAKING CHANGE
(不需要加-
)。在脚注中如:BREAKING CHANGE: xxxx。
Git 提交优化工具链
参考:Husky(husky9.x版本适配) + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范
Husky
husky
是钩子工具,用于对git进行hooks操作。
它是通过优化git hooks
的执行方式,让我们快捷的执行相应钩子。
Git Hooks
Git Hooks
是指在Git生命周期中、在某些事件运行时,执行的脚本。如提交前(pre-commit)和提交后(post-commit)。
本地钩子:
常用钩子 | 描述 |
---|---|
pre-commit |
在执行 git commit 前触发,可用于检查即将要提交的仓库快照 |
prepare-commit-msg |
在完成 git commit 后触发,会弹出含有提交信息的文本编辑器,可用于修改提交信息 |
commit-msg |
在用户输入提交信息之后触发,可以检查提交信息是否规范 |
post-commit |
在 commit-msg 钩子之后立即触发,主要用于消息通知 |
post-checkout |
在执行 git checkout 后触发,可用于显示与前一个版本的差异 |
pre-rebase |
在执行 git rebase 前触发,可以检查以避免破坏性事情 |
服务端钩子:
用于处理在
git push
进程的不同阶段。
常用钩子 | 描述 |
---|---|
pre-receive |
当用户执行 git push 前触发,用于强制开发规范 |
update |
在 pre-receive 后触发,可以防止对某些引用的强制更新 |
post-receive |
在成功push后触发,用于消息通知 |
Husky使用方法
-
安装
husky
依赖:pnpm add -D husky -w
-
初始化
husky
:pnpx husky init
此时会生成对应文件夹和命令脚本:
-
之后我们可以通过在
package.json
中创建脚本,然后在.husky
中新建相应的 执行时间文件(如pre-commit) 即可自动挂载。
Lint-Staged
lint-staged
是可以在Git暂存区文件上,运行指定lint的工具。
它运行的逻辑是:通过在package.json
中的配置,对指定文件执行相应命令。结合husky
后,就成为了在执行pre-commit
钩子时,执行lint-staged
的相应命令。
-
安装
lint-staged
:pnpm add -D lint-staged -w
-
在
package.json
文件中添加"lint-staged"配置:{ "lint-staged": { "*.{js,ts,vue}": [ "eslint --fix", "prettier --write" ], "*.{cjs,json}": [ "prettier --write" ], "*.{vue,html}": [ "eslint --fix", "prettier --write" ], "*.{scss,css}": [ "prettier --write" ], "*.md": [ "prettier --write" ] } }
接着,在"scripts"中添加
lint-staged
脚本:{ "scripts": { "lint:lint-staged": "lint-staged" } }
-
然后将
.husky/pre-commit
文件中内容修改为:pnpm run lint:lint-staged
-
接着我们可以测试一下提交:
git add . git commit -m "test: husky、lint-staged"
这时,我们成功提交了代码:
这里
lint-staged
并没有对文件进行检查和格式化,是因为我没有提交指定的文件格式。正常情况下,会有很多行提示你对哪些文件进行了检查和格式化。
**需要注意的是:*如果你出现了.husky/pre-commit: .husky/pre-commit: cannot execute binary file*的报错,则是因为文件编码问题。
我们可以将 pre-commit
文件改为 utf-8
的编码,即可解决。参考:无法执行二进制文件:Exec 格式错误。
Commitlint
用于检查
提交的消息
是否符合Conventional commit format
(Angular提交信息规范)。参考:commitlint官网
-
安装
commitlint
:pnpm add -D @commitlint/cli @commitlint/config-conventional -w
-
创建
commitlint.config.js
文件,添加如下内容:export default { // 继承的规则 extends: ["@commitlint/config-conventional"], // @see: https://commitlint.js.org/#/reference-rules rules: { "subject-case": [0], // subject大小写不做校验 // 类型枚举,git提交type必须是以下类型 "type-enum": [ // 当前验证的错误级别 2, // 在什么情况下进行验证,always表示一直进行验证 "always", [ "feat", // 新增功能 "fix", // 修复缺陷 "docs", // 文档变更 "style", // 代码格式(不影响功能,例如空格、分号等格式修正) "refactor", // 代码重构(不包括 bug 修复、功能新增) "perf", // 性能优化 "test", // 添加疏漏测试或已有测试改动 "build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等) "ci", // 修改 CI 配置、脚本 "revert", // 回滚 commit "chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例) ], ], }, };
-
接着在
.husky
文件夹中新建文件commit-msg
,并添加配置:pnpm dlx commitlint --edit \
我们提交一个不规范的内容,测试一下:
此时,可以看到提交被拦截了。我们这时提交规范内容,测试:
提交成功,我们规定的提交格式是:
<type>(<scope>): <subject>
,其中type
和subject
是必填的。
Commitizen
基于
NodeJS
和git commit
的命令行工具,辅助生成标准化规范化的commit。参考:Commitizen——标准化的Git commit工具
Commitizen
分为两部分:脚手架(cz-cli)
和 规范/适配器(Adapters)
。
官方提供的默认 规范
是 cz-conventional-changelog,脚手架
就是Commitizen。
-
安装
commitizen
:**先全局安装:**为了快捷使用
git cz
命令pnpm i -g commitizen
再局部安装:
pnpm add -D commitizen cz-git -w
这里的
cz-git
就是适配器,更多的适配器请查看Adapters。
-
接着我们在
package.json
中添加一下内容,来指定路径:{ "config": { "commitizen": { "path": "node_modules/cz-git" } } }
commitlint配置prompt
在已经配置好 commitlint.config.js
后,即可使用 git cz
:
这是 commitlint.config.js
的配置:
需要注意的是:
rules
的subject-case
,建议置为0;更多信息参考:subject-case。
import { defineConfig } from "cz-git";
export default defineConfig({
extends: ["@commitlint/config-conventional"], // 限制commit规则
rules: {},
prompt: {
alias: { fd: "docs: fix typos" },
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀(可选):",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
confirmCommit: "是否提交或修改commit ?",
},
/** 提交范围域 */
// scopes,
// https://cz-git.qbb.sh/zh/recipes/#多选模式
enableMultipleScopes: true,
scopeEnumSeparator: ",",
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
types: [
{ value: "feat", name: "✨ feat: 新增功能 | A new feature" },
{ value: "fix", name: "🐞 fix: 修复缺陷 | A bug fix" },
{ value: "docs", name: "📃 docs: 文档更新 | Documentation only changes" },
{ value: "style", name: "🌈 style: 代码格式 | Changes that do not affect the meaning of the code" },
{
value: "refactor",
name: "🦄 refactor: 代码重构 | A code change that neither fixes a bug nor adds a feature",
},
{ value: "perf", name: "🎈 perf: 性能提升 | A code change that improves performance" },
{ value: "test", name: "🧪 test: 测试相关 | Adding missing tests or correcting existing tests" },
{
value: "build",
name: "🔧 build: 构建相关 | Changes that affect the build system or external dependencies",
},
{ value: "ci", name: "🐎 ci: 持续集成 | Changes to our CI configuration files and scripts" },
{ value: "init", name: "🎉 init: 初始化 | 项目初始化。" },
{ value: "revert", name: "↩ revert: 回退代码 | Revert to a commit" },
{ value: "chore", name: "🐳 chore: 其他修改 | Other changes that do not modify src or test files" },
{
value: "save-file",
name: "🤔 save-file: 保存文件 | 文件保存类型。仅仅是为了保存文件。有时候会需要紧急提交,并快速切换分支。此时就需要提交代码。并保存文件。",
},
{ value: "config", name: "⚙️ config: 更新配置 | 配置更新。通用性的配置更新。" },
{ value: "main-pull-update", name: "✋ main-pull-update: 主分支拉取更新 | 主分支拉取更新。" },
{ value: "del", name: "🗑 del: 删除垃圾 | 删除无意义的东西,注释,文件,代码段等。" },
{ value: "mark-progress", name: "⏩ mark-progress: 标记进度 | 标记进度。" },
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [
// 如果使用 gitee 作为开发管理
{ value: "link", name: "link: 链接 ISSUES 进行中" },
{ value: "closed", name: "closed: 标记 ISSUES 已完成" },
],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
});
总结
Husky
负责快速执行 Git Hooks
;Lint-Staged
对指定文件执行脚本。两者结合就是在指定事件时,对指定文件执行脚本;如 在提交时,对提交的文件进行代码检查和格式化 。
commitlint
用于对 commit
信息进行检查;commitizen
用于一键对 git commit
进行配置。两者结合更方便的对 git commit
格式化。
VS Code插件推荐
说明:插件 == 扩展、依赖 == node_modules包
VS Code
可以为团队成员推荐插件,在 .vscode
文件夹中新建 extensions.json
文件,在其中添加如下内容:
// .vscode/extensions.json
{
"recommendations": [
"lihuiwang.vue-alias-skip",
"formulahendry.auto-close-tag",
"formulahendry.auto-rename-tag",
"dzhavat.bracket-pair-toggler",
"ms-ceintl.vscode-language-pack-zh-hans",
"streetsidesoftware.code-spell-checker",
"mikestead.dotenv",
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"mhutchie.git-graph",
"eamodio.gitlens",
"kisstkondoros.vscode-gutter-preview",
"wxzhang.json-comments",
"ritwickdey.liveserver",
"shd101wyy.markdown-preview-enhanced",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"esbenp.prettier-vscode",
"alefragnani.project-manager",
"simonsiefke.svg-preview",
"vue.volar",
"dariofuzinato.vue-peek",
"sdras.vue-vscode-snippets"
]
}
团队成员可以通过在 扩展
界面输入 @recommended
,来找到推荐的拓展,并全部下载:
代码格式化插件
ESLint
此为
ESLint
依赖辅助显示插件,具体可查看:ESLint扩展安装
// .vscode/setting.json
{
// `ESLint`扩展:配置要检查的文件
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro"
]
}
Prettier - Code formatter
此为
Prettier
依赖辅助代码格式化插件,具体可查看:Prettier拓展安装
// .vscode/setting.json
{
// `prettier`扩展:配置要格式化的文件类型
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
EditorConfig for VS Code
此为编辑器代码风格统一工具,让团队成员的代码样式趋于相近,具体可查看:格式化代码补充说明
JsonComments
随着项目的开发,
package.json
内容越来越复杂,对项目的其他成员理解该文件,造成很大困扰。但不幸的是,package.json
是标准的JSON(无法使用 JSON5 ),但JSON是不支持添加注释的。于是我们可以使用VSCode插件JsonComments
来解决
Vue.volar、
优化开发体验插件
别名路径跳转
此插件可以通过在
.vscode/setting.json
中配置中添加内容,来跳转@
映射的路径。
// .vscode/setting.json
{
// `别名路径跳转`扩展:配置别名映射
"alias-skip.mappings": {
"@": "/src" // 默认只有`@`映射
},
"alias-skip.allowedsuffix": ["js", "vue", "jsx", "ts"], // 缺省后缀名的文件列表
"alias-skip.rootpath": "package.json" // 默认项目根目录的依据
}
Auto Close Tag
此插件可以自动闭合
html
标签,优化了开发体验。具体演示可查看:Auto Close Tag。
Auto Rename Tag
此插件可以自动重写尾标签内容,优化了开发体验。具体演示可查看:Auto Rename Tag
Bracket Pair Colorization Toggler
此插件可为方括号添加颜色用于区分。
Chinese (Simplified)
简体中文插件。
Code Spell Checker
此插件用于检查代码拼写,极大减少了因为拼写导致的莫名错误。
// .vscode/setting.json
{
"cSpell.words": ["zheng"]
}
DotENV
此插件用于对
.env
文件进行语法高亮。
Image preview
此插件用于在引用图片行代码左侧显示预览图片。
Svg Preview
此插件可以预览svg图片。
Live Server
此插件可用于在线预览静态html页面。
Markdown Preview Enhanced
此插件可用于在界面右侧预览markdown文档。
npm Intellisense
此为npm智能导入插件,在import时会自动提示可以导入的依赖包。
Path Intellisense
此为路径自动补全插件。
Git管理插件
Git Graph
著名的Git管理工具,此插件可以查看每次提交的内容等。
GitLens — Git supercharged
此插件拥有查看每个文件每行代码的历史提交等功能。
Vue插件
Vue - Official
官方的Vue插件,提供代码样式高亮等功能。
Vue VSCode Snippets
此插件提供快捷的命令,可以快速构建vue相关命令。
Vue Peek
此插件可用于跳转
.vue
文件中的组件、脚本等文件。
其他插件
Project Manager
非常推荐的插件!当你有多个项目时,频繁的打开资源管理器可能会让你感到厌倦。使用此插件后,所有的项目可在VS Code侧边栏一键打开。
子包/组件库封装方式
学习ts后进行:
支持全局引入:withinstall
https://www.cnblogs.com/wp-leonard/p/17894496.html
https://juejin.cn/post/7324599628463587380
自动类型声明:vite-plugin-dts https://blog.csdn.net/gitblog_00016/article/details/138945919
自己的包安装后提示找不到文件解决方式
问题复现:
在自己的包中:
// rom-el-table/types/global.d.ts
/**
* global.d.ts — 这个文件位于components包的根目录,
* 用于给vscode的volar插件提示我们组件的属性的类型
*/
declare module "vue" {
export interface GlobalComponents {
RomTableList: (typeof import("../src/TableList.vue"))["default"];
RomTableList2: (typeof import("../src/TableList2.vue"))["default"];
}
interface ComponentCustomProperties {}
}
export {};
// rom-el-table/package.json
{
"name": "@rom/el-table",
"version": "0.0.1",
"description": "@Rom写的对element-plus的table组件二次封装",
"main": "index.js",
"scripts": {},
"keywords": [
"element-plus",
"table",
"vue",
"封装"
],
// 必要的
"exports": {
".": {
"import": "./index.js",
"types": "./types/global.d.ts"
}
},
"author": "Rom",
"license": "MIT"
}
在项目中:
// test-app/jsconfig.json
/* https://code.visualstudio.com/docs/languages/jsconfig */
{
// 配置解析文件
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES6"
},
// 包含的文件
"include": ["src/**/*"],
// 忽略检测的文件,可提高VS运行速度
"exclude": ["node_modules", "dist", "public", "src/assets"]
}
问题解决后:
API设计篇——基于Axios
常见请求方式
原生Ajax方法
此为利用
jQuery
实现的Ajax方法。Ajax
核心就是应用XMLHttpRequest
对象,可以实现在不重载页面的情况与Web服务器交换数据。
$.ajax({
type: "POST",
url: url,
data: data,
dataType: dataType,
success: function () {},
error: function () {},
});
Fetch请求方式
Fetch
是基于promise
设计的,其代码结构比起Ajax
简单多了。
fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data), // data can be `string` or {object}!
headers: new Headers({
'Content-Type': 'application/json'
})
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
Axios请求方式
Axios
是基于promise
的HTTP库,作用于node.js
和浏览器中。
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
对Axios二次封装
Axios
相比Fetch
拥有更丰富的内容,适合大型项目。而Fetch
对原生浏览器有更好的兼容性,适合小型项目。
而本文目的是为了制作一款通用前端框架,于是采用Axios
进行封装。结合Typescript和Axios源码,设计一套实用的API层架构
展望:请求响应拦截(请求中断)、超时、地址(反向代理)
未完待续...