构建Vue大型应用的10个最佳实践(小结)


Posted in Javascript onNovember 07, 2019

这些是我构建大型Vue项目得出的最佳实践,这些技巧将帮助你更高效的编码,并且使其更容易维护和协作。

在我今年的自由职业生涯中我有幸开发了一些大型Vue程序。我所说的这些项目使用了大量Vuex stores ? ,很多Vue组件(有数百个)和很多视图(pages)?。对我而言这是非常有意义的经历,我发现了很多使扩展的方法。同时我还需要修复一些乱七八糟的错误用法。?

所以我将分享我的10个最佳实践,如果你有大型项目需要开发推荐你使用他们。

1. 使用Slots可以使你的组件更强大和便于理解。

最近我写了一篇文章some important things you need to know regarding slots in Vue.js。 主要讲了为什么使用Slots可以提高组件复用且易于维护。

但是这和大型Vue项目有啥关系呢。我将描绘一个使用他们解决你痛点的蓝图。

假如我要开发一个popup。起初看起来没有什么难点,仅仅是包括标题,描述和一些按钮。 所以把所有东西都当作props不就完了吗。 我用了三个自定义props,并且click按钮的时候触发一个事件。 就是这么简单! ?

随着项目的不断发展,业务需要显示许多其他新内容:表单字段,不同的按钮(取决于显示在哪个页面上)、卡片、页脚和列表。通过添加更多的props可以解决这个问题。?但是随着业务发展,组件变的太复杂了。因为他包含了很多子组件,需要触发很多个事件。?我遇到了坑爹的问题,修改了一个功能后影响了其他page上的功能。我创造了一个怪物而不是一个可维护的组件!

但是,一开始使用slots可能会更好。最终我使小组件来重构这个组件。使他更容易维护、好理解、好扩展。

<template>
 <div class="c-base-popup">
  <div v-if="$slot.header" class="c-base-popup__header">
   <slot name="header">
  </div>
  <div v-if="$slot.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="$slot.actions" class="c-base-popup__actions">
   <slot name="actions">
  </div>
  <div v-if="$slot.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>

根据经验在我看来,由熟练使用slots的开发人员构建项目对将来项目的可维护性有更重要的意义。

⚠️ 经验规则,当子组件中使用了父组件的props时,应该使用slots。

2.好好组织的你Vuex store

通常,一个新的Vue开发人员学习Vuex是因为两个问题:

  • 当一个组件需要重一个比较远的组件(嵌套层级或者兄弟组件:译者注)访问数据时。
  • 需要持有一个销毁组件的数据时。

这样他创建第一个Vuex store,学习moudles的用法并且组织程序时。?
问题是创建modules时没有单一模式可循。我强烈建议一定要想清楚如何组织他们。据我所知,很多人更喜欢按照功能来组织他们(我也是:译者注)。例如:

  • Auth.
  • Blog.
  • Inbox.
  • Settings.

就我来说,用从API获得的数据模型组织更容易理解。例子:

  • Users
  • Teams
  • Messages
  • Widgets
  • Articles

用那个取决于自己,但是从长远来看组织良好的Vuex store更具生产力。这样也容易是新人融入并且继承你的初衷。

3.用Actions调用api和提交数据。

我的大部分(不是全部)API调用都在Vuex的actions中,你一定想知道为什么这样做。?

简单的说是因为拉取数据时需要在store中commit。还有就是他提供了我喜欢的封装和复用。其他原因就是:

  • 如果我在两个地方(blog和home page)获取第一个页面的数据。我只需使用不同的参数调用即可得到数据,初次之外没有重复的代码被调用。
  • 如果需要创建一些逻辑来避免重复拉取数据,只需要在一个地方拉取一次。除了给服务器减负以外,我还可以在任何地方使用这些数据。
  • 我可以在actions中最终Mixpanel事件,基于维护性使问题变的容易分析。我的代码中大部分action中只有一个Mixpanel调用,?我不必关注数据和发送这种工作方式太爽了。

4.使用mapState、mapGetters、mapMutations和mapAction精简代码。

当你在组件中访问state/getters或者调用actions/mutations时通常需要创建多个计算属性。使用mapState,mapGetters,mapMutations和mapActions可以将来自store modules中的数据集中在一个地方,这样可以精简代码并且更好理解。

// 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"])
 }
};

你想要的这里都有Vuex官方文档。

5. 快使用API Factories

我通常会创建this.$api助手,可以在任何地方访问我的API入口。我的项目根目录有一个api文件夹有我的所有类(如下)。

api
├── auth.js
├── notifications.js
└── teams.js

每一个都是一类接口的分组,这是我在Nuxt应用中使用插件的方式初始化。(和标准Vue应用程序中的过程非常相似)。

// 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);
 }
});

这样我可以方便的在组件或Vuex操作中调用他们,如下:

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.遵守一个commit命名规则。

在项目发展的过程中,经常需要关注组件的变更历史。如果团队中有人没有遵守commit惯例,那么将很难理解他们所做的事情。

我总是使用并推荐Angular commit message guidelines。我所有的项目中都会使用他,通常团队中的其他人也会发现他的好处。

遵守这些规则使commit更加可读,在查看项目历史时使得commit更加容易追踪。简言之,他是这样用的:

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 file更新更多。

8.始终在生产环境中冻结Package的版本。

我知道...所有软件包都应遵循 the semantic versioning rules.。但实际情况并非如此。?

为了避免一个依赖影响了整个项目在半夜被拖起来,冻结所有程序包的版本可以使你一觉睡到天明并且工作愉快。 ?

