图:Mathieu L.B
开发一个应用最难的事情就是给各种东西起名字,还有就是用什么方法组织各种东西的文件。今天跟大伙分享一个经过我实践之后的一个 Vue 项目结构。Vue 是一个前端应用框架,它只给我们制造零部件的方法与材料,但是并没告诉我们项目如何组织。好处是这很自由,怎么整都行,坏处也是很自由,因为我们自己要做很多决定,每次决定都会消费一点脑力,所以一开始你会很头痛。
语言
我在 Vue 项目里使用了 TypeScript,这比直接用纯的 JavaScript 开发的项目复杂一点点,你需要考虑各种东西的类型。但我觉得一开始不需要想太多类型的事儿,只要编辑器不报类型的错误,我就不管类型的事儿。加上 Vue 官方暂时还没有提供关于用 TypeScript 开发的文档,所以可以再等一等。不过我们还是可以在项目里用一下 TypeScript,会有一些好处,但不用太费力气研究类型。
目录结构
应用整体相关的东西我会把它放在 app 这个目录里面,应用里的资源我会单独放在各自的目录下面。
应用目录
我会把应用整体相关的东西全部放在 src/app 的下面,比如应用的路由器,路由守卫,Store,Store 插件,应用配置等等。每种东西我只会创建一个文件,文件名我会用 app 作为前缀,后面接着零部件的名字,比如 app.router 文件里定义的就是应用的路由器,app.store 里面定义的是应用的 Store。
├── app │ ├── app.config.ts │ ├── app.mixin.ts │ ├── app.router.guard.ts │ ├── app.router.ts │ ├── app.routes.ts │ ├── app.service.ts │ ├── app.store.plugin.ts │ ├── app.store.ts │ ├── app.vue
app.vue 是应用的根组件(Root Component),创建 Vue 应用的时候会用到这个组件。app.config 是应用里面需要的一些配置,在应用里如果需要配置信息,可以导入使用在这个模块里导出的配置信息。
组件
在应用全局范围或者经常用到的一些组件可以放在 src/app/components 目录的下面,比如 app-icon 是应用的小图标组件,app-logo 是应用的标志。
├── app │ ├── ... │ ├── components │ │ ├── app-home.vue │ │ ├── app-icon.vue │ │ ├── app-logo.vue
零部件
应用的布局,或者在应用全局经常用的一些功能,可以放在 src/app 下面的一个单独的目录里面。比如在 notification 目录里面,定义了应用的通知提醒功能,目录里面包含了功能需要的组件,样式还有 Store。
├── app │ ├── ... │ ├── notification │ │ ├── app-notification.store.ts │ │ ├── app-notification.vue
资源目录
应用里的资源或者应用做的事情都可以单独放在各自的目录下面,如果你的应用里出现的资源并不多,可以直接在 src 目录下面创建目录。如果有很多资源,可以给它们分分组,然后在每个群组目录的下面再去创建独立的目录。
├── post │ ├── post.store.ts │ ├── post.service.ts │ ├── post.routes.ts │ ├── components │ ├── create │ ├── update │ ├── show │ └── index
在 src/post 这个目录里存放的是我应用里的内容资源相关的东西。post.store 是资源需要的 Store 模块,post.service 里面可以定义一些需要的服务方法,post.routes 定义的是资源相关的路由,components 目录下面是一些共用的组件。
动作/操作
根据对资源做的不同的事情,我又会分成几个不同的目录,比如 create 里面放的是创建 post 这种资源用的东西,update 指的是更新资源,show 里面的东西是资源的独立页面,index 里面的东西是资源列表。
│ ├── index │ │ ├── components │ │ │ ├── post-list-filters.vue │ │ │ ├── post-list-item.vue │ │ │ ├── post-list.vue │ │ │ └── styles │ │ │ ├── post-list-filters.css │ │ │ ├── post-list-item.css │ │ │ └── post-list.css │ │ ├── post-index.store.ts │ │ └── post-index.vue
每个动作目录里面又包含了组件,样式,还有 Store。在动作目录的根目录下面有一个主组件,比如在 src/post/index 的下面,就会有一个 post-index.vue 这个组件。在 src/post/create 的下面,会有一个 post-create.vue 。这些主组件一般会作为路由组件,比如你在 post.routes 里面定义了一条内容列表路由,路由对应的组件就是 post-index 。
资源动作需要的其它组件可以放在 components 目录里面,比如在 src/post/index/components 里面, 有 post-list,post-list-item 这些组件。在 post-index 里面会使用 post-list,在 post-list 里面会使用 post-list-item ,在 post-list-item 里面又会用到一些组件,这些组件都可以放在这个 components 里面。
样式
在单文件组件里,你可以直接在组件的样式放在 style 标签里面,不过我偏好创建独立的样式文件,文件的名字跟组件的名字保持一致,然后在 style 标签里使用 @import 导入这个样式表。样式表文件一般都放在组件当前目录下面的 styles 这个目录里面。比如 src/post/components/post-list.vue 这个组件的样式表,位置就是 src/post/components/styles/post-list.css 。
Store
我的这个应用使用 Vuex 管理应用的状态,所以需要创建一些 Store 。注意在实际的应用中,一个 Store 模块是不够用的,即使你把 getters,mutations,actions 分割成不同的模块,也是不够的,因为很快就会陷入混乱之中。我的做法是按资源或者要做的事情去创建多个 Store 模块,模块里使用命名空间,这样你就会清楚的知道在应用里需要的数据或者动作来自哪里。
应用的 Store 我分成了三个级别,应用、资源、资源操作。在应用 Store 里注册使用资源 Store,在资源 Store 里注册使用资源操作 Store 模块。
示例:
├── app │ ├── app.store.ts ├── comment │ ├── comment.store.ts │ ├── create │ │ └── comment-create.store.ts
comment-create.store 是创建评论需要用的 Store,comment 下面的 comment.store 是评论资源整体的 Store,在它里面会注册使用 comment-create.store 。app.store 是应用整体的 Store 模块,在它里面会注册使用 comment.store。
这样如果我想在组件里使用 comment-create.store 里面定义的 createComment 动作。位置就是 comment/create/createComment。 这个路径就说明了我在 app.store 里注册使用 comment.store 的时候,用的名字叫 comment,我在 comment.store 里注册使用 comment-create.store 的时候,用的是 create 这个名字。
在组件里,你可以使用 Vuex 提供的帮手方法,把 Store 里的东西直接映射到组件里使用,像这样:
methods: { ...mapActions({ createComment: 'comment/create/createComment' }), }
这样你就可以直接在 Vue 组件里调用 createComment 这个方法了,实际上执行的就是在 comment-create.store 里定义的 createComment 这个动作。如果你不用帮手方法做映射,也可以直接使用 $store 上的 dispatch 方法,派发这个动作,像这样:
this.$store.dispatch('comment/create/createComment');
路由
我会在 app 目录里面定义一个路由器(router),然后交给 Vue 应用使用。路由器需要的路由可以单独放在资源各自的目录下面,比如在 post 目录里面会有一个 post.routes.ts ,在这个文件里你可以定义跟内容相关的路由。定义之后在应用的路由器里面需要导入使用 post.routes.ts 里定义的路由。
src/post/post.routes.ts
/** * 定义路由 */ const routes: Array = [ { name: 'postIndex', path: '/posts', component: PostIndex, props: route => { return { sort: 'latest', filter: route.query, }; }, }, ]; /** * 默认导出 */ export default routes;
src/app.router.ts
import postRoutes from '@/post/post.routes'; /** * 创建路由器 */ const router = createRouter({ history: createWebHistory(), routes: [...postRoutes], });
配置
在 src/app/app.config.ts 里面的东西是应用相关的配置,内容大概像这样:
/** * 服务接口基本地址 */ export const API_BASE_URL = process.env.VUE_APP_API_BASE_URL;
在配置文件里可以导出一些配置,这些配置的值大部分都来自环境变量。你可以在项目根目录的下面,创建一个 .env.development,为开发环境准备一些环境变量,在 .env.production 文件里定义在生产环境中使用的环境变量。
VUE_APP_API_BASE_URL=http://localhost:3000
这些环境变量的名字要用 VUE_APP_ 开头,Vue 在编译的时候会自动把这些环境变量替换成真实的值。
结语
上面就是我要跟大家分享的 Vue 项目结构,我在一个 Vue 项目里使用了这个结构,感觉不错,希望能给大家一个参考。研究它也花了我不少功夫,不要小看它,每个细小的决定都需要花点时间想一想。
另外我基于这个结构开发了一个 Vue CLI 插件,可以快速使用命令生成需要的组件,还有 Store,还可以自动在指定的父辈那里导入使用这些新生成的东西,省去了很多事儿,后面我会整理一下,再跟大家分享一下这个命令行工具。
宁皓网的 Vue 课程已经开始了,你可以从头开始,学会使用 Vue 框架提供的各种东西,掌握用 Vue 框架开发应用的全部技能。欢迎大家订阅宁皓网,现在订阅可以多送一年时间,老用户重新订阅或者续订可以多送两年,谢谢大家:) 点击订阅 →