构建Vue.js应用程序的最佳方式是什么,以使其能够扩展并保持可维护性和可扩展性,越长越好?这是一个我在许多场合听到的问题,我认为这个问题的一个答案在于可预测性原则。当涉及到创建一个可扩展的项目时,你希望关于它的一切都尽可能的可预测。
我所说的可预测性到底是什么意思?最简单的是,它是指能够直观地从一个功能请求或错误报告到代码库中可以解决该任务的位置的能力。此外,它是一种能力,可以迅速知道你在代码库中的那个位置可以使用什么工具,以完成手头的任务。
为什么这很重要?好吧,像我一样,你可能有过这样的经历:继承或被介绍到一个现有的项目,然后在第一个任务中,打开代码库并思考。"我甚至不知道该从哪里开始!"。
你甚至可能已经和一个代码库打了一段时间的交道,也有同样的想法。一个可预测的代码库可以尽可能地减轻这种体验,使向开发人员介绍项目更容易,继续工作更有效率。
我认为这里值得注意的是,虽然可预测性是可能的,但没有一个项目会是100%可预测的。每个项目,不管是新的还是现有的,至少都会有一个轻微的学习曲线。另外,要知道,可预测性并不意味着代码库或应用程序作为一个整体可以迅速理解。许多大规模的应用简直太复杂了,这是不可能的,它们需要时间来整体掌握。因此,可预测性并不是指看到完整的成品拼图,而更像是知道某一块的形状,并能迅速看到它的位置。事实上,一个好的代码库的性质适合于每次都能理解一块,而不应该要求它的开发者一次就考虑到整体。
那么,我们如何在代码库中实现可预测性?答案是:标准,简单明了。也许这不是你要找的答案,但这是事实。使任何东西都可预测的最好方法是使它遵循一套标准。例如,我几乎可以百分之百地预测,我今天刚买的新的全尺寸床单会适合我的床,尽管我以前从来没有用这些床单装过。为什么?因为有床单的标准尺寸系统。
社区范围内的可预测性标准
那么这就引出了一个问题,在整个Vue.js社区存在什么样的标准?我想说的是,目前有4个标准来源。
- Vue.js风格指南
- 由Vue CLI生成的脚手架
- Vue.js的官方库(在Vue.js网站的Ecosystem > Official Projects下可以找到)。
- 以及更松散的,最流行的组件框架,如Vuetify或Quasar
虽然其中一些是官方的标准,但我认为它们都提供了一个机会,让项目和开发者之间有一些共同的模式,从而使代码库更加可预测。
官方库和组件库
让我们先来谈谈官方库和流行的组件库所带来的标准化。虽然这类库的主要目的是带来功能,但其副作用是采用共享标准。例如,如果你使用Vue Router,你不仅受益于软件包本身的功能,而且你最终在一个项目中实现路由的方式与你在另一个项目中实现路由的方式基本相同,与全世界的Vue.js开发者实现路由的方式基本相同。
事实上,Vuex库拥抱并吹捧这一事实,将自己称为 "状态管理模式+库 "的首要特征。
这似乎是显而易见的,但有一点是需要指出的。如果在Vue.js中有一个现有的流行或推荐的问题解决方案(如果是官方的解决方案,那就更不用说了),在使用其他东西之前,我会仔细考虑。我和其他人一样,很乐意自己动手制作组件、商店等,但从长远来看,使用久经考验的解决方案确实是值得的,不仅因为它们提供的功能、测试覆盖率和文档,还因为它们带来的标准化。(在JavaScript的世界里,我们难道不能使用更多的标准化吗😆)。
当谈到选择使用这些更加标准化的解决方案时,重要的是要记住你所构建的是什么。你在构建一个可扩展的可重复使用的组件吗?那么也许标准库并不适合你,因为一个新的标准库是你试图建立的东西。然而,这可能不是我们大多数人正在构建的东西。我们中的大多数人可能正在构建一个应用程序,如果是这样的话,那么最好使用已经存在的标准(或至少是半标准)组件作为你的构建块。
标准文件结构的雏形
说到项目标准,文件结构是一个经常被谈论的话题,虽然Vue没有文档规定特定的结构,但它确实从Vue CLI生成的代码库中提供了一个良好的起点。
我们中的大多数人可能对这种结构很熟悉,这很了不起!这意味着我们离成功更近了一步。这意味着我们离可预测性又近了一步!所以,这里的重点是,不要想太多。坚持使用Vue开箱即用的东西,除非你有一个(真正的)好理由,否则不要偏离它。
我当然认为在这里可以做一些明智的补充(我们将在一分钟内进一步讨论这些),但没有真正的理由对已经存在的东西进行修改。有了这个自动生成的文件结构,我们就有了一个可预测的应用资产、页面、组件、路由、存储逻辑的地方,以及一个清晰的入口。不要搞乱一个可预测的好东西。
组件推荐规则
现在,专注于组件的目录,Vue风格指南为我们提供了一些进一步的建议,使我们的文件结构更加可预测。在其他方面,风格指南鼓励在定义组件时要做到以下几点。
- 如果可能的话,每个组件都应该定义在自己的专用文件(SFC)中
- 单一文件的组件应该用PascalCase来命名
- 基本组件应该以相同的前缀开始(如
Base或App)。- 你可以把基础组件看作是你的应用程序范围内可重复使用的组件,比如一个按钮或一个模式。
- 这样就可以把它们组合在一起,并声明它们的全局性,可重复使用的性质。
- 组件的名字应该是多字的,以避免与任何现有或未来的HTML元素相冲突。不要创建一个
Table,也不要创建一个Button的组件。 - 单一实例组件应以前缀开始
The- 例如,一个网站的页眉或页脚
- 这样就把它们归为一组,并宣布它们为单一用途。
- 紧密耦合的子组件应以其父组件的名称为前缀
- 例如,一个
TodoListItem在TodoList - 这就把它们分组,并声明它们是相关的
- 例如,一个
- 组件名称应该以最顶层(通常是一般)的词开始,以最具体的词结束
- 例如
SearchWidgetInput,SearchWidgetResultsList。SearchWidget - 这就把文件结构中的相关组件分组在一起
- 例如
如果这些不完全合理,请抓紧时间,一分钟后会有一张图片,可能会有帮助 🙂。
除了这些,完整的风格指南还有一些其他的标准,这些标准将帮助你的项目对社区范围内的开发者来说更容易预测。我不会在此一一赘述,但强烈建议你自己阅读并遵守该风格指南。
一些推荐的个人/团队范围内的可预测性标准
虽然官方为Vue.js社区制定了一些很好的标准,但根据我的经验,还有一些没有被广泛采用的模式,对你或你的团队的项目也同样有帮助,并能成为标准。这样的标准是必要的,因为社区范围内的标准并不是100%全面的,但是当涉及到如何决定和维护团队标准时,要小心和严格......如果你不小心,它可能是一个规则不断变化的兔子洞。下面是我对Vue.js项目标准的一些建议。
扁平的组件目录
你可能已经注意到了Vue风格指南中大多数组件规则的一个共同点。命名规则总是有助于在文件系统中把相关的组件组合在一起。正因为如此,结合下面的原因,我建议采用扁平组件目录的标准。一个扁平的组件目录有以下好处。
- 快速、轻松地从Vue开发工具中发现一个组件到在代码库中找到该文件(文件名和组件名是一样的)。
- 使用你的IDE的快速查找或文件跳转功能,根据其最一般的属性过滤文件,直至更具体的属性
- 在决定如何将组件组织到子目录中时,消除分析瘫痪现象
- 能够在一个单一的列表中一次看到你的所有组件
- 摆脱文件名和目录中关键词的冗余(如果你遵循风格指南(你应该这样做)并且你使用嵌套目录的话)(即:
post/PostList.vue,post/PostFeature.vue,等等)。 - 消除使用简短的单字组件名称的诱惑,这在嵌套目录中更容易做到(即:
post/List.vue,post/Feature.vue),并且违反了风格指南的规定。 - 消除在目录内和目录外浏览文件结构以寻找组件的现象
- 简化了组件的导入(永远是
import SomeComponent from "@/SomeComponent")。
那么,遵循风格指南的扁平结构是什么样子的呢?这里有一个很好的例子。
虽然你的大规模应用显然会有更多的文件,但每一个文件都只是一个组织良好的列表中的另一个组件名称,所以虽然文件结构的范围可能会扩大,但复杂性并没有。
标准化的路线/页面命名公约
另一个有意义的做法是用标准化的方式来命名我们的路由和页面组件。在你典型的CRUD应用程序中,你有以下不同的页面,用于每个资源。
- 一个所有资源的列表
- 一个单一资源的视图
- 一个创建资源的表单
- 编辑资源的表单
虽然其中一些可能最终成为一个嵌套的路由(比如在列表页中以模式叠加的方式查看单个资源),但它们通常最终会有一个专门的路由和一个相应的页面。
由于我有PHP框架Laravel的背景,当涉及到命名路由和以可预测的方式定义它们的路径时,我直觉地回到了Laravel已经有的标准上。这使我有经验的Laravel团队更容易快速和直观地与Vue合作。以 "用户 "资源为例,我推荐的Laravel规定的、适用于Vue的命名规则如下。
| 路径 | 路径和组件名称 | 它的作用 |
|---|---|---|
| /users | UsersIndex | 列出所有的用户 |
| /users/create | 用户创建 | 创建用户的表格 |
| /users/{id} | 用户展示 | 显示用户的详细信息 |
| /users/{id}/edit | 用户编辑 | 编辑用户的表格 |
虽然我想用更传统的Laravel方式来命名路由,比如users.index ,而不是UsersIndex ,但我发现使用PascalCase的方式也很好,而且还有与组件名称匹配的好处。
为了进一步提高一致性和灵活性,当你在路由器链接中使用你的路由时,以及以编程方式引用它们时,你也应该总是通过它们的名字来引用你的路由。比如说
<router-link :to="{name: 'UsersIndex'}">Users</router-link>
另外,值得注意的是,并不是所有的路由都完全符合这个模式,因为有些路由比其他路由更 "CRUDdy"。对于那些不符合这个模式的路由,我唯一的建议是,你继续使用PascalCase来命名你的路由以保持一致性。
一个更全面的文件结构
除了Vue CLI给你提供的基本文件结构外,我建议将以下内容标准化,以提高可预测性。
这里增加的目录是文档、助手、布局、混合器和插件。你会注意到5个中有4个旁边有一个由VS Code扩展Material Icon Theme提供的花哨的图标。这是因为在某个时候,对于某些框架或语言来说,这些目录约定是很常见的,在扩展开发者的眼中,它们有自己的图标。这不是巧合!
我还添加了一个文件globals.js。
那么,这些文件结构标准背后的原因是什么呢?好吧,我很高兴你问了!
docs
这个文件的目的是显而易见的,但更重要的是,它被包括在内,并且在你的团队每次打开代码库时都坐在那里盯着他们的脸。如果开发者不必离开他们的IDE,那么项目的某些方面就更有可能被记录下来。我还发现(这是一个惊喜),在编码一个可重用的类或组件之前先写文档,通常能帮助我更好地设计上述代码的界面或API。来吧,我敢说你可以试一试!
另外,除了docs目录,我发现在每个标准化目录的根部提供一个README.md,解释该目录的目的和任何应该包括在其中的规则,这很有帮助。这对那些不是社区范围内的标准尤其有帮助。
辅助工具
这是许多框架中常见的目录,用于基本的输入-输出类型的函数,这些函数在整个项目中是可以重用的。它们通常很容易进行单元测试,并且通常最终会被多次使用。我喜欢从一个单一的index.js 文件开始,然后随着助手的增加,把它们分成更多的分组文件,如https.js,cache.js,time.js ,等等。这个目录中的所有东西都可以直接导入并按需使用,如果一个功能最终根本没有被使用,它可以很容易地从生产捆绑中树起来。
布局
我从Nuxt以及Laravel中提取了这个惯例。它不仅可以方便地定义页面组件,而且可以在多个页面上重复使用的布局组件。这些组件不是定义页面的内容,就像它的名字一样,而是更多的定义一般的布局。例如,它是一个单栏还是双栏页面?它有一个左侧边栏还是右侧边栏?布局是否包括典型的页眉和页脚,或者是一个完全空白的布局,也许页面内容绝对居中?通常情况下,这些布局组件只有2到3个,但尽管如此,它们也是很方便的抽象概念。
混合元素
这个目录是用来组织你所有的Vue.js混合组件。我认为在每个文件名的末尾加上Mixin这个词是很重要的(比如ResourceTableMixin.js ),以便在你的文件切换器中方便搜索。虽然我还没有机会真正在一个更大规模的Vue 3项目上工作,但我认为这可能会很快变成一个composables 目录,因为更倾向于用Composition API而不是用mixin来提取反应式数据/方法。或者至少在我的标准文件结构中,除了mixins 目录外,还可能增加一个composables 目录。
插件
我喜欢为我所有的Vue项目包括的最后一个目录是plugins 目录。在包和库的世界里,我们有时最终做的配置和注册比我们做的实际编码更多。这就是这个目录的作用,包括并设置所有第三方的Vue东西。虽然它被称为插件,但我并不总是在最严格的意义上使用这个词。换句话说,它不一定是通过Vue.use() 方法注册的第三方lib。很多时候,它是,但其他时候,它使用其他的方法来设置Vue的lib(比如.component() )。对于那些只需要一两行设置的库,我会把它写在一个plugins/index.js 文件中。对于那些需要更多设置的lib,我喜欢在plugins目录下为它们创建一个专门的文件,然后将其导入到plugins/index.js 。
globals.js
这是我真正添加过的唯一的标准文件。它的目的是为Vue应用添加数量有限的全局变量,对于只在客户端运行的SPA来说,通常是指窗口。
这就是通常装饰在这个文件顶部的注释。
/**
* Use this file to register any variables or functions that should be available globally
* ideally you should make it available via the window object
* as well as the Vue prototype for access throughout the app
* (register globals with care, only when it makes since to be accessible app wide)
*/
在Vue 2中,这可以这样做。
Vue.prototype.$http = () => {}
在Vue 3中,它看起来像这样。
const app = createApp({})
app.config.globalProperties.$http = () => {}
尽管我一直被警告要注意全局变量的危险性,但我曾经读到过,"少量的全局变量 "是一件非常方便的事情,从那时起,它就被证明对我很有用。这个文件让我很容易知道这些全局变量是什么,并且让我在想要添加更多的全局变量时不必思考。
可预测性的归纳
虽然有一些社区范围内的标准是你最好不要忽视的,但也有一些标准是你可以为自己或你的团队制定的,以便使你的代码库更加可预测。虽然上面提到的一些标准已经被证明对我很有用,但可能还有其他的标准对你或你的团队很有效。最重要的是在不同的项目中坚持使用这些标准,这样它们就能达到它们的目的。
虽然可预测性的标准对你的大规模Vue.js应用来说是一个很大的好处,但仍有更多可以做的。请务必查看本系列的下一篇文章,我们将深入研究像ESLint和Prettier这样的提示和格式化工具,以保持你的代码干净、无错和一致。
