Skip to content

第四章 认识项目目录 (项目整体介绍)

前言

我们现在已经创建好项目的目录了,现在让我们来了解下项目的基本构成以及各个文件的作用。

一、node_modules文件夹

Vue 项目中,node_modules 文件夹包含了项目依赖的所有第三方模块和库。这些模块通常是通过npm(Node Package Manager)安装的,用于实现各种功能,包括但不限于前端框架、工具库、样式预处理器、构建工具等。node_modules 的存在使得开发人员能够方便地管理项目所需的各种模块和库,并且可以通过引用它们来扩展项目的功能。

node_modules

我们也可以很方便的在这里看到一些插件库的源码。

二、public文件夹

Vue 项目中,public 文件夹用于存放不需要经过构建处理的静态资源。这些资源会直接复制到构建输出的根目录下。例如,你可以将不需要经过编译的图片、字体文件、以及各种不需要打包处理的静态文件放置在 public 文件夹中。这样做可以在构建过程中节省时间,因为这些资源不需要经过 vitewebpack等构建工具的处理,而是直接复制到最终的构建输出目录中。

public

三、src文件夹

在Vue项目中,src文件夹是源代码文件夹,用于存放项目的源代码文件。通常,src文件夹包含项目的主要开发内容,包括但不限于以下内容:

  • 组件:Vue项目中的组件文件通常会存放在src文件夹中,这些组件用于构建页面和应用程序的各个部分。

  • 样式:包括CSS、Sass、Less等样式文件,负责定义项目的样式和布局。

  • JavaScript文件:包括Vue实例、路由配置、状态管理等各种JavaScript文件,用于实现项目的功能和交互逻辑。

  • 图片和其他静态资源:存放项目中使用的图片、字体文件以及其他静态资源。

src

四、.gitignore

.gitignore 文件在 Vue 项目中的作用是指定哪些文件或目录在对项目进行版本控制时应该被忽略。这意味着这些被列出的文件或目录不会被添加到版本控制系统(如Git)中,也不会被提交到代码仓库中。在 Vue 项目中,.gitignore 文件通常用于忽略以下类型的文件和目录:

  • 临时文件:例如编译产生的临时文件、日志文件、以及运行时产生的临时文件。

  • 依赖管理文件:例如 node_modules 文件夹,这是由 npm 或者 Yarn 安装的依赖包所在的目录,通常会被列入 .gitignore 文件以忽略提交到代码库中。

  • 敏感信息:例如包含个人隐私信息的配置文件、API密钥等,在提交代码时通常需要被排除在外。

gitignore

通过 .gitignore 文件,开发者可以避免向代码仓库提交不必要的、临时性的或者敏感的文件,使代码仓库保持整洁,减少不必要的提交和增加的存储空间占用。

五、index.html

在一个 Vite 项目中,index.html 在项目最外层而不是在 public 文件夹内。这是有意而为之的:在开发期间 Vite 是一个服务器,而 index.html 是该 Vite 项目的入口文件。

index

Viteindex.html 视为源码和模块图的一部分。Vite 解析 <script type="module" src="...">,这个标签指向你的 JavaScript 源码。甚至内联引入 JavaScript<script type="module"> 和引用 CSS<link href> 也能利用 Vite 特有的功能被解析。另外,index.html 中的 URL 将被自动转换,因此不再需要 %PUBLIC_URL% 占位符了。

六、package.json

package.json 是一个重要的文件,它用于描述 Vue 项目的元数据和依赖项。该文件包含了项目的名称、版本、描述、入口文件、依赖模块等信息。除此之外,你还可以在 package.json 中定义一些脚本,用于项目的构建、启动、测试等任务。

package

