扩展大型 Vue.js 应用程序的 3 个技巧

2025-05-28

扩展大型 Vue.js 应用程序的 3 个技巧

TLDR;

  • 将应用程序拆分为完全独立的模块
  • 考虑微前端架构
  • 不要过度使用Vuex store

提示#1:思考模块!

给出的文件架构vue-cli很棒。但过了 3-4 页之后,你的文件就会开始变得臃肿。
你可能会想把组件分别放到多个文件夹中。但过了 10 页之后,你又会面临同样的问题。

这个想法是按概念拆分你的应用程序。坚持使用一个唯一的词。
例如,在一个商店应用中,我们可以获取CatalogBasketPayment。现在

├─ src/
│  ├─ core/
│  ├─ modules/
│  │  ├─ Catalog/
│  │  │  ├─ Components/
│  │  │  ├─ Pages/
│  │  │  ├─ Routes/
│  │  │  ├─ Services/
│  │  │  │  ├─ catalog.api.js
│  │  │  │  └─ catalog.services.js
│  │  │  ├─ Store/
│  │  │  │  ├─ catalog.action.js
│  │  │  │  └─ catalog.getters.js
│  │  │  │  └─ catalog.mutationTypes.js
│  │  │  │  └─ catalog.state.js
│  │  │  │  └─ index.js
│  │  │  ├─ Tests/
│  │  │  ├─ Catalog.vue
Enter fullscreen mode Exit fullscreen mode

这里有几件重要的事情:

隔离

为了保持良好的隔离,模块之间不应该互相依赖。我的意思是,它们Module A不应该与 共享组件Module B。对于常用功能(例如用户登录、用户语言……),您可以使用core文件夹!

智能组件与愚蠢组件

将智能组件Pages文件夹)与哑组件(文件夹)分开非常重要Components。简而言之:

  • 智能组件:可以访问商店、路由器、窗口对象……
  • 简化组件:接收 props,发出事件。就是这样!

这种方法的主要好处是可重用性、更好地分离关注点……

分裂,分裂,分裂!

如果你的组件太大,别担心:拆分它!在 Vue.js 指南中,“紧密耦合的组件名称”规则被强烈推荐。

// 👎 Bad
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue

// 👍 Good
|- CatalogList.vue
|- CatalogListItem.vue
|- CatalogListItemButton.vue
Enter fullscreen mode Exit fullscreen mode

隔离风格!

记住:全局 CSS 是邪恶的。尽量避免使用它们!

  • .vue文件:更简单快捷的方法是使用样式标签中的 scoped 属性:<styles scoped>
  • 不喜欢.vue文件?(如果你想创建多个小组件,但又不想创建一堆文件)。可以考虑使用 vue-styled-components之类的库。它的样式封装功能简直太棒了!
  import styled from 'vue-styled-components';

  const StyledTitle = styled.h1`
      font-size: 1.5em;
      text-align: center;
      color: palevioletred;
  `;

  <StyledTitle>Cool title</StyledTitle>
Enter fullscreen mode Exit fullscreen mode

测试

应用程序测试是一个非常复杂的话题,值得用一整篇博文来阐述。为了简单起见,我们先来看看之前的文件架构,看看需要测试什么以及如何测试。

  • 组件:低优先级,易于操作。为每个组件编写单元测试。这应该很容易做到。
  • 页面:高优先级,但很难实现。你可能需要模拟 API/浏览器部分。
  • 路线:通常这里不会有 bug。留到 E2E 测试再说吧。
  • 服务:
    • api接口:我个人不测试这部分(90%的代码都是模拟的)。
    • helpers/dataFormaters:优先级高,易于操作。通常也是应用中最容易做的测试!
  • Store:最难测试的部分。可以通过集成测试来测试。单独测试 action、getter 和初始状态是没有意义的。

💡 延迟加载你的模块!

为了减少 JavaScript 包,请考虑延迟加载模块

export default new Router({
  routes: [
    {
      path: '/catalog',
      name: 'catalog',
      component: () => import(/* webpackChunkName: "catalog" */ './modules/Catalog/views/Catalog.vue')
    },
    // ...
  ]
})
Enter fullscreen mode Exit fullscreen mode

如果您已经这样做了,但仍然认为您的应用程序太大,您可以考虑下一个提示。

提示 #2:考虑微前端

过去几年,微服务架构变得非常流行。许多公司将他们传统的单体式后端拆分成许多小型服务。

带有微服务的图像

如今,似乎有些公司已经将这种后端范式复制到了前端世界。其前景与后端非常相似:将庞大的单体应用拆分成多个应用程序,实现扩展,并能够使用不同的技术编写应用程序。是的,你读对了最后一句话。你可以用 Vue 编写支付应用程序,用 React 编写目录应用程序。

如果需要,不同的应用程序可以通过在窗口对象上注册的事件(观察者发布/订阅)进行通信。

带有微前端的图像

⚠️ 但我得提醒你,没有什么灵丹妙药。当你放弃一个范式而选择另一个范式时,你并不能摆脱问题,而是会取代它们。

👍 优点:完全隔离

如果后端也是面向微服务的,那么团队就可以各自为政,端到端地控制各自的领域。

👍 优点:能够使用不同的框架/一个框架的不同版本

