写点什么

【解决】前端开发中的 5 大痛点

作者:GFE
  • 2022-11-01
    北京
  • 本文字数:8032 字

    阅读完需:约 26 分钟

【解决】前端开发中的5大痛点

前言:

通过阅读本文你了解到这 5 大痛点如何避免和解决:


  1. 如何优雅且快速的切换 Node 版本?

  2. 如何统一项目中包管理器的使用?

  3. 如何养成规范 GitCommit 编写习惯?

  4. 如何不 Copy 快速开发相似的页面/组件?

  5. 如何编写 CSS 会让同事更愿意阅读?

Node 版本管理器:

为什么要管理 Node 版本?

  1. 当我们开发的项目变得多起来的时候你就会发现每个项目所依赖的 NodeJs 环境的版本会相差很大,因为新项目的搭建我们肯定是会选择更加稳定的最新版本,那么当我们需要对历史项目进行维护的时候就免不了要切换到当时的 NodeJs 版本。

  2. 当我们想要尝鲜一些 Bate 版本的 NodeJs 所带来的新特性的时候就需要一个 Bate 版的 NodeJs 环境。

如何实现 Node 版本切换?

  1. 拒绝重复下载,重复安装;

  2. 推荐使用工具:nvm-windows,nvm

实际案例分享:

     在一次 JavaScript 脚本问题修复中发现原本正常使用的fs.writeFile函数抛出了类型异常导致数据无法正常输出到文件,在查询 API 后发现fs.writeFile(file, data[, options], callback)函数的callback参数在v7.0.0中被标记为需要必选,在v10.0.0中被要求强制必选。导致这次故障的原因就是在升级 NodeJs 版本的时候没有考虑到对历史项目影响,并在切换 NodeJs 版本到v10以下后验证了这一问题。

安装说明:

  1. 因为团队中使用 win 系统为大多数,这里还是主要来介绍 win 环境的安装;

  2. 安装前为了避免冲突,请先卸载已安装的 NodeJs,并检查下面这几个目录;

  3. 需要清空:%ProgramFiles%\nodejs

  4. 需要清空:%UserProfile%\.npmrc

  5. 建议备份:%AppData%\npm\etc\npmrc%UserProfile%\.npmrc

  6. 下载nvm-windows并按默认推荐安装,安装后重启终端通过nvm version查看版本验证安装结果;

  7. 注意需要使用管理员权限打开终端。

常用命令介绍:

  1. 查看已安装的 NodeJs:nvm list [available]

  2. 安装指定版本的 NodeJs:nvm install <version> [arch]

  3. 卸载已安装的 NodeJs:nvm uninstall <version>

  4. 切换 NodeJs 版本:nvm use [version] [arch]

更多请参考下图:

规范包管理器使用:

     同样是在协作开发中发现的问题,因为使用了不同的包管理器,导致在本地开发环境一切正常的代码推送远程后在服务器构建中却意外的失败了,但在其他同事的电脑中再次尝试本地构建是可以复现问题的,这是为什么呢?我们在最后查看node_module中的内容时发现,由于使用的不同的包管理器导致文件拉取出现了差别最终导致的这个问题。这里我们通过一份预安装脚本来限制使用相同的包管理。

预安装脚本工作原理介绍:

     通过在 install 正式执行前通过在进程中获取可以区别当前使用的包管理器的特殊标识来决定是允许还是中断执行。


包管理器标识获取:

  1. UserAgent 方案,应用案例是 which-pm-runs;


标识获取:process.env.npm_config_user_agent
复制代码


输出结果:npm/6.14.5 node/v14.17.1 win32 x64yarn/1.22.10 npm/? node/v14.17.1 win32 x64
复制代码


// UserAgent方案的完整脚本
// 定义统一包管理器const allowPM = 'yarn'const userAgent = process.env.npm_config_user_agent || ''if (userAgent !== '') { const pmName = userAgent.substring(0, userAgent.indexOf('/')) if (pmName !== allowPM) { console.warn( `\u001b[33m This repository requires using ${allowPM} as the package manager for scripts to work properly.\u001b[39m\n` ) process.exit(1) }}
复制代码


  1. ExecPath 方案,应用案例是 vue-next;


