# 为列表渲染设置属性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',
// ……
}
← 过滤器的奥秘