假设负责 Catalog 的团队想要更新到 Vue 的下一个版本。不幸的是,负责支付的团队还没有准备好。他们必须提前交付一个重要的功能,并且需要在 3 个月内完成。
当应用程序被拆分成多个小应用程序时,它们就完全独立了。他们可以升级某个库或某个框架的版本,而不会影响其他团队。

此外,如果另一个团队想要启动一个新的子应用程序,他们可以使用他们想要的技术而不会影响每个人。§

👍 优点:独立部署

这或许是最大的优势了。“团队目录”可以独立于“团队支付”运行和发布。

🤔缺点:设计系统集成

如果你有一个用 Vue 编写的设计系统,你可能不想仅仅因为某个团队想尝试一些东西就用 React 重写它。在这种情况下,你可能会对Web Components感兴趣。根据我的经验,它很棒。但是当你实际使用它时,情况就完全不同了。而且 IE11 不支持它(需要 polyfill)。

💡 提示:您可以使用以下命令生成 Web 组件:vue-cli

vue-cli-service build --target wc --name foo 'src/components/*.vue'
Enter fullscreen mode Exit fullscreen mode

🤔 缺点:协调团队很困难

如果你还在开发一个老旧的单体应用,那么要实现这一点将会非常漫长。
此外,如果后端还没有采用微服务架构,你就无法在完全隔离的孤岛中工作。也许前端团队可以独立运作,但后端团队却不能。

🤔缺点:表演

当你对应用进行模块化时,每个模块可以拆分成多个块(通过 WebPack 4 等工具)。你只需加载一次主要依赖项,然后在加载支付页面时再加载代码。如果你将现有模块转换为独立/隔离的应用程序,那么每个应用程序上的主框架将加载到每个模块上。

🤔缺点:路由

要从模块 A 转到模块 B,模块 A 需要知道完整路径。常见的做法是对 URI 进行硬编码。毕竟:“优秀的 URI 不会改变”(W3C)

⚠️ 避免炒作驱动开发™️。你可能还不需要这种架构。只有当你认为模块化已经达到极限时,才考虑微前端架构!

提示#3:善待你的商店(Vuex)

基于组件的应用程序最大的问题之一就是 store。乍一看,它看起来很棒。但第一次看到 Vue 工具时,它完全震撼了我。我开始到处使用它!然后,问题就开始接踵而至。

  • 页面加载时发生 20 次突变(使得时间旅行变得不可能);
  • 打开一个页面,执行某些操作,然后转到另一个页面并返回。状态无需重新初始化;
  • 功能过度。你需要为所有功能创建变异。

这里有一些让您的商店保持可维护的技巧。

不要过度使用商店

关于 store 有很多误解。我不知道为什么,但很多人认为:“Vue 负责管理 UI,而 Vuex 负责管理应用状态”。我强烈反对这种观点。根据我的经验,将所有逻辑都转移到 store 中会导致开销和不必要的复杂功能。而且,代码越多,就越容易出现 bug。

通常,当我们有这样的存在问题时,我们会求助于创造者。

这不是强制性的,也不建议将所有状态放入 Vuex 存储中。—— Evan You

Vue.js 创建者)

我还引用了 Dan Abramov 的另一句话,主要是因为 Vuex 部分受到了 Redux 的启发。此外,面向组件的应用程序有很多值得关注的地方。

对于全局无关紧要且不会以复杂方式发生变化的短暂状态,请使用 React。例如,某些 UI 元素中的切换按钮、表单输入状态。对于全局重要或以复杂方式发生变化的状态,请使用 Redux。例如,缓存的用户或帖子草稿。—— Dan Abramov

Redux 的创建者,Redux 相当于 Vuex,但适用于 React)

当我问自己:“我是否应该使用这家商店?”时,我通常会在心里做这样的事情:
我应该将这些数据存储在 Vuex 中吗(流程图)

换句话说,以下是一些例子:

  • 存储“当前用户,i18n 偏好设置”→是。
  • “通过 API 调用加载数据”→Joker!少点尴尬。我通常把它保存在组件的状态里。我同意,有时候,与应用的其他部分共享这些数据是有意义的。
  • 与编辑/创建表单相关的状态→编号
  • “切换 UI 元素”→否。
  • “管理一个isLoading状态”→否。

我不是说你不应该使用商店。只是要节俭地使用它!

使用命名空间存储(用于模块)

const store = new Vuex.Store({
  modules: {
    catalog: {
      namespaced: true,
      state,
      getters: {
        getFeaturedProducts () { ... } // -> getters['catalog/getFeaturedProducts']
      },
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

不要写过于简单的 getter。

为了实现相关性,getter 必须包含一些逻辑。如果要访问子属性,最好使用mapState

getters: {
  // 🤔 non useful getter
  getProducts: state => state.products,
  // 👍 useful getter
  getFeaturedProducts: state => state.products.filter(p => p.isFeatured),
}
Enter fullscreen mode Exit fullscreen mode

最初发表于maxpou.fr

文章来源:https://dev.to/maxpou/3-tips-for-scaling-large-vuejs-application-2edi
PREV
2021 年 React 开发者的最佳实践
NEXT
Java 微服务:实用指南