手把手教你打造前端规范工程

手把手教你打造前端规范工程

前端代码风格因人而异,一个项目参与的人多了,不加强控制可能就是一个大杂烩,对开发人员来讲就是一个噩梦。

如何解决这种困境?

通过使用 ESLint +Prettier+ Husky + Lint-staged+Commitlint + Commitizen 这套方案,它能够在提升代码质量,保障团队协作效率,统一项目代码风格,强化代码审查流程,支持自动化运维 和促进项目管理等方面发挥重要作用,同时为项目的长期稳健发展打下坚实基础。

Part1

背景

为何要打造这样一套前端现代化规范工程?

1、代码风格一致性:

  • ESLint:作为静态代码检查工具,它定义了一套代码质量规则,用于检测潜在的错误、不符合最佳实践的模式以及不一致的编码风格。通过统一的 ESLint 配置,团队成员能够遵循一致的编程规范,增强代码的可读性和可维护性。

  • Prettier:作为代码格式化工具,Prettier 自动按照预设的样式规则对代码进行格式化,消除了因个人偏好引起的代码风格差异,确保了代码在空格、缩进、行长度、引号使用等方面的一致性。

2、自动化代码质量保障:

  • Husky:通过 Git 钩子机制,在关键的 Git 操作(如提交)前自动触发预定义的任务。在前端规范工程中,Husky 可以确保在代码提交前执行代码检查和格式化,防止不符合规范的代码进入版本库。

  • Lint-staged:配合 Husky 使用,仅针对暂存区中的改动文件运行指定的检查与格式化任务,既节省了资源,又确保只有符合规范的更改才会被提交。

3、规范化提交信息:

  • Commitlint:强制执行提交消息的格式约定(如 Conventional Commits),确保提交信息清晰、一致,便于通过提交历史快速理解代码变更的目的、类型和影响范围。这对于生成 changelog、自动化版本管理、识别影响范围(如构建语义化版本)等 DevOps 流程至关重要。

4、标准化提交流程:

  • commitizen:提供交互式的命令行工具,引导开发者按照预定义的提问模板编写符合 Commitlint 规范的提交消息。它简化了遵循提交规范的过程,尤其对新加入团队的成员或不熟悉特定提交格式的开发者非常友好。

Part2

代码书写规范

(一)代码检查工具:ESLint

认识:

ESLint 是一个开源的、强大的静态代码分析工具,主要用于检测 JavaScript 和相关编程语言(如 TypeScript、Vue、React、Angular 等)的代码中潜在的错误、不符合最佳实践的模式以及不一致的编码风格。

网址:

·官网地址:eslint.org/

·中文网地址:eslint.nodejs.cn/

·eslint-vue配置规则:eslint.vuejs.org/rules/

使用方法:

1、需要安装相关依赖如下:

  • eslint
  • eslint-plugin-import
  • eslint-plugin-prettier
  • eslint-config-prettier
  • eslint-plugin-vue
  • vue-eslint-parser
  • @typescript-eslint/parser
  • @typescript-eslint/eslint-plugin

**

**

2、安装方法:

**

**

java 复制代码
pnpm i -D eslint eslint-plugin-import eslint-plugin-prettier eslint-config-prettier eslint-plugin-vue vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin

3、配置,eslintrc.js

在根目录下新建 .eslintrc.js 文件, rules 里面的内容根据实际需求自行添加,编辑内容如下:

