我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情
前言
最近刚好接触了一个 yarn workspace 的项目,在学习了解该项目的同时又了解到了 pnpm,pnpm 号称是最好用的 npm 工具,而且也支持 workspace 模式的开发。
众所周知现在 menorepo 已经成为近两年以来最热的词汇之一了,趁着这个机会就搭建了一个这样的项目框架,可以在以后开发新项目的时候直接在新的项目中使用。同时在拆包的时候,可以把一些已经积累在一起的组件和工具进行累积,使每个子包都可以健壮起来。
这个项目结构会满足以下几个功能:
- 至少包含4个子包,4个子包分别是:main/utils/components/cli
- main 中存放项目主体,只涉及项目业务
- utils 中存放项目中使用到的 js 工具类,供 main 使用
- components 中存放可抽离为通用解决方案的组件,供 main 使用
- cli 是自己写的脚手架,支持使用命令行生成页面并配置 router/menu/store 或 生成 components。
- utils 和 components 两个子包中的内容尽量避免 反向引用 main 中的内容,充分解耦,功能更加单一。
- 对于 是否应该把 utils 和 components 发布出去这个问题,我的想法是在 cli 中执行 git 命令,还是使用这种架构方式,方便对组件和工具的可持续化集成,并在集成过后提交到 git 仓库。所以这个仓库就必须要有完善的管理机制。避免频繁的改动或者破坏性更新。
- 整个项目要支持 eslint 校验规范以及遵循 git commitMsg 提交规范。
- components 应该提供一份文档出来,技术选项为 vitepress。
开发过程
一、搭建 pnpm workspace 项目结构
安装 pnpm
npm i -g pnpm
下面有 pnpm 的中文文档地址。
配置 workspace
创建配置文件 pnpm-workspace.yaml
packages:
- 'packages/**'
注意:第二行
-
后面跟着一个空格,因为这个空格,我搞了两天~~~
二、第一个子包: 创建 vite+vue3+ts 项目
1. 创建应用并安装依赖
# 使用 vite 创建项目
$ pnpm create vite main
# 安装依赖
$ pnpm install
因为后续还要将 components 进行分包,所以共用一套 vue 环境,所以我把依赖放在了主包上,但是不知道为什么好像并没有生效. . .
将 main 中的 package.json 中的 依赖都复制到 跟目录中的 package.json 中
2. 启动应用
在根目录下的 package.json 中配置启动脚本,并执行:
// package.json
{
...
"scripts": {
"dev:main": "pnpm -r --filter @motorepo/main run dev",
"build:main": "pnpm -r --filter @motorepo/main run build"
},
...
}
// 本地开发:在根目录下执行
$ pnpm run dev:main
// 打包:在根目录下执行
$ pnpm run build:main
启动成功之后:
按照提示,浏览器中访问地址 http://127.0.0.1:5173/ ,查看项目即可。默认项目如下:
解决 ts 提示报错的问题
现在效果是可以看到了,但是打开代码以后,会发现代码上会被 ts 有两个错误提示:
- App.vue 中 html 部分标签会被提示
【JSX 元素隐式具有类型 “any“,因为不存在接口 “JSX.IntrinsicElements“】
但是后来把配置改回去没有复现,但是当时确实解决了:
a. 不使用严格的类型检查,即在 tsconfig.json 中设置 “strict”: false
{
"compilerOptions": {
"strict": false
}
}
b. 在 tsconfig.json中设置 “noImplicitThis”: false
{
"compilerOptions": {
"noImplicitAny": false, // 是否在表达式和声明上有隐含的any类型时报错
}
}
- HelloWorld.vue 中,引用变量会被提示
类型“{}”上不存在属性“count”
解决方案为在 main 的根目录下创建如下文件
/**
* vue-file-import.d.ts 文件
* 添加此文件用来解决ts提示 【类型“{}”上不存在属性“count”】 的问题
*/
declare module"*.vue" {
import Vue from"vue";
export default Vue;
}
3. 配置 router
安装依赖 vue-router
$ pnpm add vue-router -filter @motorepo/main
配置过程分为三步:
- 编辑一个 页面 (index.vue)
// /pages/main/index.vue
<template>
<div>index.vue</div>
</template>
- 编辑 router/index.ts
// /router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'indexLayout',
component: () => import('../pages/main/index.vue'), // 懒加载组件
},
]
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
- 在 main.ts 中挂载 router
// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引入 router
import router from './router'
createApp(App).use(router).mount('#app');
这里只是把项目跑起来,并没有对 router 进行下一步的优化,后续的修改方向:
项目在后续的开发过程中肯定是按模块进行开发,后续 router 中使用 require 的方式遍历模块下的 router 文件,实现自动挂载模块 router。
这样的话在大团队中,每个人负责一个模块,各自维护自己模块的 router ,避免改动 根目录下的 router。
同样,store 也可以这么做。
4. 使用 pinia 作为状态管理,并实现数据持久化
安装依赖:
$ pnpm add pinia -filter @motorepo/main
配置过程也是三步:
- 编辑 pinia install 文件
// store/install.ts
// 引入 pinia
import { createPinia } from 'pinia';
// 持久化工具
// import piniaPluginPersist from 'pinia-plugin-persist';
// 实例化
const store = createPinia();
// 使用持久化工具
// 还需要再对应的 store 中开启 persist 字段
// store.use(piniaPluginPersist);
// 导出 store
export default store;
// # 注意:在其他模块中使用store的话,
// # 必须引入这个实例,
// # 不然无论如何都会失去响应式,
// # 而且和挂载在App上的store不是同一个
- 编辑 main 模块
// store/index.ts
import { defineStore } from 'pinia';
import pinia from '@/store/install';
const useMainStore = defineStore('main', {
state: () => ({
loading: false,
token: '',
currentScale: 0,
userInfo: {
username: '', // 用户名
realname: '', // 真实姓名
sex: '', // 性别
phone: '', // 电话
email: '', // 邮箱
birth: '', // 出生日期
avantor: '', // 头像
},
}),
getters: {
getUsername: (state) => state.userInfo?.username,
getToken: (state) => state.token,
getCurrentScale: (state) => state.currentScale,
},
// persist: {
// enabled: true,
// strategies: [
// {
// storage: localStorage,
// },
// ],
// },
});
// 数据持久化
// 1. 保存数据
const instance = useMainStore(pinia);
instance.$subscribe((_, state) => {
localStorage.setItem(
'login-store',
JSON.stringify({
...state,
}),
);
});
// 2. 获取保存的数据,先判断有无,无则用先前的
const old = localStorage.getItem('login-store');
if (old) {
instance.$state = JSON.parse(old);
}
export { useMainStore };
- 在 main.ts 上挂载 pinia
// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引入 router
import router from './router'
// 引入 pinia
import pinia from '@/store/install';
createApp(App).use(router).use(pinia).mount('#app');
- 使用方法
import { ref } from 'vue'
import { useMainStore } from '../../store'
const mainStore = useMainStore()
// 获取
const username = ref(mainStore.userInfo.username)
// 更新
setTimeout(() => {
mainStore.$patch({
token: null,
userInfo: { username: '中国人' },
})
}, 1000)
5. 为路径设置别名 @
修改 vite.config.ts,添加如下配置:
// vite.config.ts
import { resolve } from 'path';
resolve: {
alias: {
'@': resolve(__dirname, 'src'), // 设置 `@` 指向 `src` 目录
},
},
添加完这个配置后ts会报错提示,path 找不到的问题
解决方案分为两步
- 安装 @type/node 依赖: pnpm add @types/node -filter @motorepo/main
- 修改 tsconfig.json ,在 compilerOptions 字段中增加配置: "types": ["vite/client", "node"]
除了上面的修改之外,tsconfig.ts中也需要配置 @ 别名。最终配置如下:
// tsconfig.ts
{
"compilerOptions": {
"baseUrl": "./",
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": false,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"types": ["vite/client", "node"],
"paths": {
"@/*": ["src/*"],
},
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
变动主要有三个字段:
baseUrl:在设置 paths 字段的时候如果使用相对路径,则必须先配置 baseUrl 才行
types:上面解决引入 path 模块的时候增加的
paths: 具体配置
6. 配置 sass
其实 vite 中已经预置了 sass 解析器,所以只需要安装 sass 即可使用。
# 为 main 安装 sass
$ pnpm add sass -filter @motorepo/main
使用方法呢,直接在刚才的 index.vue 中使用就好了。
7. 安装 element-plus 并配置全局变量
element-plus 是专门为 vue3 做的 UI 组件库。
默认安装方法固然简单,但是真实的开发场景下,全局的 css 样式会包含:组件库css、全局默认css、iconfont、全局 scss 变量。可能还有其他的,如动画库等。因为是搭个架子所以先不放动画库了。
为 main 安装 element-plus 依赖。
$ pnpm add element-plus -filter @motorepo/main
main.ts 中安装 element-plus 并配置中文包
// 引入 elementPlus
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
createApp(App)
.use(ElementPlus, {
locale: zhCn,
})
.use(router)
.use(pinia)
.mount('#app')
将所有的 css 样式整合到一个入口文件,然后统一挂载在 main.ts
// assets/styles/index.scss
@import './base';
@import './elment-reset';
// @import './iconfont/iconfont.css';
---
// assets/style/element-reset.scss element样式并修改默认样式
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'white': #ffffff,
'black': #000000,
'primary': (
// 'base': #409eff,
'base': #333,
),
'success': (
'base': #67c23a,
),
'warning': (
'base': #e6a23c,
),
'danger': (
'base': #f56c6c,
),
'error': (
'base': #f56c6c,
),
'info': (
'base': #909399,
),
)
);
@use 'element-plus/theme-chalk/src/index.scss' as *;
.el-button {
border-radius: 0;
}
.el-menu--horizontal {
border-bottom: none;
}
.el-dialog__footer,
.el-dialog__header {
background: $white;
}
---
// assets/styles/base.scss
* {
margin: 0;
padding: 0;
}
html,
body,
#app {
width: 100%;
min-width: 1336px;
height: 100%;
background: $white;
font-family: 'PingFang SC', '思源黑体CN', 'Mircrosoft YaHei', 'SF UI Text', Arial, sans-serif;
}
button,
input,
optgroup,
select,
textarea {
font-family: 'PingFang SC', '思源黑体CN', 'Mircrosoft YaHei', 'SF UI Text', Arial, sans-serif;
}
// 列表
ul,
ol,
li {
margin: 0;
padding: 0;
list-style: none;
}
/** 滚动条 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/** 滚动条轨道 */
::-webkit-scrollbar-track {
background: none;
}
/** 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
min-height: 28px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.3);
background-clip: padding-box;
}
/** 滚动条没有滑块的轨道部分 */
::-webkit-scrollbar-track-piece {
opacity: 0;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.4);
}
.fl {
float: left;
}
.fr {
float: right;
}
.ellips {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.page-main {
background: #fff;
min-height: calc(100% - 84px);
padding: 24px;
}
// assets/styles/global-varible.scss
$headerHeight: 60px;
$footerHeight: 100px;
$mainWidth: 1336px;
$white: #eef7f2;
$blue: #3a89b0;
$black: #333;
如上四个 scss 文件,其中 index.scss 作为最终挂载文件,把其他的 scss 文件统一引入,最后挂载在 main.ts 中。挂载方法是在 main.ts 中加入如下代码:
// 引入所有的公共样式
import '@/assets/styles/index.scss';
global-varible.scss 则是全局变量,可见到里面设置了几个颜色和尺寸。需要挂载在 vite.config.ts 上。挂载方法是在 vite.config.ts 中增加如下代码:
// 配置全局 scss 变量文件
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/assets/styles/global-variable.scss";`,
charset: false,
},
},
},
需要注意的是 配置全局变量的时候,用到了 路径别名 @,所以要先配置 路径别名。
至此,在 index.vue 中就可以使用 element 组件了。我写了几个 按钮:
可见,主题颜色已经变化,且修改后的 button 样式也已经生效。
三、第二个子包:第三方工具解耦独立配置安装
第二个子包我选择放置一些开发工具类的内容。
我将它命名为 utils,作为示例使用的话,封装了一个 el-message 。
开发过程很简单,使用有序列表简单描述一下,然后粘贴出示例代码:
- packages 中执行命令
mkdir utils && cd utils && npm init -y
- 修改 utils 包的
package.json
中的name
字段,改为@menorepo/utils
- 在 utils 包的根路径下创建 index.ts 作为导出所有工具的出口
- 在 utils 包中创建 src 路径,用来存放工具
- 在 src 中新建 message 路径,开发 message 工具,message 其实是基于 element-plus 的 ElMessage 和ElMessageBox 进行的简单封装。所以也要安装 element-plus。
- 为 main 包安装 utils 包
pnpm add @menorepo/utils -filter @menorepo/main
- 在 main 中的页面中使用 message
// message.ts
import { ElMessage, ElMessageBox } from 'element-plus';
const base = (msg: string) => ({
duration: 4000,
showClose: true,
message: msg,
dangerouslyUseHTMLString: true,
});
export default class Message {
static error(msg: string) {
return ElMessage({
type: 'error',
...base(msg),
});
}
static success(msg: string) {
return ElMessage({
type: 'success',
...base(msg),
});
}
static warning(msg: string) {
return ElMessage({
type: 'warning',
...base(msg),
});
}
static info(msg: string) {
return ElMessage({
type: 'info',
...base(msg),
});
}
static confirm(msg: string, resovleFn: () => void) {
return ElMessageBox.confirm(msg, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
resovleFn();
})
.catch(() => Message.info('哦,原来是点错啦!'));
}
static delete(msg: string, resovleFn: () => void) {
return ElMessageBox.confirm(msg, '删除', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
resovleFn();
})
.catch(() => Message.info('哦,原来是点错啦!'));
}
}
核心点是 第6点。
四、第三个子包:公共组件进行解耦
公共组件其实是一个可持续集成的一个子包,可以在合适的时候直接发布到 npm 上。供 menorepo 模式的工程使用。
开发流程很是简单粗暴:
- 创建一个子包 components 件并初始化:
$ cd packages && mkdir components && cd components && npm init -y
记得修改包名 : @menorepo/components
- 开发一个组件并导出
项目结构如上图所示。
源码粘一下:
// src/button/delete-button.vue
<template>
<el-button type="danger"> 删除 </el-button>
</template>
<script lang="ts" setup>
// import {ElButton}
</script>
---
// src/button/index.ts
export * from './delete-button.vue'
import type { App } from 'vue'
import DeleteButton from './delete-button.vue'
DeleteButton.install = (app: App) => {
app.component('delete-botton', DeleteButton)
}
export { DeleteButton }
export default DeleteButton
---
// index.ts
export * from './src/button'
---
// tsconfig.json
{
"compilerOptions": {
"jsx": "preserve", // 可解决 *.vue 组件第一行,ts 提示异常的问题。
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}
- main 包中安装 子包 并开始消费组件
安装: $ pnpm add @menorepo/components -filter @menorepo/main
消费:(还是在 index.vue中测试)
// main/src/pages/index.vue
<template>
<div class="page">
<div>index.vue</div>
<div>{{ username }}</div>
<el-button type="primary">默认按钮</el-button>
<!-- 消费 components 包中的组件 -->
<p><DeleteButton /></p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useMainStore } from '../../store'
import { DeleteButton } from '@menorepo/components' // 引入components 包中的组件
import { Message } from '@menorepo/utils'
const mainStore = useMainStore()
const username = ref(mainStore.userInfo.username)
Message.success('123123')
setTimeout(() => {
mainStore.$patch({
token: null,
userInfo: { username: '中国人' },
})
}, 1000)
</script>
<style lang="scss">
.page {
background-color: pink;
}
</style>
至此,多包模式已经可以告一段落。
计划中的三个包已经完成,并可以互相调用。
这时候回头去和传统的单包开发模式进行对比,我发现了几点好处:
- components 和 utils 两个包独立出来,可以独立发布,独立维护,大大的提高了复用性。
- 项目模块清晰明了,main 包更专注于业务逻辑,而和业务耦合程度较低的内容都可以独立出去。
- 一个仓库中可以存放多个项目,项目文档维护成本降低。
- 其他的还没发现 . . .
五、第四个子包:脚手架工具的封装
这个脚手架的功能主要是有两个目的:
- 项目初期,提高项目的搭建效率,使用命令行的方式,迅速根据产品将项目的所有页面搭建起来,开发人员的目光直指业务逻辑,不再会为了搭建开发环境浪费时间。
- 保证整个项目的大结构统一,降低后来介入人员的学习成本。
总归一句话,解放双手,开心摸鱼。
1. 思路
2. 开发流程
六、代码规范及git规范的配置
1. eslint 安装及配置
# 根目录安装 eslint
$ pnpm add eslint -D -w
# 初始化 eslint, 会根据配置生成 .eslintrc.js
$ pnpm eslint --init
You can also run this command directly using 'npm init @eslint/config'.
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser, node
√ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
√ Would you like to install them now? · No / Yes
√ Which package manager do you want to use? · pnpm
在 package.json 中的 script 中增加命令:
# eslint . 为指定lint当前项目中的文件
# --ext 为指定 lint 哪些后缀的文件
# --fix 开启自动修复
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"
然后执行 pnpm lint
就可以看到lint结果了。
在执行lint的时候,会发现少了三个依赖,会逐次提示,具体安装如下:
$ pnpm add eslint-plugin-vue@latest -D -w
$ pnpm add @typescript-eslint/eslint-plugin@latest -D -w
$ pnpm add @typescript-eslint/parser -D -w
果不其然,好几个问题:(其实总共有10个问题的,已经处理了3个,下面都会有解决记录)
5:36 error Don't use `{}` as a type. `{}` actually means "any non-nullish value".
- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.
- If you want a type meaning "any value", you probably want `unknown` instead.- If you want a type meaning "empty object", you probably want `Record<string, never>` instead @typescript-eslint/ban-types
5:40 error Don't use `{}` as a type. `{}` actually means "any non-nullish value".
- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.
- If you want a type meaning "any value", you probably want `unknown` instead.- If you want a type meaning "empty object", you probably want `Record<string, never>` instead @typescript-eslint/ban-types
5:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
E:\@self\motorepo\packages\utils\src\http\http.ts
63:39 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
66:39 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
69:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
72:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
✖ 7 problems (2 errors, 5 warnings)
- 解析 vue 文件失败,报错为 提示 vue 文件第一个 尖括号 < 就报错了。
修改 .eslintrc.js 中。parser 字段改为: vue-eslint-parser 。
- 提示 vue 文件应该以驼峰方式命名
Component name "index" should always be multi-word
是因为 eslint 规定的,要检查组件名称,将这个规则改掉就好了。
修改 .eslintrc.js 中的 rule 字段,增加如下规则:
"vue/multi-word-component-names": [
"error",
{
ignores: ["index"], //需要忽略的组件名
},
],
- 提示 不要 以 {} 作为一个 类型
增加 rules 配置
"@typescript-eslint/ban-types": [
"error",
{
"extendDefaults": true,
"types": {
"{}": false
}
}
]
- 提示 不要以 any 作为类型
了解过另外一个项目组,他们组的要求是不允许使用 any 作为类型推断使用,这个有点严苛了,在开发过程中难免会用到 any
所以我选择将其关掉了
增加 rules 配置
"@typescript-eslint/no-explicit-any": ["off"],
至此,执行 pnpm lint
已经没有报错了!
因为现在代码比较少,后面如果有新的报警或报错,可以根据提示内容,再进行配置 .eslintrc.js 这个配置文件或者改代码。
2. prettier 安装及配置
安装 prettier
$ pnpm add prettier -D -w
在根目录下创建 .prettier.js
module.exports = {
// 一行的字符数,如果超过会进行换行,默认为80
printWidth: 80,
// 一个tab代表几个空格数,默认为80
tabWidth: 2,
// 是否使用tab进行缩进,默认为false,表示用空格进行缩减
useTabs: false,
// 字符串是否使用单引号,默认为false,使用双引号
singleQuote: true,
// 行位是否使用分号,默认为true
semi: false,
// 是否使用尾逗号,有三个可选值"<none|es5|all>"
trailingComma: "none",
// 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
bracketSpacing: true
}
在package.json
中的script
中添加以下命令
{
"scripts": {
"format": "prettier --write "./**/*.{html,vue,ts,js,json,md}"",
}
}
执行 pnpm format
可看到 git 记录中有好多文件都发生了变化,虽然有些看起来没变,但实际上有变化,而且跟计划中的是一致的。
husky 配置 git hooks
基于新版本的 husky 进行配置的: husky ^7.0.1,我装的是当前最新版本 8.0.1。
安装 并 初始化 husky
# 安装
$ npm add husky -D -w
# 初始化 husky 目录
# 这里没有查询 pnpm 命令,因为只加了个命令,所以就用 npm 了,理论上,手动增加一个也行。
$ npm set-script prepare "husky install"
# 执行新增的命令
$ pnpm prepare
# 添加 pre-commit 钩子配置
$ npx husky add .husky/pre-commit "npx lint-staged"
commit 时校验代码,并 format 代码
不得不提一嘴,程序开发真的越了解越知其精湛。
我也是在配置结束以后才有这个感觉的,husky 其实是把 git 的几个阶段都进行了代理,使开发人员在执行 git 命令的时候走 husky 的逻辑,将命令劫持,先执行开发人员自定义的一些操作。
正因如此,才能实现现在的操作。
我尝试了大家常用的 lint-staged 的配置方案,但是在 lint-staged 中配置执行命令,感觉会把 package.json 和 husky 的配置耦合在一起,为了解耦我尝试了几个方法,最终使用了下面的方法配置成功。
可以看到,网上的一些配置方案中,大家都是选择在 packege 中增加一个 lint-staged 字段,并且在里面配置 eslint 和 prettier 的相关命令,然后 在 script 中再增加 命令 "lint": "lint-staged"
,再然后在husky中执行 npx lint-staged
。
说真的,绕来绕去把我都给绕懵了,不知道在干嘛!
我考虑了一下,既然在 husky 中执行了 lint-staged,对应的是 package.json 中的 script 指令,所以 理论上是可以把 上面已经测试过没问题的 lint 和 format 命令放在 pre-commit 脚本里面就好了:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# npx lint-staged
pnpm run lint && pnpm run format
如上所示,直接将
npx lint-staged
指令换成pnpm run lint && pnpm run format
。
注意:只能这么写,顺序也不能乱,不然虽然控制台报错,依旧能commit成功,eslint 检查报错不会给 pre-commit 钩子捕获。
commitMsg 校验
-
格式:
git commit -m '类型: 描述性文字'
类型 概念 build 编译相关的修改,例如发布版本、对项目构建或者依赖的改动 ci 持续集成修改 docs 文档修改 feat 新特性、新功能 fix 修改bug perf 优化相关,比如提升性能、体验 refactor 代码重构 revert 回滚到上一个版本 style 代码格式修改, 注意不是 css 修改 test 测试用例修改 chore 其他修改,比如改变构建流程、或者增加依赖库、工具等 -
安装
$ pnpm add commitlint @commitlint/config-conventional -D -w
- 配置
package.json
中配置commitlint
{
// ...
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
}
}
- 添加钩子
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
git commit
就会触发提交规范的校验啦。
结语
以上就是所有内容了。
本文包含了如何新建一个 vite+ts+vue3 项目,以及日常开发中遇到的一些 ts 异常提示的解决方案。
懒人推进社会?干饭!!!
参考文档
文档相关
- pnpm 中文文档-workspace板块
- pnpm github issues 关于 No projects matched the filters in "C:\code\motorepo" 的解决方案
- vite+vue3+typescript+pnpm-workspace-monorepo 项目搭建记录 - 简书
- element-plus 中文文档
bug 相关
- JSX 元素隐式具有类型 “any“,因为不存在接口 “JSX.IntrinsicElements“
- 关于typescript:解决”类型’Vue’不存在属性”错误
- 找不到依赖,vue3 找不到模块、有关node类型的变量ts错误提示找不到