建立和维护大型Vue.js项目的10个最佳实践
作者:Yujiaao
来源:SegmentFault 思否社区
这是我在使用大型代码库进行 Vue 项目时开发的最佳实践。这些技巧将帮助您开发更有效的代码,更易于维护和共享。
今年的自由职业生涯中,我有机会从事一些大型Vue应用程序的工作。我所谈论的项目有超过12个Vuex 存储,大量组件(有时数百个)和许多视图(页面)。实际上,这对我来说是非常有意义的经历,因为我发现了许多有趣的模式来使代码可扩展。我还必须修复一些导致著名的意大利面条代码难题的错误做法。
因此,今天,我将与您分享10个最佳实践,如果您要处理大量的代码库,我建议您遵循这些最佳实践。
1.使用插槽(slot)使组件更易于理解并且功能更强大
我最近写了一篇文章,介绍有关Vue.js中的插槽您需要了解的一些重要事项。它着重说明插槽如何使您的组件更可重用且更易于维护,以及为什么要使用它们。
但是,这与大型Vue.js项目有什么关系?一图胜千言,所以我将为您画一张图片,这是我第一次后悔不使用它们。
有一天,我只需要创建一个弹出窗口。乍一看,没有什么真正复杂的,只是包括标题,描述和一些按钮。所以我要做的就是把所有东西都当作属性。最后,我用了三个属性来定制组件,当人们单击按钮时会发出一个事件。十分简单!
但是,随着项目的不断发展,团队要求我们在其中显示许多其他新内容:表单字段,不同的按钮(取决于显示在哪个页面上),卡片,页脚和列表。我发现,如果我继续使用属性来使这个组件不断扩展,似乎也可以。但是上帝,我错了!该组件很快变得太复杂了,以至于无法理解,因为它包含了无数的子组件,使用了太多的属性并发出了大量事件。我经历了一种可怕的情况,当您在某处进行更改时,它最终以某种方式破坏了另一页上的其他内容。我搞了个科学怪人的怪物,而不是一个可维护的组件!
但是,如果我从一开始就依赖插槽,情况可能会更好。最后,我重构了所有东西以提供这个小组件。易于维护,更快地理解并且可扩展性更高!
<template>
<div class="c-base-popup">
<div v-if="$slots.header" class="c-base-popup__header">
<slot name="header">
</div>
<div v-if="$slots.subheader" class="c-base-popup__subheader">
<slot name="subheader">
</div>
<div class="c-base-popup__body">
<h1>{{ title }}</h1>
<p v-if="description">{{ description }}</p>
</div>
<div v-if="$slots.actions" class="c-base-popup__actions">
<slot name="actions">
</div>
<div v-if="$slots.footer" class="c-base-popup__footer">
<slot name="footer">
</div>
</div>
</template>
<script> export default {
props: {
description: {
type: String,
default: null
},
title: {
type: String,
required: true
}
}
} </script>
我的观点是,根据经验,由知道何时使用插槽的开发人员构建的项目确实对其未来的可维护性有很大的影响。这样就可以减少发出事件的次数,使代码更易于理解,并且可以在内部显示所需的任何组件时提供更大的灵活性。
作为一个经验法则,请记住,当最终在子组件的父组件中复制子组件的属性时,应该从这一点开始使用插槽。
2.正确组织您的 Vuex 存储
通常,新的 Vue.js 开发人员开始学习 Vuex,因为他们偶然发现了以下两个问题:
他们要么需要从树结构中实际上相距太远的另一个组件访问给定组件的数据,要么
他们需要数据在组件销毁后继续存在。
验证码 博客 收件箱 设定
用户数 队伍 留言内容 小部件 文章
3.使用操作(Vuex Actions)进行 API 调用和提交数据
如果我需要在两个不同的地方(例如博客和首页)获取文章的首页,则可以使用正确的参数调用适当的调度程序。数据将被提取,提交和返回,除了调度程序调用外,没有重复的代码。
如果我需要创建一些逻辑来避免在提取第一页时提取它,则可以在一个地方进行。除了减少服务器上的负载之外,我还有信心它可以在任何地方使用。
我可以在这些操作(vuex actions)中跟踪我的大多数 Mixpanel 事件,从而使分析代码库真正易于维护。我确实有一些应用程序,其中所有 Mixpanel 调用都是在操作中单独进行的。当我不必了解跟踪什么不跟踪什么以及何时发送时,这种方式工作会给我带来有多大的快乐。
4.使用 mapState,mapGetters,mapMutations 和 mapAction 简化代码库
mapState
,mapGetters
,mapMutations
和mapActions
可以帮助你缩短你的代码,通过分组来化繁为简,从你存储里模块一个地方就能掌握全局。// NPM
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
computed: {
// Accessing root properties
...mapState("my_module", ["property"]),
// Accessing getters
...mapGetters("my_module", ["property"]),
// Accessing non-root properties
...mapState("my_module", {
property: state => state.object.nested.property
})
},
methods: {
// Accessing actions
...mapActions("my_module", ["myAction"]),
// Accessing mutations
...mapMutations("my_module", ["myMutation"])
}
};
5.使用 API 工厂
this.$api
可以在任何地方调用以获取API端点的助手。在项目的根目录下,我有一个api
包含所有类的文件夹(请参阅下面的其中一个)。api
├── auth.js
├── notifications.js
└── teams.js
// PROJECT: API
import Auth from "@/api/auth";
import Teams from "@/api/teams";
import Notifications from "@/api/notifications";
export default (context, inject) => {
if (process.client) {
const token = localStorage.getItem("token");
// Set token when defined
if (token) {
context.$axios.setToken(token, "Bearer");
}
}
// Initialize API repositories
const repositories = {
auth: Auth(context.$axios),
teams: Teams(context.$axios),
notifications: Notifications(context.$axios)
};
inject("api", repositories);
};
export default $axios => ({
forgotPassword(email) {
return $axios.$post("/auth/password/forgot", { email });
},
login(email, password) {
return $axios.$post("/auth/login", { email, password });
},
logout() {
return $axios.$get("/auth/logout");
},
register(payload) {
return $axios.$post("/auth/register", payload);
}
});
export default {
methods: {
onSubmit() {
try {
this.$api.auth.login(this.email, this.password);
} catch (error) {
console.error(error);
}
}
}
};
6.使用 $config 访问您的环境变量(在模板中特别有用)
config
├── development.json
└── production.json
this.$config
助手快速访问它们,尤其是当我在模板中时。与往常一样,扩展Vue对象非常容易:// NPM
import Vue from "vue";
// PROJECT: COMMONS
import development from "@/config/development.json";
import production from "@/config/production.json";
if (process.env.NODE_ENV === "production") {
Vue.prototype.$config = Object.freeze(production);
} else {
Vue.prototype.$config = Object.freeze(development);
}
7.遵循一个约定来写提交注释
git commit -am "<type>(<scope>): <subject>"
# Here are some samples
git commit -am "docs(changelog): update changelog to beta.5"
git commit -am "fix(release): need to depend on latest rxjs and zone.js"
看看他们的README文件以了解更多约定。
8.始终在生产项目时冻结软件包的版本
^
开头的版本:{
"name": "my project",
"version": "1.0.0",
"private": true,
"dependencies": {
"axios": "0.19.0",
"imagemin-mozjpeg": "8.0.0",
"imagemin-pngquant": "8.0.0",
"imagemin-svgo": "7.0.0",
"nuxt": "2.8.1",
},
"devDependencies": {
"autoprefixer": "9.6.1",
"babel-eslint": "10.0.2",
"eslint": "6.1.0",
"eslint-friendly-formatter": "4.0.1",
"eslint-loader": "2.2.1",
"eslint-plugin-vue": "5.2.3"
}
}
9.显示大量数据时使用 Vue 虚拟滚动条
npm install vue-virtual-scroller
<template>
<RecycleScroller
class="scroller"
:items="list"
:item-size="32"
key-field="id"
v-slot="{ item }"
>
<div class="user">
{{ item.name }}
</div>
</RecycleScroller>
</template>
10.跟踪第三方程序包的大小
npm remove lodash
npm install lodash.clonedeep
import cloneDeep from "lodash.clonedeep";
关于作者
娜达·里基(Nada Rifki)
Nada 是一位 JavaScript 开发人员,他喜欢使用 UI 组件来创建具有出色 UX 的界面。她专门研究 Vue.js,喜欢分享任何可以帮助她的前端 Web 开发人员的东西。Nada还涉足数字营销,舞蹈和中文领域。
点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,扫描下方”二维码“或在“公众号后台“回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~
- END -