阅读 153

VueJs best practices [译]

前言:

最近开始学习使用 Vue,在看完一遍官方文档,实践的过程中发现有很多约定,特别是命名和引用组件的时候组件的名字,我都觉得很奇怪,在看到这篇文章之后,说实话是被标题党了,觉得可以通过别人总结的最佳实践来指导 Vue 的开发,但是看完之后确实对很多不理解的事情给了一个解释,也有很多的参考链接,下面就是原文翻译,想看原文点击

正文

各位开发者大家好, 在探索了一段时间的 VueJs 文档和 web 之后,为了更加正确的或更普遍易于接受的方式来使用 vuejs,创建了一个最佳实践和样式指南的列表。

下面的要点是与功能/优化相关的,其他的是关于 VueJs 命名约定和元素排序。更详细的信息可以在摘要中的链接中找到。

在组件被销毁的时候使用 $off 清除事件监听器

当使用 $on 监听一个事件的时候,我们应该始终记得在 destroyed() 方法中使用 $off 移除这个事件监听器,这是为了防止我们产生内存泄漏。

事件命名需要使用 kebab-case(短横线命名法)命名

当发布或者监听一个自定义事件时,我们应该始终使用 kebab-case(短横线命名法),为什么?因为无论如何事件都会被自动转换成小写的形式,我们永远不会在 camelCase(驼峰式) 或 PascalCase(帕斯卡命名法) 中监听事件,因此,以与监听事件相同的方式(使用 kebab-case)声明该事件更有意义。

// Emitting
this.$emit('my-event') // instead of myEvent
// Listening
v-on:my-event
复制代码

避免在 watchcreated 中调用同一个事件

如果我们需要在组件初始化和属性更新的时候触发一个方法,通常的做法是执行以下操作:

watch: {
  myProperty() {
    this.doSomething();
  }
},
created() {
  this.doSomething();
},
methods: {
  doSomething() {
     console.log('doing something...');
  }
},
复制代码

即使它看起来可能是正确的,但在这里使用 created()其实是多余的。我们可以将所有功能都放入 watch,因此避免了必须在 created() 中重复代码并且依旧可以在组件初始化的时候触发这个方法。如:

watch: {
  myProperty: {
    immediate: true, // forcing handler on initial status
    handler() {
      this.doSomething();
    }
  }
},
methods: {
  doSomething() {
     console.log('doing something...');
  }
},
// Even better solution
watch: {
  myProperty: {
    immediate: true, // forcing handler on initial status
    handler() {
      console.log('doing something...'); // No need to declare a function on methods for 1 use case
    }
  }
},
复制代码

始终要在在 v-for 循环中使用 :key

在你的模板循环中添加 :key是一个常见的最佳实践,使用 v-for 的时候却不使用 :key 会导致难以发现错误,特别是在动画中。

为 Mixins 使用 $_ 前缀

Mixins 是一种将重复代码放入一个块并根据需要将其导入多次的好方法,但是(而且这是一个很大的转折),这可能会导致多个问题。从这一点讲,我们将解决重叠属性的问题。

当我们将一个 mixin 导入到组件中时,我们正在将 mixin 代码与组件代码合并,现在,具有相同名称的属性会发生什么?组件将始终占据上风,组件的属性具有更高的优先级。

如果我希望我的 mixin 拥有更高的优先级怎么办?您不能分配优先级,但是可以通过选择正确的命名约定来避免属性重叠甚至覆盖。 为了区分 mixin 属性和组件属性,我们使用 $_。为什么使用这些符号?好吧,有这几个原因:

  1. VueJs 样式指南中的约定
  2. _ 是 Vue 的私密属性保留字
  3. $ 是 Vue 的生态系统保留字

VueJs 风格指南中,您会发现他们建议加上 mixin 的名字,例如:$_myMixin_updateUser

我发现添加混入的名字会引起比可读性问题之外更多的混乱。但这也取决于 mixin,上下文环境和开发人员。

通过添加一个简单的 $_,像在 $_updateUser 中,我发现代码可读性更好,并且可以轻松地区分组件和Mixin。

mixin 中使用的内容应被限制在 mixin

跟进上一点,mixins 还有另一个问题:我们忘记了内容(stuff)。

比方说,如果我们创建一个使用 this.language 的 mixin,并且这个属性没有被定义或从 mixin内部的商店中获取,则定义 mixin的组件必须包含该 language 属性。

您已经知道,这非常容易出错。为了避免这些错误,我们在 mixin 本身内部获取了 mixin 所需的内容。不必担心我们要抓两次内容, VueJs 很聪明,如果它检测到相同的内容已经从 Store 中获取到了,就不会做双重工作(因为大多数情况下都是从 Vuex 获取内容)