标识获取:process.env.npm_execpath
复制代码


输出结果:C:\...\nvm\v14.17.1\node_modules\npm\bin\npm-cli.jsC:\...\npm\node_modules\yarn\bin\yarn.js
复制代码


const allowPM = 'yarn'const execpath = process.env.npm_execpath || ''if (!new RegExp(`${allowPM}`).test(execpath)) {  console.warn(    `\u001b[33m This repository requires using ${allowPM} as the package manager for scripts to work properly.\u001b[39m\n`  )  process.exit(1)}
复制代码

推荐方案 only-allow:

     only-allow 为 pnpm 包管理器组织开源限制方案,only-allow 内部使用 which-pm-runs 来获取当前执行的包管理器后再进行判断拦截,仅需在安装依赖后调整 scripts 中的内容即可,在 vite 项目中有使用。


  1. 添加命令:npm set-script preinstall "npx only-allow yarn"

  2. 命令执行需要 npm 版本在 v7+;

  3. 安装仅此一步。

  4. 当使用了其他的包管理执行install命令将会被中断。


GitCommit 规范:

     遵守 GitCommit 的通用规范是每个优秀项目比不可少的环节,不同的项目需求中会衍生更多的依赖项需要配置,这里我们通过介绍 husky7、commitizen/cz-cli、commitlint 三大套件的基础使用来让你快速上手拦截住不规范的 Commit 信息。

三大件套介绍(husky7+commitizen+commitlint):

  1. husky7:在 git 执行的生命周期进行 hook,替代 git 自带的 hooks;

  2. commitizen/cz-cli:通过交互的方式生成规范的 GitCommit 信息;

  3. commitlint:检查 GitCommit 信息是否规范。


husky7 使用指南:

准备工作:

  1. 安装依赖:npm install husky --save-dev

  2. 增加 prepare 命令:

  3. npm set-script prepare "husky install"

  4. 注意:npm set-script xxx在 npm7+生效。

  5. 初始化 husky:npm run prepare,生成.husky默认文件夹。

创建 Hook:

  1. 创建 Hook 命令格式:husky add <file> [cmd]

  2. 创建 Hook 命令,在 git commit 前执行 npm test:

  3. 修改 test 命令:输出hello world文本到output.txt文件。

  4. npx husky add .husky/pre-commit "npm test"

  5. 注意:如果执行后仅看到的是帮助提示,可以使用已安装到本地的 husky 来运行(.\node_modules\.bin\husky),这个问题已在 npm8 中得到了修复。

验证 hook:

  1. 添加暂存:git add .husky/pre-commit

  2. 执行提交:git commit -m "Keep calm and commit"


卸载 hook:

  1. 卸载依赖:npm uninstall husky

  2. 解除 Git 配置:git config --unset core.hooksPath

更多请参考下图:

commitizen/cz-cli 使用指南:

  1. 约定提交消息(AngularJS)



  1. 约定变更日志:conventional-changelog,同时会涉及standard-versionsemantic-release、workflow 等,非本次介绍;

  2. 前提约定:确保 npm 版本在 5.2 及以上。

初始化并使用生成 Commit 信息:

  1. 为项目初始化 cz-conventional-changelog:


// 如需强制执行要增加参数--forcenpx commitizen init cz-conventional-changelog --save-dev --save-exact
复制代码


  1. 添加执行脚本:

  2. npm set-script cm "cz"

  3. 不推荐使用 commit 做为 key,可能存在冲突导致重复执行,详见文档

  4. npm set-script执行需要 npm7+。

  5. 执行npm run cm后将启动交互来生成提交信息,提交前请将待提交文件添加到暂存区。


使用 husky7 打通 git commint 和 cz-cli :

  1. 创建./husky/prepare-commit-msg :

  2. npx husky set .husky/prepare-commit-msg 'exec < /dev/tty && git cz --hook || true'

  3. 触发git commit后将进入交互命令。


更多请参考下图:

