# 为列表渲染设置属性key

# 在v-if/v-if-else/v-else中使用key

当在相同类型的元素之间切换时,Vue会修补已存在的元素,而不是将旧的元素移除,然后在同一位置添加一个新元素。如果本不相同的元素被识别为相同,则会出现意料之外的副作用。

<div v-if="error">
  错误:{{ error }}
</div>
<div v-else>
  {{ results }}
</div>

添加了属性key,那么在比对虚拟DOM时,则会认为它们是两个不同的节点,于是会将旧元素移除并在相同的位置添加一个新元素,从而避免意料之外的副作用。

<div
  v-if="error"
  key="search-status"
>
  错误:{{ error }}
</div>
<div
  v-else
  key="search-results"
>
  {{ results }}
</div>

# 路由切换组件不变

页面切换到同一个路由但不同参数的地址时,组件的生命周期钩子并不会重新触发。这是因为vue-router会识别出两个路由使用的是同一个组件从而进行复用,并不会重新创建组件,因此组件的生命周期钩子自然也不会被触发。

解决方法:

  • 路由导航守卫beforeRouteUpdate 路由导航守卫beforeRouteUpdate,在当前路由改变且组件被复用时调用,所以可以在组件内定义路由导航守卫来解决这个问题。
  • 观察 $route对象的变化
const User = {
  template: '...',
  watch: {
    '$route' (to, from) {
      // 对路由变化作出响应
    }
  }
}

上面这种解决方案的代价是组件内多了一个watch,这会带来依赖追踪的内存开销。

如果页面中有多个内容,切换路由只是其中的某个内容发生变化,最好区分处理。假设/user?id=4&page=1每次切换路由的时候只有page变化,就不需要每次都处理id,可以进行区分操作

const User = {
  template: '...',
  watch: {
    '$route.query.id' () {
      // 请求个人描述信息
    },
    '$route.query.page' () {
      // 请求列表
    }
  }
}
  • 为router-view组件添加属性key
<router-view :key="$route.fullPath"></router-view>

上面这样通过给router-view组件设置key,可以使每次切换路由时的key都不一样,重新创建这个组件。但是缺点是每次切换路由组件时都会被销毁并且重新创建,非常浪费性能

# 区分Vuex与props的使用边界

在项目开发中,业务组件会使用Vuex维护状态,使用不同组件统一操作Vuex中的状态。这样不论是父子组件间的通信还是兄弟组件间的通信,都很容易。

对于通用组件,我会使用props以及事件进行父子组件间的通信(通用组件不需要兄弟组件间的通信)。这样做是因为通用组件会拿到各个业务组件中使用,它要与业务解耦,所以需要使用props获取状态。

# 避免v-if和v-for一起使用

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

可以修改为

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

// ...

computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
      return user.isActive
    })
  }
}

方法2

<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

修改为

<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

# 为组件样式设置作用域

CSS的规则都是全局的,任何一个组件的样式规则都对整个页面有效。为了防止css相互影响可以给css设置作用域。

在Vue.js中,可以通过scoped特性或CSS Modules(一个基于class的类似BEM的策略)来设置组件样式作用域。

对于组件库,我们应该更倾向于选用基于class的策略而不是scoped特性。因为基于class的策略使覆写内部样式更容易,它使用容易理解的class名称且没有太高的选择器优先级,不容易导致冲突。

// 使用scoped特性
<style scoped>
</style>

// 使用class Modules
<style module>
</style>

# 避免在scoped中使用元素选择器

为了给样式设置作用域,Vue.js会为元素添加一个独一无二的特性,大量的元素和特性组合的选择器会比类和特性组合的选择器慢,应该尽可能使用类选择器

<style scoped>
    /* 使用类 */
  .button {}
  /* 尽量不使用标签 */
  button {} 
</style>

# 单文件组件如何命名

  • 单文件组件的文件名的大小写 单文件组件的文件名应该始终是单词首字母大写(PascalCase),或者始终是横线连接的(kebab-case)。
  • 基础组件名
  • 单例组件名
  • 组件名中的单词顺序 组件名应该以高级别的(通常是一般化描述的)单词开头,以描述性的修饰词结尾。
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
  • 组件名应该倾向于完整单词而不是缩写。
  • 组件名为多个单词
Vue.component('todo-item', {
  // ……
})
export default {
  name: 'TodoItem',
  // ……
}

# 指令缩写

指令缩写(用:表示v-bind:、@表示v-on:)要保持统一

# 简单的计算属性

把复杂的计算属性分隔为尽可能多更简单的属性

不推荐的做法:

computed: {
  price: function () {
    var basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}

推荐的做法:

computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}

# 模板中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。

# 多个特性的元素应该分多行撰写,每个特性一行。

<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>

# prop名的大小写

在声明prop的时候,其命名应该始终使用驼峰式命名规则,而在模板和JSX中应该始终使用横线连接的方式。如下:

props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>

# 自闭合组件

<!-- 在单文件组件、字符串模板和JSX中 -->
<MyComponent/>
<!-- 在DOM模板中 -->
<my-component></my-component>

# JS/JSX中的组件名大小写

Vue.component('MyComponent', {
  // ……
})
Vue.component('my-component', {
  // ……
})
import MyComponent from './MyComponent.vue'
export default {
  name: 'MyComponent',
  // ……
}