前言

MFEGA——Make Front End Great Again

写这篇文章主要目的是总结一套完整的、可复用的前端架构,实现拿来即用、拿来即开发。

2024.10.25 - ~

前置条件

开发工具:VS CodeGit

开发框架:Node.jsPNPMVue3ViteJS

项目构建

包管理器选择

参考:Npm / Yarn / Pnpm 前端包管理工具对比

**运行速度对比:**pnpm > yarn > npm

Npm

Node.js官方的包管理工具。

依赖管理方式:

  1. npm v1 npm v2 版本中,依赖包的管理是树结构嵌套组成的

    node_modules
    └─ foo
       ├─ index.js
       ├─ package.json
       └─ node_modules
          └─ bar
             ├─ index.js
             └─ package.json
    
  2. 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创建项目

参考:超详细pnpm monorepo教程

pnpm-workspace环境搭建

如何发布一个monorepo的npm包

  1. 初始化Monorepo框架

    # 全局安装pnpm
    npm i -g pnpm
    # 初始化package.json
    mkdir VEXIOS && cd VEXIOS
    pnpm init
    
  2. 添加 pnpm-workspace.yaml文件:

    参考:pnpm中文网

    添加内容:

    packages:
      - packages/*     # 包含所有 packages 目录下的子包
      - apps/*         # 包含所有 apps 目录下的应用
      - libs/*         # 包含所有 libs 目录下的库
    

    接着我们创建这两个文件夹:

  3. apps文件夹中创建项目:

    # 创建测试项目
    cd apps
    pnpm create vite test-app
    

    创建过程中,会让你选择使用什么框架,你可以根据自己需要选择:

    接着,我们可以在 apps文件夹中找到 test-app项目:

  4. 运行项目:

    我们使用 pnpm i来安装项目所需依赖,如果需要单独安装指定项目的依赖,请使用pnpm --filter

    成功安装依赖后,我们就可以运行项目了:

如何在monorepo中使用公共组件

在我使用的目录中,libs是用来存放工具、服务等,代码通常不包含业务逻辑,是为了提高代码复用性;packages是可以单独发布的模块或包,通常它们的功能较为独立,能被单独使用。

  1. 创建 utils工具文件夹:

    cd libs
    mkdir utils && cd utils
    pnpm init
    

    utils文件夹添加以下文件:

  2. 导出工具:

    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字段:最好每次修改后给它+1
    • main字段:控制出口文件,必须要有相对应文件才能正常导出
  3. 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中查看:

  4. 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

参考:ESlint9+Prettier从0开始配置教程

ESLint 9.0配置一览

[

平面配置(flat config)推出计划

](https://eslint.org/blog/2023/10/flat-config-rollout-plans/)

迁移到ESLint v9.x

ESLint的下一步是什么

浅析ESLint v9版本的新特性

  • 默认情况下,ESLint CLI 将搜索 eslint.config.js ,而不是 .eslintrc.* 文件。
  • 弃用核心格式化规则。
  • ESLint 现在正式支持 JSON 和 Markdown 的 linting。
  • ESLint 配置检查器,即配置可视化工具——可帮助理解和检查 ESLint 平面配置(flat config)文件。

配置eslint.config.js

参考:JavaScript 规则(rules)一览

  1. 如果你还没安装eslint v9.x,请使用下述命令安装或升级:

    # -w是指在工作区(workspace)根目录安装、一般使用monorepo的情况下,只需在根目录安装一次eslint,在具体项目中引用即可
    pnpm add -D eslint@9.15.0 -w
    

    安装成功后,你可以在根目录的 package.json中看到新增依赖:

  2. 在根目录新建一个 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模块。

  3. 接着你可以在项目的任意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扩展配置介绍

  1. 在VS Code的扩展中搜索 ESLint,并点击安装,然后重启VS Code:

  2. 配置需要校验的文件:

    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就可以自动格式化代码,不需要额外执行。

参考:一文搞懂eslint、prettier以及vscode插件

  1. 在项目中安装 prettier依赖:

    pnpm add -D prettier -w
    
  2. 接着在任意文件中书写不规范代码,例如:

    <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 .
    

    这时你会发现,文件似乎没有什么变化,而是发生了闪烁(如果报错说明出现了问题)。这是因为 prettiereslint发生了冲突,我们可以使用 eslint-plugin-prettier依赖来解决,这在后文中会说明。

  3. 安装VS Code扩展 prettier

    在搜索栏搜索Prettier,出现如下图的扩展安装即可。

  4. 接着我们对 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"
      }
    }
    

    需要注意的是:

    1. 需要在设置界面搜索“格式化”,以开启保存时的格式化功能。

    1. 如果使用 prettier.config.js配置,则需要重启VS Code才能生效,或者使用 .prettierrc来即时生效
  5. 解决 eslintprettier冲突问题,安装 eslint-plugin-prettiereslint-config-prettier

    eslint-plugin-prettier:这是一个 ESLint 插件,它会使用 Prettier 来格式化代码,并将格式化结果作为 ESLint 的一项规则来检查代码可以在代码检查的同时,自动格式化代码,使其符合 Prettier 的规则。
    eslint-config-prettier:这是一个 ESLint 配置规则的包,它将禁用与 Prettier 冲突的 ESLint 规则。可以确保 ESLint 规则与 Prettier 的代码格式化规则保持一致,避免二者之间的冲突。

    参考:eslint-config-prettier和eslint-plugin-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来统一编辑器配置,具体操作如下:

    1. 安装VS Code拓展:EditorConfig for VS Code

    2. 添加文件 .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和Common JS的区别

聊聊什么是CommonJs和Es Module

ES Module CommonJS
导出方式 使用 importexport 关键字 使用 requiremodule.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 CHANGEcommit 可以将主版本号 +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方式:

  1. (推荐)采用 engine-strict模式,在 engines中配置 "npm": ">=999.0.0",来达到限制使用npm的目的。经过测试,发现其从根源上解决了只使用pnpm问题,但代码实现略不优雅。
  2. 不使用 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-pxtorempnpm add -D postcss-pxtorem。用于将CSS的像素单位 px转化为 rem的依赖包,达到自适应网页效果。

(未采用)
autoprefixerpnpm add -D autoprefixer。(生产环境生效)后置处理器,在代码打包生成后,为适配不同浏览器,将css样式添加前缀。(Vite默认包括)
cssnanopnpm add -D cssnano。(生产环境生效)用于压缩和优化 CSS,减少 CSS 文件体积。
windicss: pnpm add -D vite-plugin-windicss windicss。相比 TailwindCSS,使用 WindiCSS可能更符合该项目,因为它是按需使用,速度更快。(unocss) 原子化CSS
purgecsspnpm add -D @fullhuman/postcss-purgecsspurgecss用来删除未使用的 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提交规范。

参考:Angular提交信息规范

Git Commit Angular规范-掘金

约定式提交

基本结构

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 回退版本时

scopesubject

  • 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的执行方式,让我们快捷的执行相应钩子。

参考:如何用 Husky 向 Git 添加 Commit Hooks

Git Hooks

参考:Git高级操作: Git钩子

Git钩子详细介绍

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使用方法

  1. 安装 husky依赖:

    pnpm add -D husky -w
    
  2. 初始化 husky

    pnpx husky init
    

    此时会生成对应文件夹和命令脚本:

  3. 之后我们可以通过在 package.json中创建脚本,然后在 .husky中新建相应的 执行时间文件(如pre-commit) 即可自动挂载。

Lint-Staged

lint-staged是可以在Git暂存区文件上,运行指定lint的工具。
它运行的逻辑是:通过在 package.json中的配置,对指定文件执行相应命令。结合 husky后,就成为了在执行 pre-commit钩子时,执行 lint-staged的相应命令。

  1. 安装 lint-staged

    pnpm add -D lint-staged -w
    
  2. 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"
    	}
    }
    
  3. 然后将 .husky/pre-commit文件中内容修改为:

    pnpm run lint:lint-staged
    

  4. 接着我们可以测试一下提交:

    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 formatAngular提交信息规范)。

参考:commitlint官网

  1. 安装 commitlint

    pnpm add -D @commitlint/cli  @commitlint/config-conventional -w
    
  2. 创建 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", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
    			],
    		],
    	},
    };
    
  3. 接着在 .husky文件夹中新建文件 commit-msg,并添加配置:

    pnpm dlx commitlint --edit \
    

    我们提交一个不规范的内容,测试一下:

    此时,可以看到提交被拦截了。我们这时提交规范内容,测试:

    提交成功,我们规定的提交格式是:<type>(<scope>): <subject>,其中 typesubject是必填的。

Commitizen

基于 NodeJSgit commit的命令行工具,辅助生成标准化规范化的commit。

参考:Commitizen——标准化的Git commit工具

前端工程化配置(下) 规范仓库提交记录

Commitizen分为两部分:脚手架(cz-cli)规范/适配器(Adapters)

官方提供的默认 规范cz-conventional-changelog脚手架就是Commitizen

  1. 安装 commitizen

    **先全局安装:**为了快捷使用 git cz命令

    pnpm i -g commitizen
    

    再局部安装:

    pnpm add -D commitizen cz-git -w
    
这里的

cz-git就是适配器,更多的适配器请查看Adapters

  1. 接着我们在 package.json中添加一下内容,来指定路径:

    {
      "config": {
        "commitizen": {
          "path": "node_modules/cz-git"
        }
      }
    }
    

commitlint配置prompt

在已经配置好 commitlint.config.js后,即可使用 git cz

这是 commitlint.config.js的配置:

参考:commitlint配置模板

需要注意的是:rulessubject-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 HooksLint-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来解决

灵感来源:为任何JSON文件添加注释的VSCODE插件
插件官网:json_comments_extension

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进行封装。

参考:nonhana对axios的二次封装

Axios 如何取消重复请求

结合Typescript和Axios源码,设计一套实用的API层架构

如何优雅地封装 axios

一篇拒绝低级封装axios的文章

前端架构带你 封装axios,一次封装终身受益

展望:请求响应拦截(请求中断)、超时、地址(反向代理)

未完待续...

文章作者: Romcere.
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Romcere.
让前端再次伟大 Node.js JacaScript Vue 前端
喜欢就支持一下吧