commitlint 使用指南:

     当我们习惯使用统一的规范后就会觉得 CLI 的方式很慢,这时候我们就可以取消到原有的pre-commit中的拦截,改用只进行校验的方式来避免偶尔出现的不规范情况。

安装步骤:

  1. 安装必要依赖:


npm install --save-dev @commitlint/config-conventional @commitlint/cli
复制代码


  1. 生成配置文件:


// 注意:如果文件内容编码非UTF-8请修改,在windows中可使用记事本重新另存为UTF-8格式。echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
复制代码


  1. 创建一个提交信息校验的 hook:


npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
复制代码

验证是否可以触发:

验证 HEAD~1 到 HEAD 提交是否规范(源文档命令执行会发生错误):


npx commitlint -- --from HEAD~1 --to HEAD --verbose
复制代码


实际操作 GitCommit:

正确示范:



错误示范:


组件级模板开发:

     一些开源项目为了在被使用时可以轻松的上手或减少重复配置都会提供一份最基础的项目模板并配置相应的 CLI 工具进行拉取创建,在实际项目开发中我们也可以用类似的思想来帮助我们快速创建页面,组件,路由,控制器等。

准备环境:

  1. 安装开发依赖:npm i plop --save-dev

  2. 增加 Scripts:npm set-script new "plop";

  3. 推荐目录结构:


|-- project name    |-- plopfile.js    |-- plop-templates    |   |-- sfc3    |       |-- index.hbs    |       |-- prompt.js
复制代码


说明:我们在 plop-templates 定义的各个模板在导出后均要在 plopfile.js 集中注册生效。

第一个组件模板:

     Vue 的单文件组件将代码分割成 template、script、style 三块,其中 template、style 的编写风格相差不大。但是在 script 部分的编写 Vue3 就会有一些区别:


  1. 使用默认导出创建组件;

  2. 使用 setup 方言创建组件;

  3. 组件编写使用了 defineComponent;

  4. 组件编写使用了 reactive;

  5. 组件编写使用了 Ts;

  6. 。。。

prompt.js 脚本,负责收集用户的操作并汇总答案:

待收集的信息:
  1. Please enter a component name:

  2. Please check the required block:

  3. Please select a type:

  4. Set scoped ?

