前端项目目录结构演变

3,035 阅读9分钟

在工作过程中随着我们的业务越来越庞大,对于项目的管理和维护带来一定的难度,特别是团队中的人员存在异常变动的时候,就会出现一些问题,项目中的问题就会出现浮现出来,可能其他人写的代码在添加需求或者更改需求的时候需要调整的时候,不知道该从哪里开始入手。

其实对于一个团队来说一个良好的项目结构和编程习惯无论对于团队还是个人来说都是一个很大的成长。这里就简单的说在公司工作进三年的时间公司项目的目录结构是如何不断迭代的。其中也做了很大的改变。

我们公司无论前端还是后端都是使用的是微服务进行架构的,为了减低项目与项目之间的依赖性。在团队创建初期公司已经有了目录结构的雏形,毕竟团队刚刚成立没有多久。前端部分主要采用的是先在较为流行的Vue,本文对于目录结构的演变同样也是针对Vue项目进行延申的。

目录结构雏形

在最开始创建项目初期做项目的时候整个项目情况还是很糟糕的,数据请求都是写在**.vue文件中去请求,项目整体没有统一的规划,导致项目一旦某些内容发生改变的时候,很有可能导致牵一发而动全身。对于项目的维护来说简直可以用一塌糊涂来形容。

然而为了解决这些问题,所以对目录结构进行了第一次规划,其实这次对于目录规划主要参考了Vuex官网中的推荐的目录结构。

├─src               //  项目目录
│  ├─api                //  数据请求
│  ├─assets             //  资源
│  │  ├─baseData            //  静态数据
│  │  └─images              //  较小图片资源
│  ├─components         //  组件
│  │    ├─base              //  基础组件
│  │    └─business          //  业务组件
│  ├─routes             //  路由
│  ├─mixins             //  混入
│  ├─store              //  状态管理
│  ├─style              //  样式
│  ├─views              //  页面
│  ├─utils              //  工具
│  └─main.js            //  入口文件
└─static            //  静态资源

这种项目结构来说算是单页面中比较中规中矩的了,每个文件夹中都自己做了自己的事情,对于整体项目的维护也相对来说变得容易了一些。

  1. api:主要负责提供数据请求的方法
  2. assets:提供业务中所需要的数据资源以及较小的图片资源,vue-cli会把较小的图片编译成base64
  3. components:承载了业务中所有需要用到的组件,使用basebusiness对基础组件进一步划分
  4. routes:编写路由结构
  5. mixins:公用代码的混入
  6. store:页面中的状态管理
  7. style:页面和组件样式
  8. views:存放页面
  9. utils:页面或组件中所需要用到的工具,以及对于其他第三方工具的二次封装
  10. main.js:程序的入口文件
  11. static:较大的静态资源文件

虽然通过上面的对于项目进行了调整但是随着业务的发展项目还是会越乱,乱的地方出现在哪里?

  • 第一点,所有页面的业务组件全都存放在了一起,查找起来不是特别的方便
  • 第二点,所有页面都存放在了views中,比如用户管理可能会有用户详情,修改,新增,然而这些又和其他的业务页面存放在了一起
  • 第三点,就是router里面的内容业务越多,业务就越庞大,这对于项目来说也是一个很大的负担

其实对于上述问题都不是什么特别大的问题,都可以使用文件或者文件夹来进行拆分使每一部分业务都做到相对的独立。

改进

在进行之前做了很大的考虑,首先就是各个模块之间的存放问题,虽然使用文件夹进行划分可以解决这部分问题,但是随之带来的是,就是资源加载问题,比如A模块中不需要用的router,但是需要用store,但是B模块又需要用到router,不需要用到store,然而这就在加载页面资源的时候相对的会加载一些不需要的资源。经过反复的尝试,最后使用多页面与单页面相结合的形式。只在需要用到的某些资源的时候才会在vue实例中使用其对应的资源。

├─src                   //  项目目录
│  ├─api                    //  数据请求
│  ├─assets                 //  静态资源
│  │  ├─baseData                //  静态数据
│  │  └─images                  //  较小图片资源
│  ├─component              //  业务组件
│  ├─domain                 //  业务页面
│  ├─mixins                 //  混入
│  ├─instance               //  页面实例
│  ├─middleware             //  中间件
│  ├─publicComponents       //  公用组件
│  │    ├─base                  //  公用基础组件
│  │    └─business              //  公用业务组件
│  ├─routes                 //  路由
│  ├─views                  //  页面
│  ├─store                  //  状态管理
│  ├─styles                 //  样式
│  └─utils                  //  工具
└─static                //  静态资源

通过上面的目录结构的调整,整个项目中的内容变得更加的清晰了。加入domain主要是想要达到页面的表现和业务的逻辑相互分开,对于业务的处理放到domain中进行处理。

