如何让自己的前端项目更优雅

594 阅读17分钟

在大多数程序员小白刚接触编程时,会有一种疑惑,变量,方法应该写在哪个文件里面, 这关系到项目后期的维护,如果定义的位置不对,很容易造成这样一种情况

我在A.vue里面写了变量M,但是想在B.vue里面去调用,那这样做的后果就是,需要先把变量 M提取出来,放在公共环境里面,然后供 A 和 B 同时使用

那接下来我将从一下几个方面讲解一下怎么提高我们的代码可维护性,简洁性,美观性

1. 变量,方法存放的位置

2. 项目文件进行合理的分类

3. 使用sass/less

4. 使用prettier,eslint

代码的可维护性

为什么可维护性如此重要,  软件维护是软件开发时间最长的一个阶段,所花费的精力和费用也是最多的一个阶段,基本上软件交付之后就进入了维护阶段,占整个系统生存周期的60%~80%。下面我们来看一个饼状图,这是软件生命周期各个阶段所占比的时间

代码的可维护性直接关系到软件的生命力和项目的成本。其重要性主要体现在以下几个方面:

1. 显著降低长期成本(省钱!)

  • 易修改: 当需要添加新功能或修改旧功能时,良好的代码结构可以让你快速定位需要修改的地方,而不必担心“牵一发而动全身”。这大大减少了开发时间和人力成本。
  • 易修复: 当出现 Bug 时,清晰的代码能帮助你像侦探一样迅速找到线索(问题根源),而不是在迷宫般的代码里浪费时间。修复 Bug 的成本通常是开发阶段的好几倍。

2. 提升开发效率和团队协作(省时!)

  • 平滑入职: 新成员加入团队时,一份可维护性高的代码是最好的培训资料。他们能更快理解项目结构,开始贡献价值,而不是花几周时间苦苦 decipher(破译)代码。
  • 减少“巴士因子”: (Bus Factor:指项目中有多少关键人员被巴士撞了项目就会瘫痪)。如果只有一个人能看懂某部分复杂代码,他一旦离职或休假,项目就可能陷入停滞。可维护的代码意味着知识被共享和记录,而不是锁在某个人的脑子里。

一个简单的比喻

把写软件项目比作建房子

  • 可维护性差的代码:就像没有设计图,随意用劣质材料搭建的房子。刚开始很快,但想加个二楼?可能会塌。水管漏了?找不到阀门在哪,得砸开墙。最终,维护成本远超重建成本。
  • 可维护性好的代码:就像由专业建筑师设计,结构坚固、图纸清晰、管线分明的高质量建筑。任何后续的装修、扩建、维修都非常方便和可控。

总结

代码的可维护性不是一种“炫技”,而是一种必要的、具有高度商业价值的技术实践。 它不是为了满足程序员的“洁癖”,而是为了:

  • 省钱(降低长期维护成本)
  • 省时(提高团队开发效率)
  • 省心(保障系统稳定可靠)
  • 赚钱(支撑业务快速响应市场)

因此,在项目初期多花一点时间编写清晰、可维护的代码,或者在后期积极重构代码,是一项回报率极高的投资。忽视它,迟早会付出沉重的代价。

接下来我们步入正文,首先说明以下接下来所演示的项目的架构

1. 框架:electron+vite+vue

2. 开发语言:Typescript

3. CSS预处理器:Sass

4. Store存储器:Pinia

1. 变量,方法存放的位置

1.1 私有

当我们有一个变量或方法 M ,且这个M只有在A.vue里面才能使用,其他任何地方都用不到,那么我们就可以把M放入到A.vue里面

1.2 局部

假如 M 被 A 引用,又被 B 引用,但是并没有被其他文件所引用,若此时A和B都在同一个文件夹内,我们就可以再单独写一个hook.ts文件,将M 存放进去

实际效果如下,hook.ts里面有一个方法

