Vue.js 最佳实践清单,照亮你的开发之路

作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS


通过查看 Vue.js 文档 和网上的资料,我列了一个最佳实践和风格指南的清单,这些是被普遍认为更加正确的 Vue.js 用法。

下面列出的这些点,有些是功能和优化相关的,其他的是 Vue.js 命名约定和元素顺序相关。

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

当使用$on监听事件时,我们应该记得在destroyed()中用$off移除该事件。这样可以防止内存泄漏。

事件名总是使用 kebab-case 大小写

触发和监听自定义事件时,应该总是使用 kebab-case 大小写形式。为什么呢?因为无论怎样事件名总会自动转成小写,根本没有机会监听到驼峰式和首字母大写格式的事件名,因此,跟监听用的格式保持一致更加合理,也就是 kebab-case 形式。

// 触发事件

this.$emit('my-event') // 不推荐写法:myEvent

// 绑定事件

v-on:my-event

避免在 created 和 watch 里调用同一个方法

如果需要在组件初始化和属性变化时触发方法调用,通常的做法是这样的:

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

尽管看起来没错,但是这里使用created()是多余的。我们可以把所有功能都放进watch,这样就避免在created()中写重复的代码了,同时还能在组件初始化时触发。例如:

watch: {
  myProperty: {
    immediate: true, // 初始化时立即执行
    handler() {
      this.doSomething();
    }
  }
},
methods: {
  doSomething() {
     console.log('doing something...');
  }
},

// 这样甚至更好
watch: {
  myProperty: {
    immediate: true, //初始化时立即执行
    handler() {
      // 只用一次的代码都不需要定义方法了。
      console.log('doing something...'); 
    }
  }
},

v-for 循环总是使用 :key

在模板循环里总是加上:key是一种常见的最佳实践。不带:keyv-for会导致很难发现错误,尤其是在动画中。

mixins 中的属性名使用$_前缀

mixins 是一种绝佳的方式,它把重复代码放在一起,然后在需要的时候引入。但是,一个大大的但是,这会导致一些问题。在这里我们说下属性冲突的问题。

但我们在组件中引入一个mixin时,其实是将mixin里的代码和组件代码合并了。那么,碰到同名的属性会怎样?答案是,组件的优先级更高。

如果我想要 mixin 的优先级更高怎么办 ?你无法改变优先级,但是可以避免属性合并,甚至选择正确的命名约定来避免属性覆盖。

为了区分 mixin 属性和组件属性,我们可以使用$_前缀。为什么是这两个字符?有几个原因:

  1. 来自VueJs 风格指南的约定

  2. _ 是给Vue 私有属性用的

  3. $ 是给 Vue 开源生态用的

VueJs 风格指南 建议加上mixin的名称,例如:$_myMixin_updateUser

我发现,加上 mixin 的名称带来的迷惑性多于可读性。不过这也取决于 mixin 的开发者和使用场景。

通过添加$_,例如$_updateUser,代码的可读性好多了,也可以轻松区分组件和 mixin 了。

mixin用到的属性应该在mixin中定义

在上一点的基础上,mixin还有另一个问题:有时候会忘了定义相关属性。

如果我们创建了一个 mixin,并使用了this.language,而它没有在mixin内部定义,那么引入这个 mixin 的组件必须要包含language属性。

想必你也能看出来,这很容易出错。为了避免这些错误,我们应该在 mixin 内部定义用到的东西。不要担心会定义两次,Vue.js 足够智能,如果检测到已经从Store 获取(大部分情况下都是从 store 获取数据),就不会做重复工作。

单文件组件命名使用 PascalCase 或者 kebab-case 大小写风格

PascalCase 风格能更好地与编辑器集成,在常用的IDE中也有更好的自动完成和导入功能。

如果我们想避免大小写敏感的文件系统的问题,kebab-case 风格比较合适。

基类组件命名使用前缀

展示型、静态的和“纯”组件应该用一个前缀来与其他非“纯”组件区分开来。这样可以提高项目的可读性,以及改善团队和开发者之间的知识传递。

组件名称使用 PascalCase 风格

在 JavaScript 中,PascalCase 风格是类和原型构造器的传统约定,Vue 组件使用这种风格也比较合理。

如果你只使用通过Vue.component定义的全局组件,建议使用 kebab-case 风格。

Prop 声明时应该总是使用 camelCase,但在模板里是 kebab-case 的。

根据语言各自的约定:JavaScript 使用(camelCase) , HTML 使用 (kebab-case), prop 在JS中定义时用camelCase,而在HTML中使用kebab-case是合理的。

使用风格指南中的组件配置项顺序

听起来可能有点呆板,但是在整个项目中让组件的所有配置项保持一样的顺序,有助于内容查找,也方便创建新的组件。

Vue.js 代码约定见风格指南 (https://vuejs.org/v2/style-guide/#Component-instance-options-order-recommended)。

使用 v-for 的元素不要同时使用 v-if

这是性能杀手,对于这种错误做法,列表越大,性能损失越大。

我们通过代码来解释下。假设有这么一个场景:

<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列表,无论符合条件的games列表是否有变化。

其他框架,比如 Angular,这种写法根本无法通过编译。(使用**ngFor*的元素不能有**ngIf*

Actions 必须有返回

这是长期折腾 async/await 和 Vuex actions 得出的经验。

举例如下:

// Store
[SOME_ACTION] () {
   // 耗时的某个操作
   console.log('Action done');
}
// 使用 action
async doSomething() {
  await dispatch(SOME_ACTION);
  console.log('Do stuff now');
}
这会输出:
// Do stuff now
// Action done

这是因为await不知道要等待什么,相反,如果我们实际上返回了一个Promise.resolve()await就会等待解决,然后继续执行。

// Store
[SOME_ACTION] () {
   // 耗时的操作
   console.log('Action done');
   Promise.resolve();
}// 使用action
async doSomething() {
  await dispatch(SOME_ACTION);
  console.log('Do stuff now');
}
这会输出:
// Action done
// Do stuff now

在 actions 和 getters 内部使用选择器函数

我们创建选择器函数是有原因的,不但可以在整个应用中使用,还可以在Vuex Store 内部使用。

看看代码就明白了:

// 先定义选择器函数
export const language = (state) => state.userConfig.language;// 某个Action需要 language 属性
// 不推荐
[GET_GAMES]({ commit, rootState }) {
   const lang = rootState.userConfig.language;
   // Do stuff...
} 
// 推荐
[GET_GAMES]({ commit, rootState }) {
   const lang = language(rootState);
   // Do stuff...
}

本文已经获得李中凯老师授权转发,其他人若有兴趣转载,请直接联系作者授权。

作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS

发布了756 篇原创文章 · 获赞 1062 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/jnshu_it/article/details/104484253