这很简单:避免以^开头的版本:

{
 "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虚拟滚动条。

在页面中显示多行或需要循环大量数据时,你已经注意到该页面渲染速度很快变慢。 要解决此问题,您可以使用vue-virtual-scoller。

npm install vue-virtual-scroller

他只渲染列表中的可见项并且复用组件和dom元素,以使其尽可能高效。 如此简单就像一个魔法! ✨

<template>
 <RecycleScroller
  class="scroller"
  :items="list"
  :item-size="32"
  key-field="id"
  v-slot="{ item }"
 >
  <div class="user">
   {{ item.name }}
  </div>
 </RecycleScroller>
</template>

10.追踪第三方程序包的大小

多人合作一个项目时,如果没人关注安装的依赖包数量很快变的难以置信。为了避免程序变慢(尤其是在移动网络环境),我这VSC中使用import cost package这样就可以编辑器中看到导入的包有多大,并且找出大的原因。

例如在最近的项目中,导入了整个lodash库(压缩后24kB)。 有啥子问题? 仅仅使用cloneDeep方法。 通过import cost package找到了问题,我们通过以下方式解决了该问题:

npm remove lodash
npm install lodash.clonedeep

在使用的地方导入:

import cloneDeep from "lodash.clonedeep";

为了进一步优化,我们还可以使用Webpack Bundle Analyzer包通过树状图来可视化Webpack输出文件的大小。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
动态修改DOM 里面的 id 属性的弊端分析
Sep 03 Javascript
ExtJS下书写动态生成的xml(兼容火狐)
Apr 02 Javascript
JavaScript获取FCK编辑器信息的具体方法
Jul 12 Javascript
JavaScript修改css样式style动态改变元素样式
Dec 16 Javascript
jquery使用append(content)方法注意事项分享
Jan 06 Javascript
javascript表单验证使用示例(javascript验证邮箱)
Jan 07 Javascript
javascript创建createXmlHttpRequest对象示例代码
Feb 10 Javascript
JavaScript中合并数组的N种方法
Sep 16 Javascript
EasyUI闪屏EasyUI页面加载提示(原理+代码+效果图)
Feb 21 Javascript
ES6使用let命令更简单的实现块级作用域实例分析
Mar 31 Javascript
使用Dropzone.js上传的示例代码
Oct 10 Javascript
基于vue1和vue2获取dom元素的方法
Mar 17 Javascript
Node配合WebSocket做多文件下载以及进度回传
Nov 07 #Javascript
vue 实现单选框设置默认选中值
Nov 07 #Javascript
js使用文档就绪函数动态改变页面内容示例【innerHTML、innerText】
Nov 07 #Javascript
vue获取data数据改变前后的值方法
Nov 07 #Javascript
使用JS监听键盘按下事件(keydown event)
Nov 07 #Javascript
vue.js循环radio的实例
Nov 07 #Javascript
vue 解决遍历对象显示的顺序不对问题
Nov 07 #Javascript
You might like
星际争霸 Starcraft 编年史
2020/03/14 星际争霸
php $_SERVER当前完整url的写法
2009/11/12 PHP
PHP载入图像imagecreatefrom_gif_jpeg_png系列函数用法分析
2016/11/14 PHP
深入解析Laravel5.5中的包自动发现Package Auto Discovery
2017/09/13 PHP
通过js获取div的background-image属性
2013/10/15 Javascript
点击页面其它地方隐藏该div的两种思路
2013/11/18 Javascript
Js获取当前日期时间及格式化代码
2016/09/17 Javascript
JavaScript页面实时显示当前时间实例代码
2016/10/23 Javascript
jQuery的ajax中使用FormData实现页面无刷新上传功能
2017/01/16 Javascript
详解jQuery中关于Ajax的几个常用的函数
2017/07/17 jQuery
vue实现图书管理系统
2020/12/29 Vue.js
详解Typescript里的This的使用方法
2021/01/08 Javascript
跟老齐学Python之用while来循环
2014/10/02 Python
纯Python开发的nosql数据库CodernityDB介绍和使用实例
2014/10/23 Python
探究Python的Tornado框架对子域名和泛域名的支持
2015/05/02 Python
python Django批量导入数据
2016/03/25 Python
深入解析Python的Tornado框架中内置的模板引擎
2016/07/11 Python
python 执行文件时额外参数获取的实例
2018/12/18 Python
python 装饰器功能与用法案例详解
2020/03/06 Python
使用Python获取当前工作目录和执行命令的位置
2020/03/09 Python
Django mysqlclient安装和使用详解
2020/09/17 Python
意大利大型购物中心:Oliviero.it
2017/10/19 全球购物
英国标志性生活方式品牌:Skinnydip London
2019/12/15 全球购物
高中生期末评语
2014/01/28 职场文书
乡镇庆八一活动方案
2014/02/02 职场文书
行政人事专员岗位职责
2014/03/05 职场文书
销售个人求职信范文
2014/04/28 职场文书
质量负责人任命书
2014/06/06 职场文书
十佳标兵事迹材料
2014/08/18 职场文书
领导班子奢靡之风查摆问题及整改措施
2014/09/27 职场文书
2014年财务工作总结范文
2014/11/11 职场文书
道德模范事迹材料
2014/12/20 职场文书
2019年国庆祝福语(70句)
2019/09/19 职场文书
Pygame Time时间控制的具体使用详解
2021/11/17 Python
《雀魂PONG☆》4月1日播出 PV角色设定情报
2022/03/20 日漫
关于Redis的主从复制及哨兵问题
2022/06/16 Redis