export const getSvgIcon = async (icon: string): Promise<string> => {
  const res = await window.electron.ipcRenderer.invoke('nodeIcon', 'getSvgDataById', [icon])
  if (res) {
    const svg = res.svg
      .replace(/(width|height)=".*?"/g, '') // 移除原始宽高
      .replace('<svg', '<svg width="100%" height="100%"') // 设置为响应式宽高 + 可继承颜色
    return svg
  }
  return ''
}

这个方法在OrdinaryNode.vue以及TransportNode.vue用到,但是其他文件并不需要这个方法,所以这个方法无需提升为一个全局函数

1.3 全局

如果M 被A,B,C都引用到了,且这三个文件分散在项目的不同位置,那么我们就需要把M提升为全局变量,那下面我先简单的介绍一下pinia

Pinia 是 Vue.js 官方推荐的新一代状态管理库,可以把它理解为 Vuex 的升级版,旨在为 Vue 应用提供一个更简单、直观且功能强大的状态管理解决方案。

他是什么,为什么需要pinia

  1. 什么是状态管理?
    在 Vue 应用中,多个组件可能需要共享同一个数据(比如用户登录信息、全局主题等)。如果这个数据在每个组件里都单独维护,会非常混乱且难以同步。状态管理就是把这个“共享数据”提取出来,放在一个全局的“仓库”里进行统一管理,任何组件都可以从这里读取或修改数据。
  2. Pinia 就是这个“仓库”
    你可以把它想象成一个整个应用都可以访问的“全局数据对象”,但它比简单的全局对象更强大,因为它具备响应式可追踪易于调试的特性。

在项目中,我们就可以在 src 文件夹下创建一个store文件夹,在此文件夹下面编写我们的全局变量

如下图所示,state里面就是我们的变量,actions里面就是我们的方法

当我们定义好变量和方法后,就可以在任意的地方去使用它

另外,无论我们采用哪种方法去定义变量,都要记得写注释,注释能够一眼直观的了解到这个东西是做什么的

2. 项目文件进行合理的分类

对项目文件进行合理分类,是为了能够快速直到某些文件是做什么功能,在debug,定位功能点时,能够快速找到该文件

2.1 整体而言

下面是项目的主体目录

可以看到我依照功能点将文件进行来初步的划分

assets:静态资源,比如svg,png

components:单个组件,比如全局的一些小弹窗,loading加载页面,自定义的css动画

page:重要的主页面,消息栏,菜单,主窗口

plugins:一些插件,比如SVG插件

router:路由相关的文件

scss:全局样式文件

store:全局储存文件

thoroughfare:主进程与渲染进程通信的桥梁

types:全局类型定义

utils:全局工具函数

2.2 细致化划分

以store为例,我们不能把所有的文件都直接放到store目录下面,还要对每个文件进行分类

store下面的五个文件夹,是根据功能进行了一个划分

假如说我们要找关于message功能的文件,就可以立即直到与message相关的所有文件

除store以外,我们再来看看我们的vue是怎么分类的

与store类似的是,vue 文件也需要进行按照功能进行分类

有一点需要注意,假如说 A.vue里面有很多组件,并且这些组件只在A里面用到,那么我们可以把这些组件理解为私有组件,并在A同级处创建一个components文件夹,把A用到的组件存放进去

我们以childWindows文件夹为例,如下图所示

ChildWin.vue是总界面,他需要用到上面所有的vue文件,但是这些文件也只有在ChildWin用到

3. 使用 Sass/Less

由于本项目使用的是Sass,所以我们重点讲解Sass

3.1 什么是Sass

简单来说,Sass 是一个 CSS 预处理器。它是 CSS 的一个强大扩展,能让你使用 CSS 中原本没有的特性(如变量、嵌套、混合、继承等)来编写更具逻辑性和可维护性的样式代码,然后再将其编译成标准的 CSS 文件供浏览器使用。

你可以把它想象成 CSS 的一个“升级版”或“增强版”。

为什么需要Sass

