为列表渲染设置属性key
切忌使用下标作为key,会失去虚拟Dom对比的优化
// v-for:key
// bad
<div v-for = '(item,i) in items' :key = 'i'>
<!--内容-->
</div>
// good
<div v-for = '(item,i) in items' :key = 'item.id'>
<!--内容-->
</div>
在v-if/v-if-else/v-else 中使用key
添加key,对比虚拟Dom时会认为是不同的节点,将旧元素直接移除并在相同位置添加新元素
// bad
<div v-if = 'error'>
{{error}}
</div>
<div v-else>
{{results}}
</div>
// good
<div v-if = 'error' key = 'search-status'>
{{error}}
</div>
<div v-else key = 'search-results'>
{{results}}
</div>
路由切换组件不变
路由的params参数改变不会重新触发组件的生命周期
解决方法
路由导航守卫 beforeRouteUpdate
拉取数据重新渲染视图,vue-router2.2+ 支持,推荐使用,观察 $route
对象的变化,添加 watch
,增加依赖追踪的内存开销
// bad
const user = {
template:' ',
watch:{
'$route'(to,from){
//...
}
}
}
// good
const user = {
template:' ',
watch: {
'$route.query.id'(to,from){
//...
},
'$route.query.page'(to,from){
//...
}
}
}
为router-view组件添加属性key,利用虚拟dom渲染通过key对比节点的原理,不足之处在于切换路由组件会被销毁并重新创建
<router-view :key = '$route.fullPath'></router-view>
为所有路由统一添加 query
上级路由携带的
query
参数,需要在所有路由中携带,且不影响切换
解决方案
- 使用全局守卫
beforeEach
// 缺点 全局守卫 beforeEach 会执行两次,且每次切换路由都会切换两次
const query = {refer: 'test'}
router.beforeEach((to,from,next)=>{
to.query.referer ? next() : next({...to,query,...query})
})
- 使用函数劫持(推荐使用)
const query = {refer: 'test'};
const transitionTo = router.history.transitionTo
router.history.transitionTo = function(location,onComplete,onAbout){
location = typeof location === 'object' ? {...location,query:{...location.query,...query}} : {path:location,query}
transitionTo.call(router.history,location,onComplete,onAbout)
}
区分 vuex 和 props 的使用边界
业务组件使用
vuex
维护状态,方便组件之间通信
通用组件使用
props
以及事件 进行父子组件通信,与业务解耦,在通用组件中定义props
尽可能详细,指定类型
避免 v-if 和 v-for 一起使用
推荐的做法
为过滤列表中的项目,可将循环列表替换为一个计算属性,返回过滤后的列表
为避免渲染本该隐藏的列表,可将v-if放到容器组件上
为组件样式设置作用域
通过scoped
特性或者css Modules
设置样式作用域,组件库使用class
策略,使用容易理解的class
名称且没有太高的选择器优先级,不容易导致冲突
// good
<style scoped>
.button{
border:none
}
</style>
// good
<style module>
.button{
border:none
}
</style>
避免在scoped中使用元素选择器
在scpoed样式中,类选择器优于元素选择器,大量使用元素选择器很慢
// bad
<style scoped>
button{
border:none;
}
</style>
//good
<style scoped>
.btn{
border:none;
}
</style>
避免隐性的父子组件通信
通过props和事件进行父子组件之间通信,不可使用this.$parent或改变prop
单文件组件命名
单文件组件名,单词首字母大写,或者始终是横线连接
// bad
components
|-mycomponent.vue
components
|-myComponent.vue
//good
components
|-MyComponent.vue
components
|-my-component.vue
基础组件名,以特定的前缀开头,组件不含Vuex store的全局状态
// bad
components
|-MyButton.vue
|-VueTable.vue
|-Icon.vue
//good
components
|-BaseButton.vue
|-BaseTable.vue
|-BaseIcon.vue
单例组件名,以The前缀命名,表示唯一性,该组件不接受任何prop,非复用组件
// bad
components
|-Heading.vue
|-MySiderbar.vue
//good
components
|-TheHeading.vue
|-TheSidebar.vue
精密耦合的组件名,与父组件紧密耦合的组件应该以父组件为前缀命名
// bad
components
|-TodoList.vue
|-TodoItem.vue
|-TodoButton.vue
components
|-SearchSidebar.vue
|-NavigationForSearchSideBar.vue
//good
components
|-TodoList/
|-Item/
|-index.vue
|-Button.vue
|-index.vue
//better
components
|-TodoList.vue
|-TodoListItem.vue
|-TodoListItemButton.vue
components
|-SearchSidebar.vue
|-SearchSideBarNavigation.vue
组件名中的单词顺序 高级别单词开头+描述性修饰词结尾
//bad
components
|-ClearSearchButton.vue
|-RunSearchButton.vue
|-SearchInput.vue
|-TermsCheckbox.vue
// good
components
|-SearchButtonClear.vue
|-SearchButtonRun.vue
|-SearchInputQuery.vue
|-SettingsCheckboxTerms.vue
完整单词的组件名
//bad
components
|-SdSettings.vue
|-UProfOpts.vue
// good
components
|-StudentDashboardSettings.vue
|-UserProfileOptions.vue
模板中的组件名大小写 组件名除根组件App外应始终由多个单词组成
//bad
vue.component('todo',{
// ...
})
export default{
name:'Todo',
// ...
}
// good
vue.component('todo-item',{
// ...
})
export default{
name:'TodoItem',
// ...
}
模板中的组件名大小写 本身html对大小写不敏感,尽量使用横线连接
// bad
<!-- 在单文件组件和字符串模板中 -->
<mycomponent />
<!-- 在单文件组件和字符串模板中 -->
<myComponent />
<!-- 在DOM 模板中-->
<MyComponent></MyComponent>
// good
<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>
<!-- 在DOM 模板中 -->
<my-component></my-component>
JS/JSX中的组件名大小写
// bad
Vue.component('myComponent',{
//...
})
import myComponent from './MyComponent.vue'
export default {
name: 'myComponent',
// ...
}
export default {
name: 'my-component',
// ...
}
// good
Vue.component('MyComponent',{
//...
})
Vue.component('my-component',{
//...
})
import myComponent from './MyComponent.vue'
export default {
name: 'MyComponent',
// ...
}
自闭合组件
// bad
<!-- 在单文件组件和字符串模板和JSX中 -->
<MyComponent></MyComponent>
<!-- 在DOM 模板中 -->
<my-component/>
// good
<!-- 在单文件组件和字符串模板和JSX中 -->
<MyComponent />
<!-- 在DOM 模板中 -->
<my-component></my-component>
prop名的大小写
// bad
props:{
'greeting-text':String
}
<WelcomeMessage greetdingText='hi'/>
//good
props:{
greetingText:String
}
<WelcomeMessage greetding-text='hi'/>
多个特性的元素
多行分隔对象的多个属性
// bad
< img src='https://vuejs.org' alt='vue logo'>
<MycComponent foo='a' bar='b' baz='c' />
//good
<MyComponent
foo="a"
bar="b"
baz="c"
/>
模板中简单的表达式
// bad
{{
fullName.split(' ').map(function(word){
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
// good
<!-- 模板中 -->
{{ normalizedFullName}}
computed:{
normalizedFullName:function(){
return this.fullName.split(' ').map(function(word){
return word[0].toUpperCase()+word.slice(1)
}).join(' ')
}
简单的计算属性
复杂的计算属性应尽可能分为更多简单的属性,易于测试,易于阅读,低耦合
// bad
computed:{
price:()=>{
let basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPrecent || 0)
)
}
}
// good
computed:{
basePrice:()=>{
return this.manufactureCost / (1 - this.profitMargin)
}
discount:()=>{
return this.basePrice * (this.discountPrecent || 0)
}
finalPrice()=>{
return this.basePrice - this.discount
}
}
指令缩写
指令缩写保持统一,用 :
标识 v-bind
,@
表示 v-on
网友评论