为了节约篇幅只说明改动文件作用

  1. component:只存放页面中所依赖的业务组件,文件夹内部使用文件夹对页面与页面的划分
  2. domain:用于处理业务部分,比如数据提交前的处理,数据请求前的数据处理
  3. instance:页面的实例,多页面所有,内部使用文件夹对页面进行划分
  4. middleware:Vue实例中通用的中间件,内部使用两个文件夹进行拆分,分别是middleware.style.jsmiddleware.javascript.js
  5. publicComponents:所有页面公用的组件,文件夹内部分为basebusiness对业务组件和基础组件进一步划分

这里有一点需要注意的是,对于instanceroutes的理解和认知,instance是以业务模块为单位,然而routes是一模块功能为单位的。举个栗子,instance中所划分的是业务A的相关内容,但是这部分业务很少,只有一个页面,那么这个时候就不需要用到routes业务B内容则很多那么就需要使用routes对其中的内容进一步的划分。

说白了instance的作用就是,对业务与业务之间的划分,使两个业务虽然在同一个项目中也做了划分处理。然而routes则是在业务的基础上进行了二次划分。本该属于该业务的只在该业务模块所管辖范围内进行处理。

在系统的开发过程中随着项目中的内容日益的增多,会普遍的带来一个很大的问题,当views中的文件业务特别多的时候就需要在页面中定义很多很多的方法,以及对于业务的处理。当去修改代码的使用,那感觉简直不要太爽,一个文件上前行代码,而且方法相互之间的依赖性谁都离不开谁,哈哈哈,这感觉太香了。而且在开发过程中,写个函数需要频繁的上下滚动。为了解决这个问题最后考虑到页面中的内容较多的问题,为了实现,结构和行为相脱离使用混入的形式。

在混入文件中添加对页面业务的混入,views文件中JavaScript根据页面中的业务放到对应的***.js文件中,减少页面中的业务处理的js代码。在调整之后需要在miaxins中去引入domain文件了。

最终版本

为了实现结构与表现的分离,上面虽然使用混入间接的解决了这个问题,但是违背了混入的最初的用意,久而久之这也不是什么长久之计,还是需要对内部进行调整,在没有出现domain之前,全部都是写在***.vue中,那么可不可以直接提取这部分内容直接注入到页面中呢?经过尝试是可以的,那么抽离的这部分也就是JavaScript仍然还是很臃肿。

其实domain不但控制了页面还是控制了行为,对domain的内容进行分解分为controllerservice页面表现全部都放入controller统一处理和管理,service则负责单个业务的处理和页面数据的管理。

这里对Vue脚手架进行了升级,使用了Vue3.0脚手架。

├─api                   //  数据请求
├─assets                //  静态资源
├─components            //  组件
├─controllers           //  控制层
├─instance              //  页面实例
├─middleware            //  中间件
├─mixins                //  混入
├─publicComponents      //  公共组件
│  ├─base                   //  基础组件
│  └─basic                  //  业务组件
├─routers               //  路由
├─services              //  业务处理
├─style                 //  样式
└─views                 //  页面结构

前端同学应该都知道前端通过结构(HTML),表现(Style),行为(JavaScript)来完成整个页面的,然而这次的调整是对原有domain的二次划分,使整个页面结构、样式、行为脱离。

可能有的同学会问,那么servicecontrollerview他们之间是如何关联在一起的呢?其实这里使用的es6class在页面创建的时候把controller和相关service引入到页面中,在页面创建时,同时创建controller,在controller实例化的时候把service以参数的形式注入到controller中。

为什么要这样设计,当某一部分业务需要暂时调整的时候就可以直接再创建一个新的类注入进去即可,不需要更改原有代码,当业务需要变更回来的时候,直接要更换一下注入进去的service即可。

test.vue

<template>
    <div>
        <p>{{controller.pageService.page}}</p>
        <p>{{controller.pageService.size}}</p>
    </div>
</template>

<script>
import TestController from "@/controllers/test/view/TestController";
import TestService from '@/services/test/view/TestService';

export default {
    data:() => ({
        controller: new TestController(
            new TestService();
        )
    })
}
</script>

TestController.js

export default class TestController {
    constructor(pageService){
      this.pageService = pageService;
    }
}

TestService.js

export default class TestService {
    constructor(){
        this.page = 1;
        this.size = 20;
    }
}

通过对domain的拆分之后项目整体来说无论是对页面的调整还是拓展都变得更加的容易的了,无论业务怎么变更,即使是换套业务逻辑也不需要更改原来的代码只需要调整注入进去的实例即可。目前来说还是蛮香滴。

总结

本文记录一下对于项目结构的整体调整以及考虑,主要目的为了达到整体结构、表现、行为的相对脱离。通过对目录结构的调整,对于整体项目的维护与拓展有了更高的可控性。

文章略有潦草,若有什么问题请在文章下面留言。针对其业务单独写了脚手架qj-web-cli欢迎大家下载。