原生 CSS 在项目变得庞大复杂时会显得力不从心,主要有以下痛点:

  1. 代码重复:相同的颜色、字体、边距值需要在多个地方重复书写,难以统一修改。
  2. 缺乏逻辑:无法使用变量、条件语句、循环等编程特性,代码冗长且不灵活。
  3. 选择器嵌套复杂:书写多层嵌套的选择器时非常繁琐,且难以管理。
  4. 难以维护:随着项目增长,CSS 文件变得臃肿且难以组织和阅读。

Sass 正是为了解决这些问题而生的。

Sass 最初设计了一种使用缩进(类似 Python)的语法,名为 缩进语法(通常就叫 Sass)。后来,为了更平滑地过渡,它引入了另一种更接近 CSS 的语法,名为 SCSS

现在最常用的是 SCSS。

特性SCSS (Sassy CSS)Sass (缩进语法)
扩展名.scss.sass
语法完全兼容 CSS。任何合法的 CSS 都是合法的 SCSS。它使用花括号 {} 和分号 ;不使用花括号和分号,依靠缩进和换行来指定格式。
示例scss .container { padding: 20px; p { color: red; } }sass .container padding: 20px p color: red

3.2 Sass特性

3.2.1 变量 Variables

可以存储颜色、字体、尺寸等任何值,并在整个项目中重复使用。修改一个变量,所有用到它的地方都会自动更新。

// 定义变量
$primary-color: #3498db;
$font-stack: Helvetica, sans-serif;

// 使用变量
body {
  font-family: $font-stack;
  color: $primary-color;
}

3.2.2 嵌套 Nesting

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;

    li {
      display: inline-block;

      a {
        text-decoration: none;
        &:hover { // & 代表父选择器,这里是 ‘a'
          color: red;
        }
      }
    }
  }
}

3.2.3 混合 Mixins

可以定义可重用的代码块,甚至可以传入参数。类似于编程中的函数。

// 定义一个带参数的 mixin
@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  border-radius: $radius;
}

// 使用 mixin
.box {
  @include border-radius(10px);
}

3.2.4 继承/占位符 Extend/Placeholders

允许一个选择器继承另一个选择器的所有样式,避免重复代码。

// 定义一个带参数的 mixin
@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  border-radius: $radius;
}

// 使用 mixin
.box {
  @include border-radius(10px);
}

3.2.5 模块化 Modules

可以将样式拆分到多个文件中(局部文件),然后用 @use 规则将它们加载到另一个文件中。这有助于更好地组织代码。

  • _colors.scss (局部文件,文件名以下划线开头)
  • styles.scss (主文件)
// _colors.scss
$primary: #3498db;

// styles.scss
@use 'colors'; // 引入模块(注意不需要下划线)

body {
  color: colors.$primary; // 通过命名空间访问
}

3.2.6 运算 Operations
可以在代码中执行数学运算。

.container {
  width: 100% / 3; // 计算宽度为 33.333%
  height: 100px + 50px; // 计算高度为 150px
}

3.3 项目使用

在前面的项目文件合理分类里面已经讲解到,我们项目中的scss文件夹就是存放的全局样式属性

然后我们就可以在项目里面去使用这些全局设置的样式数据

3.5 总结

特性Sass原生 CSS
本质预处理器,一种编程语言样式表语言
功能变量、嵌套、混合、继承、模块等基础样式定义
运行需要先编译成 CSS浏览器可直接解析
可维护性,适合大型复杂项目,项目越大越难以维护

4. 使用prettier,eslint

我先来简单明了地介绍一下 Prettier 和 ESLint。它们是现代前端开发中不可或缺的两个工具,但职责完全不同。

一句话总结

Prettier:管 代码长得好看不好看(格式)。

ESLint:管 代码写得对不对、好不好(质量与错误)。

4.1 Prettier:代码格式化器

Prettier 是什么,能做什么

Prettier 是一个“有主见”的代码格式化工具。它的核心目标是结束团队中关于“代码风格”的争论(比如用单引号还是双引号、缩进用几个空格、代码换行等)。

