第八期:前端九条启发分享
一、 设置git commit的校验 (husky:^5
版本之前配置与这里不一样)
在一般情况下, 我们每次执行git commit -m'提交信息'
的时候, 会触发针对commit信息的校验以及代码风格的校验, 如果没通过校验会导致commit失败, 那么这些校验是如何加入到项目中的那?
大部分时候添加针对commit的校验我们会使用husky
这个包, 让我们来安装一下它:
yarn add husky -D
在packgae.json中添加下面内容, 这里是初始化.husky/目录并指定该目录为git hooks
所在的目录, 所谓的git hooks
可以理解为git的钩子函数, 比如git add
执行之前, git commit
之后等等生命周期的钩子函数, 而git hooks
所在目录就是用来定义git
各个生命周期需要执行的各种函数。
{
"scripts": {
"prepare": "husky install"
}
}
第一步: 添加代码校验
接下来我们在命令行执行:
yarn prepare
npx husky add .husky/pre-commit "npm run lint"
上述第二条命令定义了pre-commit
这个生命周期执行npm run lint
这句命令。
在packgae.json中定义lint
命令是使用eslint
检测src
文件夹里的文件, 当然啦检测什么文件都可以。
"scripts": {
"lint": "eslint src"
},
第二步: 添加提交信息校验
校验commit
信息我们最简便的方式就是依赖commitlint
这个包。
yarn add @commitlint/config-conventional @commitlint/cli -D
在根目录里新建commitlint.config.js
并写入下面内容:
module.exports = {
extends: ["@commitlint/config-conventional"]
};
添加校验commit的hook:
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
上面命令的意思是创建一个commit-msg
hook的文件, 里面的内容是npx --no-install commitlint --edit "$1"
。
commitlint
命令来检测$1
这个参数的值是否合法, 而$1
就是我们的git commit -m ${1}
。
校验的标准
这里我只列出最常用的几个:
规范 | 使用方式 |
---|---|
docs | git commit -m"docs: 更新xxx文档" |
feat | git commit -m"feat: 新增xxx模块" |
fix | git commit -m"fix: 修复了上线的xxxxbug" |
style | git commit -m"style: 更新用户信息弹框的样式" |
test | git commit -m"test: 修改添加用户按钮的测试用例" |
与你项目里配置不一样?
有的同学可能发现了, 我这里的写法可能与你项目里的写法不一致, 你的项目可能是如下的写法:
{
"husky": {
"hooks": {
"pre-commit": "npm run test",
"commit-msg": "commitlint -e $HUSKY_GIT_PARAMS"
}
}
}
上述写法是husky^6
版本之前的写法, 现在husky
已经更新到了7版本, 所以未来创建新项目可能不会再用这种写法配置了。
git的hook
二、 ///这种注释的使用
我们在react
项目代码里会看到下面这种注释, 那么这种奇怪的注释是干么的那?
/// <reference types="react-scripts" />
原来这种/// <reference types="包" />
的写法主要用在.d.ts
文件里面, 作用是引入其他的.d.ts
文件, 比如我需要用到一个xxx.d.ts
文件, 但是这个并没有放在node_modules/@types
里面, 所以并不会被系统默认导入。
三、 推荐一款react
引导插件 guide
一般网站第一次访问的时候都会有个引导操作, 使用guide
就可以轻松实现, 让我们一步步来配置:
yarn add byte-guide
这个插件的使用方法的中心思想是, 我们以数组的形式传入一套配置, 找个配置里面有要被挂载
的目标dom, 以及显示的文案等信息, 然后每次点击下一步就可以依次执行数组内的对象:
import Guide from "byte-guide";
export default function Home() {
const SEARCH = {
hotspot: true,
title: "搜索组件",
selector: "#search",
placement: "top-left",
content: <div>这个搜索组件很厉害</div>,
offset: { x: 20 },
};
const TOTAL = {
title: "列表总数",
parent: "#search",
selector: "#total",
placement: "right",
content: "这里是列表总数。",
};
return (
<div className={"org"}>
<div id={"search"}>目标1</div>
<div id={"total"}>目标2</div>
<Guide
lang="en"
localKey="uni-key"
className="my-guide"
steps={[SEARCH, TOTAL]}
onClose={() => {
console.log("点击关闭");
}}
afterStepChange={(nextIndex, nextStep) => {
console.log("点击下一步");
}}
/>
</div>
);
}
主要属性的说明
localKey
: 我们引导组件的显示一般是只有用户第一次登录才显示, 或者是当用户点击了引导的结束按钮就永远不显示了,guide
将是否需要展示引导组件的值储存在Local Storage
中, 所以这里定义了储存的key名。placement
定义了弹出框所在的位置, 比如顶端的元素我们会选择使用bottom
之类的属性。offset
虽然设置了placement
但是难免位置不尽人意, 所以可以使用这个属性对引导框进行位置的再调。
注意事项
组件内置了简单的国际化, 也就是当我们不指定文案的时候, 他自己会根据传入的语言英语
、中文
、日语
进行文案的改变。
这个插件有bug, 如果两个元素处于父元素的内部滚动状态时, 会出现定位偏差非常多的问题, 所以如果你的引导项目涉及内部滚动的元素则不建议使用这个插件。
四、 eslint 7 版本之后可以注释
发现团队项目中难免会出现一些/* eslint-disable*/
这种注释, 每次看到会尝试将其删除, 但是可能会引起一系列的连锁翻译, 这种情况下如果我们使用的是eslint^7
版本及以上, 则可以添加注释, 说明为什么这里必须使用/* eslint-disable*/
。
// eslint-disable-next-line a-rule -- 这里的错误无法修复!
上面的写法更加友好, 因为毕竟这种禁止eslint
检测的方式不推荐大家使用, 如果必须要使用也需要在初期就标明原因。
五、 统一一个index文件导出的利与弊
比如我们有很多的工具方法
放在同一个文件夹下,a.js
文件导出a1, a2
等方法 我们常常使用一个index.js
文件进行统一的导出, 项目里会有很多如下的写法:
export * from './a';
export * from './b';
export * from './c';
export * from './d';
这种写法虽然看起来计较"优雅"但是有不少弊端存在, 这里我们就列举一下:
1: 导出名字可能重复
这是个小问题因为会有ts
报错提示, 起因是index.js
文件里面没有写出具体的方法名, 所以需要每次修改留意一下index文件是否报错。
2: 代码寻径麻烦
这点也就是对操作最不友好的一点, 比如我们在某个页面上有一个外界导入的方法, 我们mac电脑的话点击`c
ommand + 鼠标左键就可以查到这个方法的出处, 但是往往只是寻径到了
index.js`, 这里也没有具体的方法名称呈现, 所以需要再次进到各个文件里面查询。
3: 改变导出没有提示
当我们修改一个导出时没有提示, 比如删掉一个导出方法, 修改一个导出方法的名称, 此时并不会在文件本身以及index.js
文件种报错, 报错的地方是具体使用到这个方法的文件, 这就可能会忽略到, 因为不启动项目直接git push
也是可以检验通过的。
4: ts类型二次推导
有时候会出现你命名导出了一个方法函数, 但是eslint报错说没有导出该成员
, 但是当我们进入到这个方法的文件夹里后, eslint的报错就消失了, 这种不好的体验都与这种写法有关。
六、svgr
库转换svg图片
, filter:drop-shadow
xxx.svg
这些文件是不好控制的, 我们想动态的传入很多配置可能需要借助svgr
这个库将svg图片
转换成react
组件的形式, 通过下面的命令可以在命令行生成代码数据:
npx @svgr/cli --icon 图片的地址.svg
在命令行里生成肯定是不能工程化的, 其实各种脚手架已经内置了这个svgr
库, 所以我们其实如下方式就可以使用组件模式的
svg图片啦。
import { ReactComponent as Logo } from "./logo.svg";
// ....
<Logo width={"30px"} style={{ filter: "drop-shadow(red 20px 0)" }} />
filter: "drop-shadow(red 20px 0)"
这句可以使svg变成红色,关于它的细节我会单拿一篇文章去讲。
七、 yarn resolutions 管理被多项目依赖的包
项目中会引用很多的npm包, 比如我们项目里面依赖了A包
, A包
自身又依赖 ^3.1.5
版本的B包
, 我们项目本身yarn add B
已经安装了^3.0.5
版本的B包
, 此时B包
其实是可以通用的, 完全没必要引用两个版本。
并且如果打包的话由于我们两处依赖的不同版本的B包
导致打包后体积的增大, 所以yarn
提供了resolutions
这个配置项, 我们可以如下配置(package.json):
"resolutions": {
"B": "^3.1.5"
}
举一个实际的例子
我们创建一个新项目
npx create-react-app 项目名称
进入到项目中
yarn list --depth=1
列出项目的依赖, 并且只打印一级依赖即可, 我们就以workbox-build
为例:
我们在外层package.json
进行配置:
"resolutions": {
"fs-extra": "^4.0.2"
}
此时执行yarn
命令, 并且在此yarn list --depth=1
打印:
上面的依赖关系已经变化了, 不是仅仅在workbox-build
包的下面了。
八、 pnpm 代替 yarn lean做包的管理
我们经常使用yarn + lean
这种方式管理子项目, 但是传输中pnpm
更适合做这个工作, 那么为什么说pnpm
更适合那?
yarn
管理node_modules
的机制会导致, A包
依赖的B包
, 那么B包
的文件位置会处于与A包
同级, 这就导致开发者虽然没有执行yarn add B
但是项目中也可以引入import B from 'B'
, 这就为开发埋下拉隐患。
pnpm
采用的方式是, 将包统一管理, 单个项目的node_modules
文件里面储存的类似软连接
, 这样就不会出现重复安装某个依赖的情况了, 并且也没必要将B 与 A
放在同一层级了, 也就不存在莫名其妙的引入B包的情况
。
当我们有很多子项目并且依赖越来越复杂的时候, pnpm
的优势就显示出来了。
九、 微前端的子应用, 如何单独启动
微前端应用里面都会有几个子项目, 这些子项目往往会依赖一个主项目(壳工程), 主项目负责分发一些重复使用的资源, 比如react react-router
等等公用的依赖, 并且也会出现主项目里的一些自定义的utils
工具方法传递给子项目
。
由于存在众多需要主项目分发的资源那么就对子项目单独的运行制造了困难, 我们每次都要启动至少主项目+子项目
, 并且想要看到完整工程还需要启动其余的所有子项目
, 但我们有什么办法只启动子项目自身那?
我这里的做法是利用请求的代理, 因为大部分微前端框架的原理是每个子项目
都是一个js文件
, 主应用启动后按需加载子项目的js入口文件
, 那么我们就可以访问线上测试环境
的地址, 然后找到线上测试环境
的主应用访问子应用的url
, 然后将这个url
代理其指向本地启动的子项目
的地址即可。
这样做的好处就是可以让子项目完全处于一个线上环境的主应用的上下文里, 并且不用启动其余子项目, 要注意子项目的css
文件也要代理过来。
end
这次就是这样, 希望与你一起进步。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。