1、name、version、private 和 type 属性

  • "name":即项目名。
  • "version":即项目版本。
  • “type”:用于指定项目的模块类型,特别是在一些现代的 JavaScript 项目中,比如使用 ECMAScript 模块的项目。当 "type" 字段被设置为 "module" 时,它表示该项目使用 ECMAScript 模块类型。这意味着该项目中的 JavaScript 文件将按照 ECMAScript 模块规范进行解析。这个字段的使用允许开发者在不同的模块类型之间进行切换,以确保项目使用正确的模块类型,从而提供更好的兼容性和功能性。
  • "private":用来指示该包是否被认为是私有的。如果将 "private" 字段设置为 true,表示该包是私有的, npm 发布工具在执行 "npm publish" 时将会禁止发布这个包。这样可以防止意外发布私有包到 npm 仓库中。默认情况下,"private" 字段的值是 false,即可以发布到 npm 仓库中。

2、script 属性

package.json 文件中的 "scripts" 字段下,你可以定义各种自定义的脚本命令,比如启动开发服务器、打包项目、运行测试等等。

javascript
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview"
}

项目生成后,默认的 script 配置:

  • "dev" : "vite"这个命令是用来启动 Vite 开发服务器,会在开发过程中实时编译和热更新你的代码,使得开发过程更加高效。

  • "build" : "vite build"这个命令用于将你的项目构建为生产环境的可部署代码,通常会进行代码压缩、打包、优化等操作,以便用于在生产环境中部署。

  • "preview" : "vite preview" 这个命令在构建生产版本后会启动一个本地服务器,帮助你预览构建后的项目。这对于在构建前查看最终输出非常有用。

运行时,只需要在 terminal 使用 npm run 或者 yarn 加上命令即可。

bash
npm run dev      /  yarn dev
npm run build    /  yarn build
npm run preview  /  yarn preview

3、dependencies 和devDependencies 属性

  • dependencies — 指定了项目运行所依赖的模块
    • –save 参数表示将该模块写入 dependencies 属性
  • devDependencies — 指定了项目开发所需要的模块
    • –save-dev 表示将该模块写入 devDependencies 属性
javascript
"dependencies": {
  "vue": "^3.3.8"
},
"devDependencies": {
  "@vitejs/plugin-vue": "^4.5.0",
  "vite": "^5.0.0"
}

4、其他

除了项目初始化默认的属性外,package.json 还可以设置一些其他的属性。

  • "engines":指明了该项目所需要的node.js版本。
javascript
"engines": {
 "node": ">= 4.0.0",
 "npm": ">= 3.0.0"
}
  • "bin":许多包有一个或多个可执行文件希望被安装到系统路径。在npm下要这么做非常容易。
javascript
"bin" : { "myweb" : "./cli.js" }
  • "eslintConfig":eslint 配置,eslint 工具是为了保证代码的一致性和避免一些语法错误。
  • "browserslist":用以兼容各种浏览器。
javascript
"browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
]

七、README.md

1、介绍

README.md 是一个项目根目录下的说明文件,通常用于向其他开发者或项目贡献者传达项目的信息和使用方式。在 Vue 项目中,README.md 的作用包括:

  • 项目介绍:README.md 中通常包含项目的介绍和背景,包括项目名称、描述、主要功能,以及可能的用途和相关信息。

  • 安装和运行:README.md 文件可以包含关于如何安装项目依赖和运行项目的说明,包括安装依赖模块的命令、如何启动开发服务器等。

  • 配置和部署:提供关于配置和部署项目的指引,例如如何进行环境配置、如何打包项目以及如何部署到生产环境。

  • 使用示例:可以在 README.md 中包含一些使用示例或演示,以便其他开发者快速了解项目的基本用法和特性。

  • 贡献方式:向其他开发者展示如何参与项目的贡献,包括如何报告问题、提交修改请求或者参与讨论。

README

2、配置

在根目录下创建doc文件夹,放入下面配置文件。

doc文件下 css-naming.md文件

javascript
# CSS 命名规范