ruby 复制代码
module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es6: true
  },
  parser: 'vue-eslint-parser',
  extends: [
    'eslint:recommended', // 推荐使用eslint基础配置规则
    'plugin:vue/vue3-recommended', // 对于Vue3项目,识别vue3语法规则
    'plugin:@typescript-eslint/recommended', // 使用推荐的TypeScript 专用 lint 规则
    'prettier', // 引入eslint-config-prettier,应用 Prettier 的格式化规则,禁用冲突规则
    'plugin:prettier/recommended' // 引入eslint-plugin-prettier,推荐的 prettier 专用 lint 规则
  ],
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    jsxPragma: 'React',
    ecmaFeatures: {
      jsx: true
    }
  },
  /**
   * 自定义或覆盖特定规则 https://www.wenjiangs.com/docs/eslint,vue规则:https://eslint.vuejs.org/rules/
   * 主要有如下的设置规则,可以设置字符串也可以设置数字,两者效果一致
   * 'off' 或 0 - 关闭规则
   * 'warn' 或 1 - 开启警告规则,使用警告级别的错误:warn (不会导致程序退出),
   * 'error' 或 2 - 开启错误规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
   */
  rules: {
    'no-debugger': 'off',
    'no-case-declarations': 'off', // 防止在 switch 语句的 case 子句中声明变量
    'no-console': 'off',
    'no-undef': 'off',
    'no-useless-escape': 'off',
    // 'no-unused-vars': 'off', // 防止出现未使用的变量、函数参数或常量
    'no-restricted-imports': 'off',
    'no-use-before-define': 'off',
    'no-sparse-arrays': 'off',
    'compat/compat': 'off',
    'no-constant-condition': 'off',
    'prefer-const': ['warn', { destructuring: 'all', ignoreReadBeforeAssign: true }],
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    '@typescript-eslint/no-use-before-define': 'off',
    '@typescript-eslint/ban-ts-comment': 'off',
    '@typescript-eslint/ban-types': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
     // 检查未使用的变量、函数参数、类属性
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^_',
        varsIgnorePattern: '^_',
      },
    ],
    '@typescript-eslint/no-redeclare': 'off',
    '@typescript-eslint/no-this-alias': 'off',
    'vue/no-setup-props-destructure': 'off',
    'vue/script-setup-uses-vars': 'error',
    'vue/no-reserved-component-names': 'off',
    'vue/custom-event-name-casing': 'off',
    'vue/attributes-order': 'off',
    'vue/one-component-per-file': 'off',
    'vue/html-closing-bracket-newline': 'off',
    'vue/max-attributes-per-line': 'off',
    'vue/multiline-html-element-content-newline': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'vue/attribute-hyphenation': 'off',
    'vue/require-default-prop': 'off',
    'vue/require-explicit-emits': 'off',
    'vue/require-toggle-inside-transition': 'off',
    'vue/html-self-closing': 'off',
    'vue/multi-word-component-names': 'off',
    'vue/no-v-html': 'off',
    'vue/no-dupe-keys': 'off',
    'vue/no-deprecated-v-on-native-modifier': 'off',
    'vue/html-indent': 'off',
    'vue/no-template-shadow': 'off',
    "prettier/prettier":'off'
  }
};

4、配置.eslintignore

在根目录下新建 .eslintignore 文件, 用来让 eslint 不用检查这些文件

lua 复制代码
// .eslintignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
public

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.husky

src/assets/
plop-templates/

package.json
README.md

(二)代码格式化工具: prettier

认识:

Prettier 是一个自动化的代码格式化器,支持多种编程语言,包括 JavaScript、TypeScript、HTML、CSS、JSON、Markdown 等。它的目标是提供一种统一、可配置但非协商式的代码风格,旨在消除团队成员之间因代码风格偏好产生的分歧,提高代码可读性和维护性。

网址:

·官方网址:prettier.io/

·prettier中文网:www.prettier.cn/

使用方法

1、安装:

css 复制代码
pnpm i -D prettier

2、配置.prettierrc.js

在根目录下新建 .prettierrc.js 文件, 里面的内容根据实际需求自行添加,编辑内容如下:

java 复制代码
module.exports = {
    // 每行最多字符数量,超出换行(默认80)
    printWidth: 80,
    // 超出打印宽度 (always | never | preserve )
    proseWrap: "preserve",
    // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
    arrowParens: "always",
    // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
    bracketSameLine: false,
    // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
    bracketSpacing: true,
    // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
    embeddedLanguageFormatting: "auto",
    // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
    htmlWhitespaceSensitivity: "css",
    // 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
    insertPragma: false,
    // 在 JSX 中使用单引号替代双引号,默认false
    jsxSingleQuote: false,
    // 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
    quoteProps: "as-needed",
    // 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
    requirePragma: false,
    // 结尾添加分号
    semi: true,
    // 使用单引号 (true:单引号;false:双引号)
    singleQuote: true,
    // 缩进空格数,默认2个空格
    tabWidth: 2,
    // 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
    trailingComma: "es5",
    // 指定缩进方式,空格或tab,默认false,即使用空格
    useTabs: false,
    // vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
    vueIndentScriptAndStyle: false,
    // 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
    endOfLine: "auto",
  };

