前端代码质量为何如此重要
我这里收集了两篇很有趣的文章可以从反面论证这个问题如何把前端项目写成一座屎山?、能把队友气死的8种屎山代码(React版), 可以概述为以下问题:
问题 | 特征 | 示例 |
---|---|---|
组件膨胀 | 组件变得非常庞大,包含大量的逻辑和功能,违反了单一职责原则。 | 一个组件既包含UI 渲染逻辑,又包含数据处理、网络请求等功能。 |
不合理的状态管理 | 状态管理不合理,数据流混乱,难以理解和调试。 | 全局滥用props 传递或未合理使用状态管理工具(如Redux 或Vuex )。 |
未优化的渲染 | 组件频繁重新渲染,性能下降。 | 在render 方法中执行昂贵的计算或使用不必要的forceUpdate 。 |
过于复杂的条件渲染 | 复杂的条件渲染逻辑,难以理解。 | 使用大量的嵌套if 语句或三元表达式来根据不同条件渲染内容。 |
缺乏组件复用性 | 组件过于专用,难以在不同部分重用。 | 在多个地方复制粘贴相似的代码而不提取为可重用的组件。 |
未优化的数据传递 | 不合理的数据传递方式,导致不必要的props 传递和组件更新。 |
将大量的数据通过props 传递给子组件,即使子组件只需要其中一部分数据。 |
忽视性能优化 | 未考虑性能优化,导致页面加载缓慢或响应时间长。 | 没有使用懒加载、缓存数据或合理使用虚拟列表等性能优化技术。 |
不合理的路由管理 | 路由管理混乱,没有明确定义的路由结构。 | 未使用路由懒加载,导致首次加载时加载所有路由组件。 |
硬编码的数据和字符串 | 数据和字符串硬编码在组件中,难以维护和修改。 | 直接在组件中写死API 端点或文本内容。 |
不合理的错误处理 | 错误处理不完善,可能导致未处理的异常和应用崩溃。 | 未使用try...catch 块捕获异常,或者忽略了异步操作的错误。 |
滥用全局状态 | 过度使用全局状态,导致数据管理复杂化。 | 将所有状态都存储在全局状态管理中,而不是在局部组件内部。 |
无序的文件和目录结构 | 项目缺乏良好的文件和目录组织,难以找到所需的代码文件。 | 未将组件、工具函数和样式表分门别类存放。 |
不合理的依赖管理 | 依赖项过多或不合理,导致项目庞大和加载时间延长。 | 引入大量不必要的第三方库或组件。 |
ESLint
、Prettier
和TypeScript
的作用
ESLint
、Prettier
和TypeScript
都是前端开发中常用的工具和技术,它们各自具有不同的作用,可以帮助提高代码质量、可维护性和可读性。
-
ESLint
:- 作用 :
ESLint
是一款静态代码分析工具,用于检查和识别JavaScript代码中的潜在问题和不良实践。它允许开发者定义一组规则,以强制代码风格、可维护性和性能最佳实践。 - 用途 :
ESLint
可以用来检查代码中的语法错误、代码风格问题、不推荐使用的特性、未使用的变量等,以确保代码质量一致并减少潜在的错误。
- 作用 :
-
Prettier
:- 作用 :
Prettier
是一个代码格式化工具,它能够自动格式化代码,以确保代码在整个项目中具有一致的风格和格式。 - 用途 :
Prettier
可以格式化代码的缩进、行宽、空格、括号等方面,减少了代码审查过程中关于格式的争议,同时提高了代码的可读性。
- 作用 :
-
TypeScript
:- 作用 :
TypeScript
是一种静态类型的超集,它为JavaScript
添加了类型系统。开发者可以定义变量、函数和对象的类型,并在编译时进行类型检查。 - 用途 :
TypeScript
可以帮助开发者在编码过程中捕获类型错误,提高代码的健壮性和可维护性。它还提供了更强大的IDE
支持,使代码更易于理解和维护。
- 作用 :
ESLint
安装与配置
假设你已经阅读了前面的文章安装好了node
,只需执行下面命令即可安装eslint
bash
# npm
npm install eslint --save-dev
# yarn
yarn add eslint -D
初始化配置.eslintrc.js
文件
bash
npx eslint --init
接下来是一些规则配置的引导,可以按照自己的代码风格进行填写:
当当,一份基础的病历,哦不,配置项就生成好了。
通常来说,这样一份配置已经基本能满足日常使用的要求了,但是,本着不折腾折腾要死的精神接下来细细评味每个配置项的作用以及如何选择适合你的规则集。
配置文件概览
json
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended"
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"react"
],
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}
运行时(env
)
项目的运行环境,比如在浏览器,node
,使用es6
、es5
等等。
设定运行环境的好处,这样能让eslint
理解当前处于哪个运行时,从而减少对运行时全局变量误报。如浏览器环境中的window
变量和node
环境的global
等
解析器
parser
和 parserOptions
是 ESLint
配置中与代码解析相关的两个重要配置项。它们用于定义如何解析和理解你的 JavaScript
代码。以下是对这两个配置项的详细介绍:
parser
配置项
parser
配置项用于指定用于解析 JavaScript
代码的解析器。解析器负责将代码转换成抽象语法树(AST
),以便 ESLint
可以在 AST
上进行规则检查。
常见的解析器选项包括:
"espree"
:这是ESLint
默认使用的解析器,它支持ECMAScript 5.1
语法。"babel-eslint"
:用于支持最新的ECMAScript
版本和Babel
扩展的解析器。"@typescript-eslint/parser"
:用于解析TypeScript
代码。"vue-eslint-parser"
:用于解析Vue.js
单文件组件中的代码。
parserOptions
配置项
parserOptions
配置项是parser
的补充,它允许你根据自身项目特点进行更深入解析定制。
常见的 parserOptions
选项包括:
ecmaVersion
:指定要支持的ECMAScript
版本,如 5、6、2015、2017 等。sourceType
:指定模块系统类型,可以是"script"
(默认)或"module"
。ecmaFeatures
:一个对象,用于启用或禁用特定的语言特性,如jsx
(用于支持React JSX
语法)。
全局配置
global
globals
选项用于定义在代码中全局范围内可访问的变量,而不会引发"未定义的变量"错误。这对于那些在整个项目中都可用的全局变量非常有用,比如浏览器环境中的window
对象或Node.js
环境中的process
对象。
例如,如果你的项目中使用了全局变量myGlobalVar
,你可以将其添加到ESLint
配置文件中:
js
// .eslintrc.js
module.exports = {
// 其他配置选项...
globals: {
myGlobalVar: 'readonly', // 可以使用myGlobalVar变量,但不允许重新赋值
},
};
定制化配置
settings
settings
选项用于传递一些特定于项目的配置信息给ESLint
。这可以包括自定义规则的配置、环境配置、插件配置等。
例如,如果你在项目中使用了React,并且想要启用与React相关的ESLint
规则,可以在settings
中进行配置:
js
// .eslintrc.js
module.exports = {
// 其他配置选项...
settings: {
react: {
version: 'detect', // 自动检测React版本
},
},
};
overrides
overrides
选项允许你为特定的文件、目录或通配符模式定义不同的 ESLint
配置。这在处理不同类型的文件或特定规则集时非常有用。例如,你可以针对测试文件使用不同的规则,或者针对不同的目录使用不同的配置。
下面是一个示例,演示如何在 ESLint
配置中使用 overrides
:
js
// .eslintrc.js
module.exports = {
// 通用配置
rules: {
'no-console': 'warn',
},
overrides: [
{
files: ['**/*.test.js'], // 匹配所有测试文件
rules: {
'no-console': 'off', // 关闭 no-console 规则
},
},
{
files: ['src/**/*.js'], // 匹配 src 目录下的所有 JavaScript 文件
rules: {
'custom-rule': 'error', // 启用自定义规则
},
},
],
};
ignorePatterns
ignorePatterns
选项允许你指定哪些文件或目录应该被 ESLint
忽略,不进行代码检查。这通常用于排除不需要进行代码质量检查的文件,如第三方库、构建产物、测试数据等。
下面是一个示例,演示如何在 ESLint
配置中使用 ignorePatterns
:
js
// .eslintrc.js
module.exports = {
// 通用配置
rules: {
'no-console': 'warn',
},
ignorePatterns: ['node_modules/', 'build/'], // 忽略 node_modules 目录和 build 目录下的文件
};
偷学别人的配置(继承)
extends
extends
选项用于引入外部的 ESLint
配置文件,这些配置文件定义了一组规则,用于检查代码的质量和风格。通常,这些配置文件可以是 ESLint
内置的,也可以是社区维护的或自定义的。
常见的用法是引入预定义的配置文件,如 "eslint:recommended"
或 "plugin:react/recommended"
,以便快速启用一组常见的规则。你还可以创建自己的配置文件,并在项目中重复使用。
js
// .eslintrc.js
module.exports = {
extends: 'eslint:recommended', // 使用 ESLint 推荐的规则集
// 其他配置选项...
};
plugins
plugins
选项通常和extends
一起使用,用于引入 ESLint
插件,这些插件包含了额外的规则和功能,用于检查特定的代码模式或框架。ESLint
插件通常由社区维护,并提供了与特定技术栈或项目需求相关的规则。
例如,如果你正在使用 React,你可以使用 eslint-plugin-react
插件来启用与 React 代码相关的规则。首先,需要安装该插件:
js
// .eslintrc.js
module.exports = {
plugins: ['react'], // 引入 eslint-plugin-react 插件
extends: ['eslint:recommended', 'plugin:react/recommended'], // 使用 React 相关规则
// 其他配置选项...
};
规则(rules
)
在 ESLint
中,rules
是一个非常重要的配置选项,它用于定义代码检查规则,以控制代码的质量、风格和最佳实践。通过配置 rules
,你可以告诉 ESLint
如何处理各种代码问题,例如禁用不建议使用的语法、强制一致的代码风格或检测潜在的错误。
rules
配置对象中的每个属性都代表一个规则,这些规则通常以字符串或数组的形式定义,具体取决于规则的要求。规则可以有以下几种值:
"off"
或0
:关闭规则,不执行检查。"warn"
或1
:启用规则,并将问题报告为警告,但不会阻止代码的执行。"error"
或2
:启用规则,并将问题报告为错误,可能会阻止代码的执行。
下面是一个简单的示例,演示如何配置一些常见的 ESLint
规则:
js
// .eslintrc.js
module.exports = {
rules: {
'no-console': 'warn', // 不允许使用 console,报告为警告
'indent': ['error', 2], // 强制使用两个空格的缩进
'quotes': ['error', 'single'], // 强制使用单引号
'semi': ['error', 'always'], // 强制使用分号
},
};
Prettier
安装与配置
你可以使用 npm
或 yarn
来安装 Prettier
,全局安装或项目本地安装都是可行的。
bash
# npm
npm install --save-dev prettier
# yarn
yarn add prettier -D
Prettier 支持通过 .prettierrc
配置文件来定义代码格式化规则。创建一个名为 .prettierrc
的文件并定义你的规则,以下是一个示例:
js
{
"printWidth": 80, // 每行的最大字符数
"tabWidth": 2, // 缩进宽度
"useTabs": false, // 使用空格而不是制表符进行缩进
"semi": true, // 在语句末尾使用分号
"singleQuote": true, // 使用单引号而不是双引号
"trailingComma": "all", // 多行对象和数组的尾随逗号
"bracketSpacing": true, // 对象字面量中的括号之间的空格
"arrowParens": "always" // 箭头函数参数周围使用括号
}
集成 Prettier 到编辑器
为了方便使用 Prettier,你可以将它集成到你使用的代码编辑器中。以下是一些常见的编辑器插件链接,用于集成 Prettier:
使用 Prettier
一旦安装和配置了 Prettier,你可以在命令行中或通过编辑器插件来格式化代码。以下是一些常见的用法示例:
-
通过命令行格式化文件:
bashnpx prettier --write yourfile.js
-
通过编辑器插件快捷键触发格式化。
-
集成 Prettier 到你的构建工具(如
webpack
、gulp
等),以在构建过程中自动格式化代码。
Prettier
可以帮助你和你的团队保持一致的代码风格,减少代码审查时的格式问题,并提高代码的可读性。
与ESLint
配合使用
Prettier
和 ESLint
可以很好地搭配使用,以确保你的代码既符合一致的格式,又符合代码质量和最佳实践规则。以下是它们搭配使用时需要注意的问题:
-
规则冲突问题:
Prettier
和ESLint
可能会存在一些规则冲突,例如ESLint
可能要求使用特定的缩进,而Prettier
会自动格式化成另一种缩进样式。为了解决这个问题,可以使用eslint-config-prettier
插件来禁用与Prettier
冲突的ESLint
规则,如示例中的'plugin:prettier/recommended'
配置。
-
避免重复规则:
- 避免在
ESLint
和Prettier
中定义相同的规则,因为这可能会导致重复的检查和冲突。通常,你可以将代码格式化的任务留给Prettier
,而将更多关注代码质量和最佳实践的任务留给ESLint
。
- 避免在
-
使用编辑器插件:
- 配置和使用编辑器插件是
Prettier
和ESLint
结合使用的关键。确保你的编辑器插件正确配置,以在保存文件时自动运行Prettier
和ESLint
。
- 配置和使用编辑器插件是
-
预览变化:
- 在格式化代码之前,使用编辑器插件或命令行工具预览
Prettier
的格式化变化,以确保你满意最终的格式化结果。
- 在格式化代码之前,使用编辑器插件或命令行工具预览
-
手动修复
ESLint
报告的问题:- 虽然
Prettier
负责格式化代码,但有时ESLint
可能会报告其他代码质量问题,如未使用的变量或潜在的错误。这些问题应该根据ESLint
的建议进行手动修复。
- 虽然
TypeScript
本文并不是ts
的学习指南,因此本小结的主要目标是分析ts
使如何帮助我们写出高可维护性代码的。
TypeScript
是什么
简单来说ts
是js
的超集(js
包含于ts
),在js
的基础上扩展了静态类型、面向对象的编程范式(如类、继承、抽象类等)能力,并且ts
属于编译型语言,最终会编译js
,因此开发者无需担心js
迁移到ts
的兼容性成本。
bash
# 安装ts
yarn add typescript -D
# 初始化ts配置项
npx tsc --init
# 编译ts代码
npx tsc
配置tsconfig.json
文件
json
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "./dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true
},
"include": [
"./src/**/*.ts"
],
"exclude": [
"node_modules",
"**/*.test.ts"
]
}
这几乎就是日常会用到的所以关于ts
的配置,如果需要用到一些特殊的配置,查阅官方文档即可。
-
compilerOptions
:这是一个包含许多编译选项的对象。以下是一些常见的配置选项:target
:指定要编译为的ECMAScript
版本,如"ES3"
,"ES5"
,"ES6"
,"ES2015"
,"ES2017"
, 或"ESNext"
。通常,你会设置为"ES6"
或更高版本,以利用新的语言特性。module
:指定生成的模块系统,可以是"CommonJS"
,"AMD"
,"System"
,"UMD"
,"ES6"
, 或"ESNext"
。这取决于你的项目和目标环境。outDir
:指定编译后的 JavaScript 文件的输出目录。strict
:将一组严格的类型检查选项打开。通常,将其设置为true
以启用强类型检查。noImplicitAny
:禁止隐式的any
类型。如果设置为true
,则需要明确为每个变量指定类型。esModuleInterop
:允许在导入默认导出时使用import x from 'module'
语法。
-
include
:一个包含匹配文件和目录模式的数组,指定哪些文件应该包含在编译中。 -
exclude
:一个包含匹配文件和目录模式的数组,指定哪些文件应该排除在编译之外。
类型检查
本文的目的是讲解如何通过类型检查写出优雅的代码而不是教大家如何写类型体操,关于ts
类型体操,推荐这篇文章接近天花板的TS类型体操,看懂你就能玩转TS了。言归正传,接下来我将从类型定义、类型推断、如何处理类型报错三个方面讲解ts
的类型魔法。
类型定义
类型定义是开发者显式为变量、函数参数、函数返回值等代码元素指定类型的方式。通过类型注解,你告诉 TypeScript
某个变量或参数应该具有什么类型。类型注解使用冒号(:
)来定义。
ts
function add(a: number, b: number): number {
return a + b;
}
在上面的例子中,a
和 b
被显式注解为 number
类型,而函数 add
的返回值也被注解为 number
类型。
类型定义为js
注入了静态语言的特性,它能带给开发者更大的掌控感和安全感(类型错误会在编译时抛出),当然也会丢失js
部分灵活性和高效性。个人更偏爱ts
带来的掌控感和安全感,让我能够更加确信自己的代码bug free
类型推断
类型推断是 TypeScript
编译器自动推断代码中元素的类型的过程,而不需要显式的类型注解。TypeScript
使用类型推断来确定变量、函数参数、函数返回值等的类型,基于变量的初始化值和上下文信息。
ts
let x = 42; // TypeScript 推断 x 为 number 类型
在上述例子中,TypeScript
推断 x
的类型为 number
,因为它被初始化为数字。
类型推断还在函数参数和返回值中发挥作用:
ts
typescriptCopy codefunction multiply(a: number, b: number) {
return a * b; // TypeScript 推断返回值类型为 number
}
在这里,虽然没有显式注解函数 multiply
的返回值类型,TypeScript
会根据函数体中的操作自动推断返回值类型为 number
。
如何处理类型报错
-
仔细阅读错误信息:
TypeScript
提供了详细的错误信息,包括错误类型、位置和相关上下文。仔细阅读这些错误信息是解决问题的第一步。错误信息通常会告诉你哪里出了问题以及如何修复它。 -
检查类型注解: 确保你的类型注解与变量、参数和函数返回值的实际使用方式匹配。如果你在注解和实际值之间存在不匹配,会导致类型错误。
-
使用类型断言: 有时候,你可能比
TypeScript
更了解某个值的类型。在这种情况下,你可以使用类型断言(Type Assertion)告诉TypeScript
如何处理该值。但要小心使用,确保你的断言是正确的,否则可能引入运行时错误。tslet value: any = "Hello, TypeScript!"; let length: number = (value as string).length; // 类型断言
-
调整变量或函数签名: 如果你发现类型错误是因为变量或函数的签名与实际用途不匹配,你可能需要重新设计或调整它们。
-
利用工具和编辑器支持: 当你在代码中处理类型错误时,现代编辑器如 Visual Studio Code 通常会提供代码补全和错误修复建议。利用这些工具来更轻松地纠正错误。
ESLint
和TypeScript
安装必备依赖
bash
yarn add @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
配置到Eslint
配置项中
js
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
]
}
ok,到这里就能在项目中使用ts + eslint + prettier
结合体啦,这里附赠一个临时禁用规则的注释(谨慎使用,IDE
中可以装插件自动生成禁用注释,不需要刻意记忆)
ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const unusedVar: string = "Hello";
代码质量工作流程
提交前检查
Git
提供了代码提交过程中的一些生命周期钩子,我们可以借助lint-staged
和husky
两个包很方便配置代码静态检查能力。
husky
是一个工具,用于管理Git
钩子。lint-staged
用于在提交前只运行钩子在暂存区的文件上。
配置 lint-staged
: 在 package.json
文件中,添加一个 lint-staged
配置,用于指定在提交前运行的命令。例如:
json
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write",
"git add"
]
}
这个配置表示在提交前,针对 JavaScript
和 TypeScript
文件,将首先运行 eslint --fix
来修复ESLint
错误,然后运行 prettier --write
来格式化代码,最后使用 git add
添加更改。
配置 husky
钩子: 在 package.json
文件中,添加一个 husky
配置,以在 pre-commit
钩子中运行 lint-staged
:
json
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
这个配置会在每次提交前运行 lint-staged
。
持续集成
使用持续集成工具(如Jenkins
)来集成代码质量检查,下面以Jenkins
为例讲解集成的过程
- 安装
Jenkins
: 首先,你需要在服务器上安装Jenkins
。按照官方文档的说明来执行这一步骤。 - 安装必要的插件: 安装
Jenkins
插件,例如NodeJS
插件,以便在Jenkins
上执行Node.js
项目。 - 创建
Jenkins
任务: 在Jenkins
上创建一个新任务。你可以选择自由风格项目或流水线项目,具体取决于你的需求。在任务配置中,定义构建步骤,包括安装和配置代码质量检查工具、运行测试和检查代码质量。 - 配置触发: 配置
Jenkins
任务的触发条件。你可以选择轮询代码库、Webhooks
或使用插件(如GitHub
或GitLab
插件)来触发构建。 - 执行构建: 一旦任务配置完成,
Jenkins
将根据触发条件自动执行构建。在构建过程中,代码质量检查工具将运行,检查代码的质量。
团队协作
- 在团队中制定统一的代码规范,并用脚手架存储含有该规范的项目模板。
- 通过代码审查(提
PR
)来确保一致性
结语
感谢你能看到最后,这是前端工程化系列的第三篇文章,后续陆续会产出构建工具、缓存管理、兼容性、模块化、性能优化、自动化测试等内容,如果对你有帮助的话点个👍和收藏吧❤️