收集后处理:
const actions = {  // 本次需要增加一个模板  type: "add",  // 输出组件的目录  path: `src/components/${name}/index.vue`,  // 生成组件的模板定义  templateFile: "plop-templates/sfc3/index.hbs",  // 收集到得数据  data: {    name,    // 是否包含template    template: blocks.includes("template"),    // 是否包含style    style: blocks.includes("template"),    // 是否包含script    script: blocks.includes("script"),    default: type === "default",    setup: type === "setup",    reactive: type === "reactive",    scoped: scoped,  },}
复制代码

index.hbs 模板,负责构建组件的每一块:

     Handlebars是一个轻量的语义化模板,我们通过掌握一些简单的模板语法就可以正常使用,除非你要构建更加复杂的组件模板,那么你可以深入学习一下。

文本渲染:

使用{{ xxx }}的形式直接渲染。

条件判断:

条件判断助手代码块开始为{{#if xxx}},结束为{{/if}},当 template 为 true 时渲染中间的内容。


{{#if template}}<template>  <div>
</div></template>{{/if}}
复制代码
可以使用 Plopjs 内置的Case Modifiers,快速转换变量命名风格:
  • camelCase: changeFormatToThis

  • snakeCase: change_format_to_this

  • dashCase/kebabCase: change-format-to-this

  • dotCase: change.format.to.this

  • pathCase: change/format/to/this

  • properCase/pascalCase: ChangeFormatToThis

  • lowerCase: change format to this

  • sentenceCase: Change format to this,

  • constantCase: CHANGE_FORMAT_TO_THIS

  • titleCase: Change Format To This

示例模板:
{{#if template}}<template>  <div>
</div></template>{{/if}}
{{#if script}}<script>export default { setup () { return {} }}</script>{{/if}}
{{#if style}}<style{{#if scoped}} scoped{{/if}}>
</style>{{/if}}
复制代码

CSS 属性排序:

     在大多数时候我们都特别在乎 JavaScript 代码的规范,安装了各式各样的校验工具,同样在 CSS 里面也应该有一份类似的规范来对编写做一定的限制,这样可以避免重复定义的样式被覆盖的情况,也体现了在编写样式时的思路,同样也提高的 CSS 的阅读性和维护性。

CSScomb for VSCode:

CSScomb 是一款专注于 CSS、Less、SCSS 或 Sass 的编码样式格式化程序。


自定义配置:

  1. CSS 排序顺序:布局 → 尺寸 → 界面 → 文字 → 交互;

  2. 自定义配置文件:VSCode → 设置 → 工作区 → 右上角打开设置(json);



  1. 完整配置,建议在工作区使用,配置来自JowayYoung


// .vscode/settings.json{  "csscomb.formatOnSave": true, // 保存代码时自动格式化  "csscomb.preset": {    "always-semicolon": true, // 分号结尾    "block-indent": "\t", // 换行格式    "color-case": "lower", // 颜色格式    "color-shorthand": true, // 颜色缩写    "element-case": "lower", // 元素格式    // "eof-newline": false, // 结尾空行    "leading-zero": false, // 保留前导零位    // "lines-between-rulesets": 0, // 规则间隔行数    "quotes": "double", // 引号格式    "remove-empty-rulesets": true, // 剔除空规则    "space-between-declarations": "\n", // 属性换行    "space-before-closing-brace": "\n", // 后花括号前插入    "space-after-colon": " ", // 冒号后插入    "space-before-colon": "", // 冒号前插入    "space-after-combinator": " ", // 大于号后插入    "space-before-combinator": " ", // 大于号前插入    "space-after-opening-brace": "\n", // 前花括号后插入    "space-before-opening-brace": " ", // 前花括号前插入    "space-after-selector-delimiter": "\n", // 逗号后插入    "space-before-selector-delimiter": "", // 逗号前插入    "strip-spaces": true, // 剔除空格    "tab-size": true, // 缩进大小    "unitless-zero": true, // 剔除零单位    "vendor-prefix-align": false, // 前缀缩进    "sort-order": [      // 布局属性      "display", "visibility", "overflow", "overflow-x", "overflow-y",      // 布局属性:浮动      "float", "clear",      // 布局属性:定位      "position", "left", "right", "top", "bottom", "z-index",      // 布局属性:列表      "list-style", "list-style-type", "list-style-position", "list-style-image",      // 布局属性:表格      "table-layout", "border-collapse", "border-spacing", "caption-side", "empty-cells",      // 布局属性:弹性      "flex-flow", "flex-direction", "flex-wrap", "justify-content", "align-content", "align-items", "align-self", "flex", "flex-grow", "flex-shrink", "flex-basis", "order",      // 布局属性:多列      "columns", "column-width", "column-count", "column-gap", "column-rule", "column-rule-width", "column-rule-style", "column-rule-color", "column-span", "column-fill", "column-break-before", "column-break-after", "column-break-inside",      // 布局属性:格栅      "grid-columns", "grid-rows",      // 尺寸属性      "box-sizing","margin","margin-left","margin-right","margin-top","margin-bottom","padding","padding-left","padding-right","padding-top","padding-bottom","border","border-width","border-style","border-color","border-colors","border-left","border-left-width","border-left-style","border-left-color","border-left-colors","border-right","border-right-width","border-right-style","border-right-color","border-right-colors","border-top","border-top-width","border-top-style","border-top-color","border-top-colors","border-bottom","border-bottom-width","border-bottom-style","border-bottom-color","border-bottom-colors","border-radius","border-top-left-radius","border-top-right-radius","border-bottom-left-radius","border-bottom-right-radius","border-image","border-image-source","border-image-slice","border-image-width","border-image-outset","border-image-repeat","width","min-width","max-width","height","min-height","max-height",      // 界面属性      "appearance","outline","outline-width","outline-style","outline-color","outline-offset","outline-radius","outline-radius-topleft","outline-radius-topright","outline-radius-bottomleft","outline-radius-bottomright","background","background-color","background-image","background-repeat","background-repeat-x","background-repeat-y","background-position","background-position-x","background-position-y","background-size","background-origin","background-clip","background-attachment","bakground-composite","background-blend-mode","mask","mask-mode","mask-image","mask-repeat","mask-repeat-x","mask-repeat-y","mask-position","mask-position-x","mask-position-y","mask-size","mask-origin","mask-clip","mask-attachment","mask-composite","mask-box-image","mask-box-image-source","mask-box-image-width","mask-box-image-outset","mask-box-image-repeat","mask-box-image-slice","box-shadow","box-reflect","filter","mix-blend-mode","opacity","object-fit","clip","clip-path","resize","zoom","cursor","pointer-events","touch-callout","user-modify","user-focus","user-input","user-select","user-drag",      // 文字属性      "line-height","line-clamp","vertical-align","direction","unicode-bidi","writing-mode","ime-mode","text-overflow","text-decoration","text-decoration-line","text-decoration-style","text-decoration-color","text-decoration-skip","text-underline-position","text-align","text-align-last","text-justify","text-indent","text-stroke","text-stroke-width","text-stroke-color","text-shadow","text-transform","text-size-adjust","src","font","font-family","font-style","font-stretch","font-weight","font-variant", "font-size", "font-size-adjust", "color",      // 内容属性      "overflow-wrap", "word-wrap", "word-break", "word-spacing", "letter-spacing", "white-space", "caret-color", "tab-size", "content", "counter-increment", "counter-reset", "quotes", "page", "page-break-before", "page-break-after", "page-break-inside",      // 交互属性      "will-change", "perspective", "perspective-origin", "backface-visibility", "transform", "transform-origin", "transform-style", "transition", "transition-property", "transition-duration", "transition-timing-function", "transition-delay", "animation", "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-iteration-count", "animation-direction", "animation-play-state", "animation-fill-mode"    ] // 属性排序  }}
复制代码


  1. 增加快捷键优化:


// .vscode/keybindings.json[{  "key": "ctrl+alt+c", // "cmd+alt+c"  "command": "csscomb.execute"}]
复制代码



  1. 更多的配置选项可参考说明文档:csscomb options

写在最后

     通过这一篇的学习就不要再为环境不能平滑切换而放弃新功能的尝鲜了。统一的包管理器也不会再有参差不齐的问题出现。规范的 GitCommit 可以为阶段的总结生成日志,平时的查询也会更加方便。组件级别的模板编写可以在长期迭代的项目中节省时间,规范编码。同样也不能忽略 CSS 也是前端的一部分,良好的结构同样起到了便于阅读和维护的作用。当然在开发过程中还会有一些可能被忽略掉的痛点,下次整理后再发出来,也欢迎在评论区打出你发现的前端开发痛点还有哪些?✔

团队介绍

     高灯科技交易合规前端团队(GFE), 隶属于高灯科技(北京)交易合规业务事业线研发部,是一个富有激情、充满创造力、坚持技术驱动全面成长的团队, 团队平均年龄 27 岁,有在各自领域深耕多年的大牛, 也有刚刚毕业的小牛, 我们在工程化、编码质量、性能监控、微服务、交互体验等方向积极进行探索, 追求技术驱动产品落地的宗旨,打造完善的前端技术体系。


  • 愿景: 成为最值得信任、最有影响力的前端团队

  • 使命: 坚持客户体验第一, 为业务创造更多可能性

  • 文化: 勇于承担、深入业务、群策群力、简单开放


Github:github.com/gfe-team


团队邮箱:gfe@goldentec.com


作者:GFE(高灯科技交易合规前端团队)


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发布于: 刚刚阅读数: 3
用户头像

GFE

关注

一起成长、一起分享、一起创造价值 2020-04-09 加入

💻 努力打造这块领土,构建完整良好的知识体系 🏠 保持对技术的热爱,保持初心,保持丰富的想象力 🏷️ 标签 | 热爱技术 钻研探索 有责任心 前端领域 📥 邮箱 | gfe@goldentec.com

评论

发布
暂无评论
【解决】前端开发中的5大痛点_前端_GFE_InfoQ写作社区