3、配置.prettierignore

在根目录下新建 .prettierignore 文件, 用来让 prettier 不用格式化这些文件

bash 复制代码
/dist/*
/node_modules/**
/public/*
src/assets/

.prettierrc.js
.eslintrc.js
commitlint.config.js
README.md
package.json
vite.config.ts

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.husky/*

(三) Vscode插件 和设置:

Vscode插件:

在vscode插件栏,搜索并安装如下插件;

1、eslint:提供eslint的校验,保存自动修复功能

2、 prettier:提供prettier 保存自动格式化的功能

扩展:

项目自身配置eslint、prettier插件与vscode安装插件关系:

相辅相成,共同作用于项目格式化校验和修复;项目自身配置eslint、prettier插件的格式化规范,其优先级要高于vscode安装的eslint、prettier插件;

Vscode设置:

在项目根目录下新建 .vscode/setting.json 文件,写入以下内容,即可在保存代码的时候自动按照 eslint 和 prettier 的规范进行代码格式化

json 复制代码
{
  // 设置npm包管理器为pnpm
  "npm.packageManager": "pnpm",
  // 设置编辑器的Tab大小为2个空格
  "editor.tabSize": 2,
  // 配置编辑器在每次保存时自动格式化代码
  "editor.formatOnSave": true,
  // 设置默认的代码格式化工具为prettier
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  // 配置编辑器在保存时执行的代码动作,包括应用所有明确的修复
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit",
    "source.fixAll.eslint": "explicit",
    "source.fixAll.stylelint": "explicit"
  },
  // 自定义拼写检查词典,包含一些特定的技术词汇
  "cSpell.words": [
    "lint-staged",
    "lintstagedrc",
    "persistedstate",
    "pinia",
    "pnpm",
    "stylelint",
  ],
}

Part3

代码提交规范

前提:

需要安装如下依赖:

  • husky

  • lint-staged

  • commitizen

  • cz-git

  • @commitlint/cli

  • @commitlint/config-conventional

(一)husky

认识:

Husky 是一个 Git 钩子管理工具,允许您在 Git 的各个生命周期事件(如 pre-commit、pre-push、post-commit 等)中轻松添加自定义脚本。通过 Husky,您可以确保在提交代码之前执行必要的代码检查(如 ESLint、Prettier、单元测试等),防止不符合规范或存在错误的代码进入版本库。这有助于提高代码质量和维护项目的整洁性。

网址: typicode.github.io/husky/

使用方法:

1、配置husky

执行下面两行代码

arduino 复制代码
npm pkg set scripts.prepare="husky install" // 在 package.json 中添加脚本
npm run prepare // 初始化 husky,将 git hooks 钩子交由 husky 执行

执行完这两行代码以后,发生了两件事情:

第一个是 package.json 中新增了一个脚本

json 复制代码
"scripts": {
    "prepare": "husky install"
  },

第二个是根目下新增了 .husky 文件夹;

2、配置pre-commit

2.1、新建pre-commit 文件并添加内容

在.husky文件夹下新建pre-commit 文件,然后添加以下内容:

bash 复制代码
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

pnpm exec lint-staged

作用:是在执行git commit时,先加载husky的内部脚本,然后使用pnpm执行lint-staged命令,对暂存区中的代码进行代码风格检查和格式化,确保符合项目规范后再提交。

2.2、配置lint-staged

在package.json中,添加如下配置,表示通过eslint来自动修复各类文件格式;

json 复制代码
"lint-staged": {
    "*.{vue,js,ts,jsx,tsx,md,json}": "eslint --fix"
  }

扩展:

Lint-Staged:是一个在 Git暂存区(staging area)中运行 linters 的工具。当您准备提交代码时,Lint-Staged 仅针对已暂存的文件运行指定的 linting 工具(如 ESLint、Prettier、stylelint 等)。这样可以确保只有即将被提交的改动经过了严格的质量检查,避免了对整个项目进行 linting 所带来的性能开销,同时也确保每次提交都是干净且符合规范的。

配置只校验暂存区文件:

在package.json中,添加如下命令,表示只校验暂存区代码格式;

json 复制代码
"scripts": {
    "lint:lint-staged": "lint-staged"
}

3、 效果展示

到这里为止,代码提交时,代码自动检测功能已经实现了,来测试一下效果。

结论:通过配置husky和eslint,提交代码的时候会自动进行格式化,如果有格式化解决不了的错误,就会报错。

(二)commitlint

认识:

Commitlint 是一个用于验证 Git 提交消息格式的工具,基于社区广泛接受的 Conventional Commits 规范。它确保提交消息遵循一定的结构和约定,如明确的类型(fix、feat、docs、chore 等)、简洁的描述、可选的正文和 footer 等。通过规范化提交消息,Commitlint 帮助生成清晰、一致的 changelog,便于追踪项目变更历史,辅助自动化版本发布流程(如通过 semantic-release)。

网址: commitlint.js.org/

使用方法:

1、配置commit-msg

新建commit-msg文件并添加内容

在.husky文件夹下新建commit-msg 文件,然后添加以下内容:

bash 复制代码
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

pnpm exec commitlint --edit "${1}"

作用: 使用 pnpm 执行 commitlint 命令,根据 commitlint.config.js 中的配置检查提交消息格式是否符合项目规范。如果提交消息不符合规范,commitlint 尝试自动修正消息内容,以便用户在继续提交时获得符合规范的消息。

2、配置commitlint.config.js

在项目跟目录下新建commitlint.config.js文件,并添加如下配置:

css 复制代码
// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */

module.exports = {
  ignores: [commit => commit.includes("init")],
  extends: ["@commitlint/config-conventional"],
  rules: {
    // @see: https://commitlint.js.org/#/reference-rules
    "body-leading-blank": [2, "always"],
    "footer-leading-blank": [1, "always"],
    "header-max-length": [2, "always", 108],
    "subject-empty": [2, "never"],
    "type-empty": [2, "never"],
    "subject-case": [0],
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "docs",
        "style",
        "refactor",
        "perf",
        "test",
        "build",
        "ci",
        "chore",
        "revert",
        "wip",
        "workflow",
        "types",
        "release"
      ]
    ]
  },
  prompt: {
    messages: {
      type: "Select the type of change that you're committing:",
      scope: "Denote the SCOPE of this change (optional):",
      customScope: "Denote the SCOPE of this change:",
      subject: "Write a SHORT, IMPERATIVE tense description of the change:\n",
      body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
      breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
      footerPrefixsSelect: "Select the ISSUES type of changeList by this change (optional):",
      customFooterPrefixs: "Input ISSUES prefix:",
      footer: "List any ISSUES by this change. E.g.: #31, #34:\n",
      confirmCommit: "Are you sure you want to proceed with the commit above?"
      // 中文版
      // type: "选择你要提交的类型 :",
      // scope: "选择一个提交范围(可选):",
      // customScope: "请输入自定义的提交范围 :",
      // subject: "填写简短精炼的变更描述 :\n",
      // body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
      // breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
      // footerPrefixsSelect: "选择关联issue前缀(可选):",
      // customFooterPrefixs: "输入自定义issue前缀 :",
      // footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
      // confirmCommit: "是否提交或修改commit ?"
    },
    types: [
      {
        value: "feat",
        name: "feat: 🚀 A new feature",
        emoji: "🚀"
      },
      {
        value: "fix",
        name: "fix: 🧩 A bug fix",
        emoji: "🧩"
      },
      {
        value: "docs",
        name: "docs: 📚 Documentation only changes",
        emoji: "📚"
      },
      {
        value: "style",
        name: "style: 🎨 Changes that do not affect the meaning of the code",
        emoji: "🎨"
      },
      {
        value: "refactor",
        name: "refactor: ♻️ A code change that neither fixes a bug nor adds a feature",
        emoji: "♻️"
      },
      {
        value: "perf",
        name: "perf: ⚡️ A code change that improves performance",
        emoji: "⚡️"
      },
      {
        value: "test",
        name: "test: ✅ Adding missing tests or correcting existing tests",
        emoji: "✅"
      },
      {
        value: "build",
        name: "build: 📦️ Changes that affect the build system or external dependencies",
        emoji: "📦️"
      },
      {
        value: "ci",
        name: "ci: 🎡 Changes to our CI configuration files and scripts",
        emoji: "🎡"
      },
      {
        value: "chore",
        name: "chore: 🔨 Other changes that don't modify src or test files",
        emoji: "🔨"
      },
      {
        value: "revert",
        name: "revert: ⏪️ Reverts a previous commit",
        emoji: "⏪️"
      }
      // 中文版
      // { value: "特性", name: "特性: 🚀 新增功能", emoji: "🚀" },
      // { value: "修复", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
      // { value: "文档", name: "文档: 📚 文档变更", emoji: "📚" },
      // { value: "格式", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
      // { value: "重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
      // { value: "性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
      // { value: "测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
      // { value: "构建", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
      // { value: "集成", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
      // { value: "回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
      // { value: "其他", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" }
    ],
  }
};