CSS 的命名,采用 [BEM](http://getbem.com/) 命名规范,可以有效的避免组件间样式的相互污染,减少嵌套层级。具体规则如下:

## BEM 是什么?

BEM 使用分隔符将类名区隔成三个部分:

[prefix]-[block]\_\_[element]--[modifier]

- **prefix**:全局的前缀,这里指代表 tdesign 的前缀,也就是 t-
- **Block(块)** :组件的最外层父元素,这个类包含最通用和可重用的功能。
- **Element(元素)** :组件内可包含一个或多个元素,元素为块添加了新功能。无需重置任何属性。
- **Modifier(修饰类)** :块或元素都可以通过修饰词来表示为变体。

## BEM 的特点

- 易于理解和学习
- 创建的结构易于扩展和维护

## BEM 命名规则

通过几个常见实例,来讲解一下项目实践中的注意事项。

### 1. 无元素和修饰类的块

### 2. 含简单修饰类的组件

- 组件可能有变体,**变体必须使用修饰类来表示**
- **修饰类不能单独使用**。修饰类旨在增强而非替代基类

### 3. 含元素的组件

稍微复杂的结构通常有子元素。

- 每个需要设置样式的子元素**必须**包含一个类名。
- 不要省略 HTML 中子元素的类名,否则会对组件中的元素使用更高权重的选择器。BEM 目的之一是保持样式一致和较低的权重值(specificity)。
- 如果结构具有多个层级的子元素,**类名无需反映出每个层级**
- BEM 并不打算传达结构层级。表示组件中子元素的 BEM 类名**只需包含块名和元素名**

### 4. 含修饰类的元素

在某些情况下,你可能希望更改组件中的单个元素。在这种情况下,**不能**在组件上添加修饰类,只能在元素上添加修饰类。

### 5. 基于组件修饰类调整元素样式

如果需要以相同的方式调整同一个组件内的多个元素,可以在组件的基类上添加修饰类,再基于这个修饰类来调整次级元素的样式。虽然增加了样式权重值,但修改组件更加简单了。

### 6. 类名含有多个单词怎么办?

使用双下划线与双连字符区隔块\_\_元素--修饰类的意图正是为此。因此:

- 使用**连字符**来区隔单词,保证样式类名全小写,**不使用驼峰命名法(Camel-Case)**
- 类名应该易于阅读,因此除非缩写是普遍可识别的,否则缩写并不建议使用

<!-- GOOD -->
prefix-block\_\_element-name--modifier-name

<!-- BAD -->
prefix-block\_\_elementName--modifierName

### 7. 如何表示组件状态?

很多组件具有多种状态,比如 tab 组件有激活状态、禁用状态。

- 使用独立的状态钩子来表示状态,状态类名以 t-is-开头,我们整理一套**推荐使用的状态类名**(见附)供君选用
- 设置状态样式时,状态类名**必须**与其作用的元素类名或者块类名联合使用(.a.b 形式),不能使用后代选择器、子选择器等其他方式设置样式,以减小对全局样式的污染
- 修饰类的作用是用来表示组件的另外一个变体,而非另外一种状态,所以状态**不使用**修饰符的形式(如 t-tabs\_\_tab-disabled)

<!-- GOOD -->
<style>
  .t-tabs {
  }
  .t-tabs__tab {
  }
  .t-tabs__tab.t-is-active {
  }
  .t-tabs__tab.t-is-disabled {
  }
</style>
<div class="t-tabs">
  <ul class="t-tabs__tablist">
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-is-active">tab 1</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-is-disabled">tab 2</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab">tab 3</a>
    </li>
  </ul>
</div>

<!-- BAD -->
<style>
  .t-tabs {
  }
  .t-tabs__tab {
  }
  .t-tabs__tabitem .t-is-active {
  }
  .t-tabs__tabitem .t-is-disabled {
  }
</style>
<div class="t-tabs">
  <ul class="t-tabs__tablist">
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-is-active">tab 1</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-is-disabled">tab 2</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab">tab 3</a>
    </li>
  </ul>
</div>

<!-- BAD -->
<style>
  .t-tabs {
  }
  .t-tabs__tab {
  }
  .t-tabs__tab-active {
  }
  .t-tabs__tab-disabled {
  }
</style>
<div class="t-tabs">
  <ul class="t-tabs__tablist">
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-tabs__tab-active">tab 1</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab t-tabs__tab-disabled">tab 2</a>
    </li>
    <li class="t-tabs__tabitem">
      <a href="" class="t-tabs__tab">tab 3</a>
    </li>
  </ul>
</div>

#### 常见状态类名

| **状态**  |           **类名**           |          **说明**           |
| :-------: | :--------------------------: | :-------------------------: |
|   加载    |         t-is-loading         |                             |
|   禁用    |        t-is-disabled         |                             |
|   成功    |         t-is-success         |                             |
|   报错    |          t-is-error          |                             |
|   警告    |         t-is-warning         |                             |
|   聚焦    |         t-is-focused         |                             |
|   选中    |        t-is-selected         |                             |
|   勾选    |         t-is-checked         |                             |
|   关闭    |        t-is-closable         |         是否可关闭          |
|   激活    |         t-is-active          |     比如当前 tab 选中项     |
|  当前项   |         t-is-current         | 主要用于 step,表示当前步骤 |
| 隐藏/显示 |   t-is-hidden/t-is-visible   |                             |
| 展开/折叠 | t-is-expanded/t-is-collapsed |                             |

#### 推荐常用单词

| **状态**  |           **类名**            |
| :-------: | :---------------------------: |
||            header             |
||             body              |
||            footer             |
| 容器/内容 |  container / wrap / content   |
|   侧边    |   side / sidebar / sidemenu   |
| 边框修饰  | xxx–bordered / xxx–borderless |

#### 图标类,以 info 举例

| **说明** |            **类名**             |
| :------: | :-----------------------------: |
|   默认   |           t-icon-info           |
| 其他颜色 | t-icon-info t-icon-info--danger |
| 其他尺寸 |      t-icon-info t-size-l       |

#### 尺寸类

|  **尺寸**  |      **类名**      |  **说明**   |
| :--------: | :----------------: | :---------: |
|    较小    |     t-size-xs      | extra small |
||      t-size-s      |    small    |
| 中(默认) |      t-size-m      |   middle    |
||      t-size-l      |    large    |
|    较大    |     t-size-xl      | extra large |
| 高度 100%  | t-size-full-height |
| 宽度 100%  | t-size-full-width  |
| 宽度自适应 | t-size-auto-width  |

doc文件下 git-noem.md文件

javascript
### git 提交规范

每次提交尽量只做一件事 没事多提交!
添加到混存区: git add .
将暂存区内容添加到本地仓库中: yarn run commit 或者 npx cz

1.选择 type(非必填)
以下为日志类别对照表:
feat: 新功能  |   fix: 修复    |  perf: 优化    |  refactor: 重构
style: 格式   |   revert: 回滚 |  build: 构建
test: 测试    |   ci: 集成     |  chore: 其它   |   docs: 文档
2.输入本次修改的范围(非必填)
What is the scope of this change: eg. 首页
3.输入提交的信息(必填)
Write a short, imperative tense description of the change: eg. 样式替换
4. 提交详细的描述信息(可选: 直接回车下一步)
Provide a longer description of the change: eg. 船盘 title 字体更改颜色
5.是否是一次重大的更改(可选: 直接回车下一步)
Are there any breaking changes?
6.是否影响某个 open issue(直接回车下一步)
Does this change affect any open issues?

#### git 常用命令

├─ git status 查看状态
├─ git pull 拉取远程代码
├─ git push 推送代码
├─ git reset HEAD~ 撤销暂存区内容
├─ git checkout xxx 切换版本
├─ git checkout -b xxx 创建一个分支并切换
├─ git merge xxx 合并代码
├─ git branch xxx 创建分支
├─ git branch -d xxx 删除分支
├─ git branch -v 显示所有分支
├─ git reset --hard commitId 回滚版本所对应的 commit SHA
├─ git push --force 此时本地代码落后于远程代码

doc文件下 naming-convention.md

javascript
### 命名规范

1. 目录名
  参照项目命名规则,有复数结构时,要采用复数命名法。
    eg. docs、assets、components、directives、mixins、utils、views
2. HTML 文件名
  全部采用小写方式, 优先选择单个单词命名,多个单词命名以下划线分隔。
3. CSS 文件名
  全部采用小写方式, 优先选择单个单词命名,多个单词命名以短横线分隔.
4. js 文件名
  全部采用小写驼峰式命名.
5. 图片命名
  全部采用小写方式, 优先选择单个单词命名,多个单词命名以下划线分隔。
  图片业务(可选)+ 图片功能类别(必选)+ 图片模块名称(可选)+ 图片精度(可选)@2x | @3x
  字体图标 统一放置/assets/icons/svg 采用svg-icon组件进行显示
  eg. logo_national.gif cargo_disc_xxx.png
6. js命名
  6.1 变量
    camelCase 类型 + 对象描述或属性的方式。 let mySchool = "我的学校"
  6.2 常量 全部大写下划线分割 使用大写字母和下划线来组合命名, 下划线用以分割单词
  6.3 方法
    camelCase 统一使用动词或者动词 + 名词形式
    私有方法下划线开头
      eg. jumpPage setFormVal _initRules
    ps:
      canXxx 判断是否可执行某个动作(权) 函数返回一个布尔值。true:可执行;false:不可执行
      hasXxx 判断是否含有某个值 函数返回一个布尔值。true:含有此值;false:不含有此值
      isXxx 判断是否为某个值 函数返回一个布尔值。true:为某个值;false:不为某个值
      getXxx 获取某个值 函数返回一个非布尔值
      setXxx 设置某个值 无返回值、返回是否设置成功或者返回链式对象

7. Vue 相关命名
  7.1 页面命名
    优先选择单个单词命名,多个单词命名驼峰式连接.
  7.2 组件命名
    业务组件: 单词大写开头 (PascalCase)
    基础组件: 采用 kebab-case
      ps: 模板中业务组件大写开头 快速区分; 基础组件 采用 kebab-case
    命名顺序: 组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾
      eg. SearchForm, SelectPort
  7.3 路由命名
    页面路径采用 kebab-case 格式 path: '/user-info'
  7.4 自定义事件
    采用 kebab-case 的事件名, 便于区分原生事件和自定义事件。
      eg. this.$emit('my-event') <MyComponent @my-event="handleDoSomething" />
  8. 特殊规范
    ref: camelCase命名方式,以ref结尾 eg. formRef, chartRef
    hooks: camelCase命名方式,以use开头Hook结尾
      eg. useTableParamsHook
    store: camelCase命名方式,以use开头Store结尾
      eg. useUserInfoStore
    dialog: 弹层model变量以dialog结尾
      eg. xxxDialog: true
    api: 调用某一接口以接口模块开头、Api结尾; 便于区分方法和接口
      eg. userApi.xxx()
  9.每个组件文件代码总行数不要超过 500行
    建议vue组件不要超过500行,既然是组件化开发,那么如果一个组件文件体积太大,存在几十个方法、几十个 data数据,那就说明这个组件大概率包含的功能点太多,是可以被继续细化出多个单一功能的子组件

  10.每个函数不要超过 100行
    不要让一个方法函数包含过多的逻辑功能,函数包含过多逻辑,容易混乱,遵循函数功能单一原则

doc文件下 plugin-library.md文件

javascript
### vscode plugin

| 插件名称                      | 用途                |
| :---------------------------- | :------------------ |
| Vue Language Features (Volar) | vue3 开发工具(必装) |
| ESLint                        | 代码规范(必装)      |
| prettier                      | 代码格式化(必装)    |
| Stylelint                     | -(必装)             |
| Color Highlight               | 颜色高亮工具        |
| Error Lens                    | 开发报错显示        |
| Vue Peek                      | 引入跳转工具        |

### 第三方库

| 第三方库名称   | 官网                                               |
| :------------- | :------------------------------------------------- |
| vue3           | https://vuejs.org/                                 |
| ant-design-vue | https://www.antdv.com/components/overview-cn       |
| pinia          | https://pinia.web3doc.top/                         |
| dayjs          | https://dayjs.fenxianglu.cn/                       |
| echarts        | https://echarts.apache.org/handbook/zh/get-started |
| echarts-demo   | https://www.isqqw.com/                             |

doc文件下 ts-noem.md文件

javascript
### ts 注意事项[参考]

1、变量定义
const num = ref(0)
const str = ref('string')
const bool = ref(false)
const array = ref([]) | ref<[]>() | ref<XXX[]>() | reactive([])
const obj = ref({}) | ref({ xxx: String })
enum xxxEnum {
  已下架 = 1
  进行中
  已上线
}


2、类构造函数对象参数不使用any,同时继承父类和接口的办法
interface BaseInterface {
    getTableData(params?: unknown): unknown
    postFormData(params: unknown): unknown
    putFormData(params: unknown): unknown
    deleteRecord(ids: number[] | string[]): unknown
}

class BaseXXXClass extends BaseApiClass implements BaseInterface {
    id = 0
    readonly xxx = 'xxx'

    constructor(data: Partial<BaseXXXClass> = {}) {
        super()
        Object.assign(this, data)
    }
}

3、简写空方法
handleXXXClick(): void {
    return;
}

4、unknown类型遍历键值
export const paramsFix = (query: unknown) => {
  type typeofKey = keyof typeof query;

  if (typeof query === 'object' && query) {
    const keys = Object.keys(query);
    keys.forEach((key: string) => {
      const keyValue = query[key as typeofKey];
      if (keyValue !== undefined && keyValue !== '') {
      console.log(keyValue);
      }
    });
  }
}

README.md文件

javascript
# Testing Web

## 项目说明
| 项目由 vue3 + vite + vue-router + element-plus + sass + ts 搭建
| 采用 unplugin-auto-import 和 unplugin-vue-components 做自动导入
| vue 等相关生命周期可直接使用

### 安装

| 采用yarn进行node包管理
| yarn 安装依赖
| yarn dev 运行开发环境
| yarn build 打包发布

### 资源引入顺序

| 第一级: vue 相关引入(放在最前面)、第三方插件的js、css
| 第二级: 组件引入
| 第三级: 接口 api、store、hooks、utils、私有方法

### [命名规范(click here)](./doc/naming-convention.md ':include')

### [css 规范(click here)](./doc/css-naming.md ':include')

### [ts 规范(click here)](./doc/ts-noem.md ':include')

### [git 规范(click here)](./doc/git-noem.md ':include')

### [插件和库(click here)](./doc/plugin-library.md ':include')

八、vite.config.js

vite.config.js 是在使用 Vite 构建工具时的配置文件,它允许你对项目的构建行为进行自定义,并且提供了一些属性可以进行配置。

  • root:指定项目的根目录路径。

  • base:指定项目的基本 URL 路径,用于在构建生产版本时确定发布位置。

  • mode:指定构建模式,可以是 "development" 或 "production"。

  • publicDir:指定静态资源目录,例如图片、字体等。

  • server:配置开发服务器的选项,比如主机、端口等。

  • build:用于配置构建相关的选项,比如输出目录、压缩选项等。

  • optimizeDeps:用于配置依赖项优化相关的选项,比如预构建依赖项等。

  • plugins:允许你使用插件来扩展 Vite 的功能,比如添加特定类型的文件处理器等。

vite-config

后续我们会对此文件进行配置,这里仅先介绍。

九、yarn.lock

yarn.lock 文件是 Yarn 包管理工具自动生成的锁文件,用于记录当前项目中所有依赖包的精确版本信息。它的作用主要包括:

  • 锁定版本:yarn.lock 文件中记录了每个依赖包的确切版本号,这样可以确保在不同的开发环境中使用相同的版本,避免因为依赖包的版本不一致而导致的问题。

  • 提供一致性:保证项目的每一次构建都使用相同版本的依赖包,从而确保整个项目的一致性和可重复性。

  • 更快的安装速度:由于 yarn.lock 文件记录了特定版本的依赖包的下载链接,Yarn 可以利用这些信息来加速依赖包的安装过程。

yarn-lock

总结

本文简要介绍了使用 Vite 初始化项目之后,目录各个文件的作用,后面我们也会增加很多项目的配置文件以及对现有配置文件的修改,上文中 README.md 的配置可在 github 仓库中直接copy,仓库路径:https://github.com/SmallTeddy/ProjectConstructionHub