比如说我写了一个很乱的代码

const hello= (name)=>{return 'Hello, '+ name}

Prettier 会一键把它格式化成统一的、漂亮的风格:

const hello = (name) => {
  return 'Hello, ' + name
}

Prettier 的特点

  • 霸道:它只有很少的配置选项。你基本上要遵守它定好的规则,但这正是它的优点——不用再为风格吵架。
  • 支持广泛:不仅能格式化 JavaScript,还支持 TypeScript、JSX、CSS、SCSS、JSON、HTML、Markdown 等多种语言。
  • 集成方便:可以和编辑器(VS Code)、Git hooks 等无缝集成,实现保存即格式化。

简单说:Prettier 让你的代码风格变得一致和美观。

4.2 Prettier 在项目中的使用

  1. 先确保 VS Code编辑器有 Prettier 插件

然后编写 prettier.config.js 文件,这个文件的内容就是规范我们代码的格式

文件内容如下

module.exports = {
  // 一行最多 80 字符
  printWidth: 100,
  // 使用 4 个空格缩进
  tabWidth: 2,
  // 不使用 tab 缩进,而使用空格
  useTabs: false,
  // 行尾需要有分号
  semi: false,
  // 使用单引号代替双引号
  singleQuote: true,
  // 对象的 key 仅在必要时用引号
  quoteProps: 'as-needed',
  // jsx 不使用单引号,而使用双引号
  jsxSingleQuote: false,
  // 末尾使用逗号
  trailingComma: 'none',
  // 大括号内的首尾需要空格 { foo: bar }
  bracketSpacing: true,
  // jsx 标签的反尖括号需要换行
  jsxBracketSameLine: false,
  // 箭头函数,只有一个参数的时候,也需要括号
  arrowParens: 'always',
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准
  proseWrap: 'preserve',
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
  // 换行符使用 lf
  endOfLine: 'auto'
}

然后再编写 .prettierignore 文件,这个文件表示的是我们要忽略哪些文件,比如说node_modules里面的文件肯定不需要代码美化

文件内容如下