作用: commitlint.config.js 是 commitlint 工具的核心配置文件,它用于定义项目中 Git 提交消息(commit messages)应遵循的格式规则和规范。

3、配置commit提交命令

在package.json中,添加如下配置,用于自动提交全部文件并执行 git-cz

json 复制代码
// package.json
"scripts": {
    "commit": "git pull && git add -A && git-cz && git push",
},
"config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
}

扩展:

Commitizen:是一个辅助创建符合 Conventional Commits 规范的 Git 提交消息的命令行工具。它提供了一个交互式的提交流程,引导开发者按照规定的格式输入提交信息,包括类型、scope(可选)、描述、正文(可选)和 footer(可选)。

cz-git: 这个包名表明它可能是一个与Git结合使用的Commitizen插件或适配器。一般来说,Commitizen通过cz命令行工具与用户交互,引导他们按照预定义的格式填写提交信息。

4、效果展示:

到这里为止,所有的配置都完成了,来测试一下效果;

使用pnpm run commit来执行代码默认自动提交;

代码提交完成且没有报错提示。

Part4

总结

1、通过使用 ESLint +Prettier+ Husky + Lint-staged+Commitlint + Commitizen这套方案,它能够在提升代码质量,保障团队协作效率,统一项目代码风格,强化代码审查流程,支持自动化运维 和促进项目管理等方面发挥重要作用,同时为项目的长期稳健发展打下坚实基础。

2、需要安装的依赖包及配置如下:

perl 复制代码
{
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build:dev": "vite build --mode development",
    "build:test": "vite build --mode test",
    "build:prod": "vite build --mode production",
    "lint:lint-staged": "lint-staged",
    "commit": "git pull && git add -A && git-cz && git push",
    "prepare": "husky install"
  },
  "dependencies": {
    "vue": "^3.4.23",
  },
  "devDependencies": {
    "@commitlint/cli": "^17.3.0",
    "@commitlint/config-conventional": "^17.8.1",
    "@typescript-eslint/eslint-plugin": "^5.32.0",
    "@typescript-eslint/parser": "^5.32.0",
    "commitizen": "^4.3.0",
    "cz-git": "^1.3.12",
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-prettier": "^5.1.3",
    "eslint-plugin-vue": "^9.25.0",
    "husky": "^9.0.11",
    "lint-staged": "^15.2.2",
    "prettier": "^3.2.5",
    "typescript": "^4.6.4",
    "vue-eslint-parser": "^9.4.2",
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  },
  "lint-staged": {
    "*.{vue,js,ts,jsx,tsx,md,json}": "eslint --fix"
  }
}

相关推荐
周胡杰1 分钟前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
不念霉运4 分钟前
Gitee DevOps:中国企业数字化转型的“本土化加速器“
运维·gitee·团队开发·代码规范·devops·代码复审
敲代码的小吉米15 分钟前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊17 分钟前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js
九月TTS1 小时前
TTS-Web-Vue系列:组件逻辑分离与模块化重构
前端·vue.js·重构
我是大头鸟1 小时前
SpringMVC 内容协商处理
前端
Humbunklung1 小时前
Visual Studio 2022 中添加“高级保存选项”及解决编码问题
前端·c++·webview·visual studio
墨水白云2 小时前
nestjs[一文学懂nestjs中对npm功能包的封装,ioredis封装示例]
前端·npm·node.js
满怀10152 小时前
【Vue 3全栈实战】从响应式原理到企业级架构设计
前端·javascript·vue.js·vue
luckywuxn2 小时前
使用gitbook 工具编写接口文档或博客
前端