对单文件组件使用 PascalCase (帕斯卡命名法)或 kebab-case(短横线命名法)

帕斯卡命名法与编辑器具有更好的集成,并在常用的 IDE 之间提供了更好的自动完成/导入功能。 如果要避免在不区分大小写的文件系统出现问题,可以考虑采用短横线命名法。

对基础组件命名时使用前缀

Presentational, dumb, or pure 组件需要使用前缀来和其他的 non pure 组件进行区分。这大大提高了项目的可读性以及团队和开发人员之间的知识传递。

使用PascalCase(帕斯卡命名法)作为JS中的组件名称

在JavaScript中,帕斯卡命名法是类和原型构造函数的约定,这使 Vue 组件也使用帕斯卡命名法具有了意义。

如果我们仅通过使用全局组件定义 Vue.component,建议使用短横线命名法。

Prop 名称声明期间应始终使用 camelCase(驼峰命名法),但模板中使用 kebab-case(短横线命名法)

遵循每种语言的约定:JavaScript (驼峰命名法)和HTML(短横线命名法),这使 prop 在 js 文件中使用驼峰命名法声明并且在 HTML 中使用短横线命名法具有了意义。

使用样式指南中规定的组件选项顺序

这看起来似乎很乏味,但是在整个项目中查找组件和创建新的组件时,对整个项目中的组件的所有选项遵循相同的顺序会很有帮助。

VueJs约定可以在样式指南中找到。

切勿在相同的元素上使用v-if,比如 v-for

这是一个性能杀手,列表越大,这种不良实践将使更多性能受到影响。 让我们用代码来解释,想象一下以下情况:

ul>
  <li
    v-for="game in games"
    v-if="game.isActive"
    :key="game.slug"
  >
    {{ game.title }}
  <li>
</ul>
复制代码

将会被像下面这样的方式进行执行:

this.games.map(function (game) {
  if (game.isActive) {
    return game.title
  }
})
复制代码

我们可以看到的是我们会在整个 games 列表进行迭代,无论 game 的活动列表是否被更新。

在其他框架中,比如说 Angular,这种操作不会被编译(*ngIf不能在存在的同一元素中进行*ngFor)

Actions 必须有返回值

这是在学习 async/await 和 Vuex actions 后所收获的。 请看以下示例:

// Store
[SOME_ACTION] () {
   // Doing stuff that takes a while
   console.log('Action done');
}
// Consuming action
async doSomething() {
  await dispatch(SOME_ACTION);
  console.log('Do stuff now');
}
This will output:
// Do stuff now
// Action done
复制代码

发生这种情况是因为 await 不知道该 await 什么,相反,如果我们实际上返回一个 Promise.resolve()await 它将等待这个 resolve 然后继续执行。

// Store
[SOME_ACTION] () {
   // Doing stuff that takes a while
   console.log('Action done');
   Promise.resolve();
}
// Consuming action
async doSomething() {
  await dispatch(SOME_ACTION);
  console.log('Do stuff now');
}
This will output:
// Action done
// Do stuff now
复制代码

actionsgetters 里面使用选择器

我们之所以创建选择器,是有原因的,它不仅可以在整个应用程序中使用,而且可以在Vuex Store 中使用。 配合代码更好的理解:

// We have this selector
export const language = (state) => state.userConfig.language;
// In one of our actions, we need language:
// Bad
[GET_GAMES]({ commit, rootState }) {
   const lang = rootState.userConfig.language;
   // Do stuff...
}
// Good
[GET_GAMES]({ commit, rootState }) {
   const lang = language(rootState);
   // Do stuff...
}
复制代码

总结

  1. 在组件被销毁的时候使用 $off 清除事件监听器
  2. 事件命名需要使用 kebab-case(短横线命名法)命名
  3. 避免在 watchcreated 中调用同一个事件
  4. 始终要在在 v-for 循环中使用 :key
  5. 为 Mixins 使用 $_ 前缀
  6. mixin 中使用的内容应被限制在 mixin
  7. 对单文件组件使用 PascalCase (帕斯卡命名法)或 kebab-case(短横线命名法)
  8. 对基础组件命名时使用前缀
  9. 使用PascalCase(帕斯卡命名法)作为JS中的组件名称 10. Prop 名称声明期间应始终使用 camelCase(驼峰命名法),但模板中使用 kebab-case(短横线命名法)
  10. 使用样式指南中规定的组件选项顺序
  11. 切勿在相同的元素上使用v-if,比如 v-for
  12. Actions 必须有返回值
  13. actionsgetters 里面使用选择器

参考链接

谢谢!