构建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 相关文章推荐
JavaScript 学习技巧
Feb 17 Javascript
图片onload事件触发问题解决方法
Jul 31 Javascript
JS操作Cookies的小例子
Oct 15 Javascript
javascript的事件触发器介绍的实现
Jun 05 Javascript
JavaScript charCodeAt方法入门实例(用于取得指定位置字符的Unicode编码)
Oct 17 Javascript
jQuery实现的简单分页示例
Jun 01 Javascript
Javascript使用function创建类的两种方法(推荐)
Nov 19 Javascript
Vue响应式添加、修改数组和对象的值
Mar 20 Javascript
在JS中如何把毫秒转换成规定的日期时间格式实例
May 11 Javascript
JS实现搜索关键词的智能提示功能
Jul 07 Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 Javascript
只有 20 行的 JavaScript 模板引擎实例详解
May 11 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
全国FM电台频率大全 - 20 广西省
2020/03/11 无线电
同一空间绑定多个域名而实现访问不同页面的PHP代码
2006/12/06 PHP
php+mysql开源XNA 聚合程序发布 下载
2007/07/13 PHP
MySQL 日期时间函数常用总结
2012/06/12 PHP
部署PHP时的4个配置修改说明
2015/10/19 PHP
PHP编写RESTful接口
2016/02/23 PHP
Mac下关于PHP环境和扩展的安装详解
2019/10/17 PHP
新闻内页-JS分页
2006/06/07 Javascript
js下通过prototype扩展实现indexOf的代码
2010/12/08 Javascript
js用typeof方法判断undefined类型
2014/07/15 Javascript
JavaScript中的null和undefined区别介绍
2015/01/01 Javascript
JS函数this的用法实例分析
2015/02/05 Javascript
JavaScript正则表达式之multiline属性的应用
2015/06/16 Javascript
基于jQuery实现文本框只能输入数字(小数、整数)
2016/01/14 Javascript
JS弹出窗口的运用与技巧大全
2016/11/01 Javascript
js实现一个可以兼容PC端和移动端的div拖动效果实例
2016/12/09 Javascript
JS实现异步上传压缩图片
2017/04/22 Javascript
Angular如何由模板生成DOM树的方法
2019/12/23 Javascript
Vue 封装防刷新考试倒计时组件的实现
2020/06/05 Javascript
JS实现电脑虚拟键盘的操作
2020/06/24 Javascript
js实现简单抽奖功能
2020/11/24 Javascript
python实现批量获取指定文件夹下的所有文件的厂商信息
2014/09/28 Python
Python 模板引擎的注入问题分析
2017/01/01 Python
Python中表达式x += y和x = x+y 的区别详解
2017/06/20 Python
Python基于FTP模块实现ftp文件上传操作示例
2018/04/23 Python
解决python中遇到字典里key值为None的情况,取不出来的问题
2018/10/17 Python
python 抓包保存为pcap文件并解析的实例
2019/07/23 Python
python 爬取疫情数据的源码
2020/02/09 Python
俄罗斯品牌服装在线商店:VIPAVENUE
2020/08/10 全球购物
汽车检测与维修个人求职信
2013/09/24 职场文书
代理班主任的自我评价
2014/02/04 职场文书
大学生先进事迹材料
2014/02/16 职场文书
领导干部群众路线个人对照检查材料思想汇报
2014/09/30 职场文书
2015年社区教育工作总结
2015/05/13 职场文书
聊一聊python常用的编程模块
2021/05/14 Python
mysql 生成连续日期及变量赋值
2022/03/20 MySQL