/dist/**
/node_modules/**
/electron-release/**
/node_modules/**
**/*.svg
**/*.sh
/public/**
/src/renderer/src/assets/**
package.json
tsconfig.json
tsconfig.node.json
components.d.ts
/dist-electron/**

如果设置了这两个文件后,发现VS Code并没有生效

那我们需要编写VS Code 的settings.json配置文件,先找到这个settings.json文件,如下图

文件内容如下

{
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "prettier.requireConfig": true,
}

4.3 ESLint:代码检查器

ESLint是什么,能做什么

ESLint 是一个静态代码分析工具。它的核心目标是查找你的代码中的模式问题潜在错误

它会检查你的代码,然后抛出类似这样的错误或警告:

  • 错误'x' is assigned a value but never used.(变量定义了但没使用)
  • 错误Unexpected console statement.(不允许使用 console.log
  • 警告Prefer const over let.(建议用 const 而不是 let
  • 最佳实践Expected '===' and instead saw '=='.(建议用严格相等 ===

ESLint的特点

  • 可高度配置:有大量规则可供选择,你可以自由决定哪些规则是错误(Error),哪些只是警告(Warning),或者直接关闭。社区也有很多预设配置(如 eslint:recommendedairbnb)。
  • 可修复:很多规则可以自动修复(比如自动把 == 改成 ===)。
  • 可扩展:可以自己编写插件和规则,例如支持 React(eslint-plugin-react)或 Vue(eslint-plugin-vue)的特定规则。

简单说:ESLint 帮你发现代码中的错误和不良实践,保证代码质量和避免 Bug。

ESLint和Prettier如何配合工作

虽然它们有部分功能重叠(比如缩进、分号),但它们的核心职责不同。在项目中,它们完美互补:

  1. 分工合作

    • Prettier 负责所有格式问题(分号、换行、空格、引号、最大长度等)。
    • ESLint 负责所有代码质量问题(未使用的变量、全局变量泄露、使用全等操作符等)。
  2. 工作流程
    通常的流程是:ESLint 先检查代码质量问题并修复其中可自动修复的 -> 然后 Prettier 进行格式化 -> 最后再由 ESLint 检查一遍(因为格式化可能会引入新的格式问题,需要 ESLint 再次验证)。

  3. 如何避免冲突?
    为了避免 Prettier 的格式化规则和 ESLint 的格式规则冲突,我们会使用 eslint-config-prettier 这个配置包来关闭所有与 Prettier 冲突的 ESLint 规则。这样,ESLint 就只专注于代码质量检查,把格式问题完全交给 Prettier。

总结与类比

工具职责类比
Prettier代码格式化出版社的排版编辑。他只管文章排版是否美观、字体是否统一、段落间距是否一致。他不管文章内容有没有错别字或语法错误。
ESLint代码质量检查出版社的审稿编辑。他主要负责检查文章的逻辑是否通顺、有没有错别字、语法是否正确、用词是否得当。他不管文章用的是什么字体。

4.4 ESLint在项目中的使用

和 Prettier 类似,我们也需要先安装插件

然后项目中需要有一个eslint.config.mjs的文件

文件内容如下

import tseslint from '@electron-toolkit/eslint-config-ts'
import eslintConfigPrettier from '@electron-toolkit/eslint-config-prettier'
import eslintPluginVue from 'eslint-plugin-vue'
import vueParser from 'vue-eslint-parser'

export default tseslint.config(
  { ignores: ['**/node_modules', '**/dist', '**/out'] },
  tseslint.configs.recommended,
  eslintPluginVue.configs['flat/recommended'],
  {
    files: ['**/*.vue'],
    languageOptions: {
      parser: vueParser,
      parserOptions: {
        ecmaFeatures: {
          jsx: true
        },
        extraFileExtensions: ['.vue'],
        parser: tseslint.parser
      }
    }
  },
  {
    files: ['**/*.{ts,mts,tsx,vue}'],
    rules: {
      'vue/require-default-prop': 'off',
      'vue/multi-word-component-names': 'off',
      '@typescript-eslint/no-explicit-any': 'warn', // 改为警告
      '@typescript-eslint/no-unused-vars': 'off',
      '@typescript-eslint/no-unsafe-function-type': 'warn',
      '@typescript-eslint/explicit-function-return-type': 'off',
      '@typescript-eslint/prefer-as-const': 'warn',
      'no-case-declarations': 'warn',
      'vue/no-side-effects-in-computed-properties': 'warn',
      'no-prototype-builtins': 'warn',
      'vue/block-lang': [
        'error',
        {
          script: {
            lang: 'ts'
          }
        }
      ]
    }
  },
  eslintConfigPrettier
)

rulers就是我们配置的提示信息,

总结

在软件开发的世界里,我们常常专注于实现炫目的功能和满足紧迫的deadline,而忽略了隐藏在代码之下的基石——可维护性。本文探讨的,正是如何夯实这一基石。

通过有章法的代码组织(变量/方法的合理存放与状态管理)、清晰的项目结构(按功能模块分类)、现代化的开发工具(Sass/Less)以及严格的团队规范(Prettier/ESLint),我们并非只是在遵守一些最佳实践,而是在构建一个坚韧、灵活且可持续的软件系统。

提高代码的可维护性,远非一种吹毛求疵的“洁癖”,它是一项极具远见的技术投资。它直接转化为更低的长期成本、更快的开发效率、更顺畅的团队协作以及更强的应对变化的能力。最终,一个易于维护的项目,是每一位开发者都能愉快工作的项目,也是能够经受住时间考验、走向成功的关键。

记住,今天我们为代码多付出一份心思,未来它必将回报我们十分从容。 让我们从下一个项目、下一行代码开始,有意识地去书写这份从容。