最近在团队的小组里做了一次关于Vue.js 2.0
的一些 最佳实践 和常见的 采坑记录 的小分享,这里总结了一下分享给大家~ 如果哪里有问题欢迎大家diss
^_^
代码规范
关于代码规范,我的理解是不一定非要用什么特别严苛的标准或是特别权威的规范,只要项目团队遵循一套统一的约定就可以。当然,参考他人的经验沉淀从而制定自己的代码规范还是很有必要的。
1. 对齐
- 静态属性 (static binding) 在前, 动态属性 (dynamic binding) 在后
- 属性绑定 (:属性)在前, 事件绑定 (@事件) 在后
- 如果有使用指令,则指令语句写在最前面
e.g:
<!-- bad -->
<li
:name="n.name"
@click="check"
v-for="n in list"
:key="n.id"
class="list-item"
:tag="n.tag"
type="product"
>{{ n.name }}
</li>
<!-- good -->
<li
v-for="n in list"
class="list-item"
type="product"
:key="n.id"
:name="n.name"
:tag="n.tag"
@click="check"
>{{ n.name }}
</li>
2. 简化的表达式(不要在模版里使用复杂的表达式)
- 对于列表里的数据表达式,可以用
filter
来处理 - 其他情况下,可以使用
computed property
e.g:
<!-- bad -->
<template>
<h1>
{{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }}
</h1>
</template>
<!-- good -->
<template>
<h1>
{{ `${year}-${month}` }}
</h1>
</template>
<script type="text/javascript">
export default {
computed: {
month() {
return this.twoDigits((new Date()).getUTCMonth() + 1);
},
year() {
return (new Date()).getUTCFullYear();
}
},
methods: {
twoDigits(num) {
return ('0' + num).slice(-2);
}
},
};
</script>
3. 组件编码规范
- 导出一个清晰、组织有序的组件,使得代码易于阅读和理解。同时也便于标准化。
- 能避免操作
dom
就尽量避免,实在要用的话最好使用ref
来代替querySelector
等选择器方法 - 一个
.vue
的文件行数最好控制在 200 行左右 - 善用
v-if
和v-show
。比如,涉及到权限的必须用v-if
而非v-show
。例如,用户必须登录后才能查看的,请用v-if
- 请尽量保证数据流的可追踪性。尽量不要使用
$parent
,而是通过props
属性接收父组件的传入
参考:Vue组件设计规范
最佳实践
1. 属性绑定
1.1 绑定字符串不需要加冒号
e.g:
<!-- bad -->
<component :message="hello" />
<!-- good -->
<component message="hello" />
1.2 布尔属性省略值时默认为 true
e.g :
<my-modal visible />
<!--等价于-->
<my-modal :visible="true" />
1.3 HTML原生属性可以不用 props
绑定 或者 组件特有的特性也不用 props
绑定
e.g:
<component class="className" />
<bootstrap-date-input data-date-picker="activated" />
2. 事件绑定
2. 绑定无参函数不需要加 ()
e.g:
<!-- bad,括号多余 -->
<button @click="onClick()">按钮</button>
<!-- good,隐式传递了 event 对象 -->
<button @click="onClick">按钮</button>
2.2 只有一行代码的事件函数,可以直接写在标签上
e.g:
<button @click="visible = !visible">显示/隐藏</button>
2.3 在监听原生DOM事件时,方法以 事件 为唯一的参数。如果使用内联语句,语句可以访问一个 $event
属性:
e.g:
<button @click="handle($event, 1)">click me</button>
如果在父组件中想要给子组件抛出的事件添加自定义参数,可利用此属性:
e.g: https://jsfiddle.net/hysunny/eywraw8t/228187/
3. 修饰符
Vue 内置了许多常用的 修饰符 ,可以让你少写几行代码,提高开发效率。
e.g:
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
4. 按需加载组件
一般配合 Vue-Router
使用,适用于大型应用,将应用分割成小的代码块,只在需要的时候才从服务器加载。
实现方式:
- 异步组件实现
- es6 import
好处:
- 按需加载,节省首次加载实践,提高速度,也算是一个性能优化;
- 组件只会加载一次,加载完成后会缓存下来,使用一个组件多次使用的场景。
e.g:
// 异步组件实现
export default new Router({
routes: [
{
path: '/test',
name: 'test',
component: resolve => require(['../components/Test'], resolve)
},
]
})
// ES6 import
const Test1 = () => import('../components/Test1')
const Test2 = () => import('../components/Test2')
export default new Router({
routes: [
{
path: '/test1',
name: 'test1',
component: Test1
},
{
path: '/test2',
name: 'test2',
component: Test2
}
]
})
5. 过滤器
用于一些常见的文本格式化,如展示发布时间。
e.g:
<template>
<!-- 在双花括号中 -->
<span>{{ message | capitalize }}</span>
<!-- 在 `v-bind` 中 -->
<div :message="message | capitalize"></div>
</template>
<script>
export default {
data() {
return {
message:1
}
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
}
</script>
6. 多个元素块可以用template包裹
参考: Vue.js 在-lt-template-gt-元素上使用-v-if-条件渲染分组
e.g:
<template v-if="list.length">
<div>header</div>
<div>list</div>
<div>footer</div>
</template>
<template v-else>
<div>no list</div>
</template>
7. v-for 循环加 key
当 Vue.js
用 v-for
正在更新已渲染过的元素列表时,它默认用 就地复用 策略。如果数据项的顺序被改变,Vue
将不会移动 DOM
元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
稍微深入一点,加 key
的原因是为了给 virtualDom中
的 diff
做优化,结果就是提高 virtualDom
更新效率。
patch参考: https://github.com/aooy/blog/issues/2
8. 使用频率较高的方法挂载到Vue实例上
这样做的好处是不需要每次使用都要 import
,提高开发效率。
e.g:
Vue.prototype.$utils = {
cookie,
formatDate,
...vdom,
getQueryString
}
new Vue();
// 使用
this.$utils.getQueryString('from_url');
9. 多级组件传递数据使用 $attrs
和 $listeners
在 Vue 2.4
版本,配合 interitAttrs
选项,父组件中未被 props(v-on) 绑定的属性(事件) 可以在子组件中,通过 $attrs
, $listeners
获取。个人认为好处是不用再每个组件都显式绑定 props
或 事件,坏处是传递的属性或事件不够明确。一般情况下不需要使用,但是在创建更高层次的组件时非常有用。
参考:
$attrs: https://cn.vuejs.org/v2/api/#vm-attrs
$listeners: https://cn.vuejs.org/v2/api/#vm-listeners
常见的坑
1. 对象和数组的更新检测
由于 JavaScript
的限制,Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
还是由于 JavaScript
的限制,Vue 不能检测对象属性的添加或删除
解决方法: 对于数组来说可以使用 vm.$set
或改用可观察数组的变异方法,对于对象来说可以使用 vm.$set
或 Object.assign
e.g. https://jsfiddle.net/hysunny/eywraw8t/228152/
2. mixins同名选项混合问题
当我们想覆盖一个组件的一些东西或想扩展某个组件时,可以用 Vue
的 mixins
不过要注意:
当组件和混合对象含有同名选项时,同名钩子函数将混合为一个数组,都会被调用;混合对象的钩子将在组件自身钩子之前调用。
值为对象的选项,例如methods
,components
和directives
,将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。
e.g: https://jsfiddle.net/hysunny/eywraw8t/228225/
3. v-if 与 v-for 的优先级问题
v-if
与 v-for
一起使用时,v-for 具有比 v-if 更高的优先级,因此如果想有条件的跳过循环的执行,则需将 v-if
置于外层元素(或 template
)上。如:
<ul v-if="todos.length">
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.name }}
</li>
</ul>
<p v-else>No todos left!</p>
4. 遍历对象顺序不一致
使用 v-for
遍历对象时,是按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎
下是一致的。
比如: 在 ios 下当对象的key为字母时,排序为降序,其他机型为升序。
解决方法: 如果要保证顺序,可以加个排序的 filter
或者 改用数组。
参考: https://github.com/vuejs/vue/issues/1827
5. event bus 多次触发
$on
的事件可在 created
或 mounted
注册
需在 beforeDestroy
或 destoryed
的时候使用用 $off
销毁
否则在某些情况下会被被多次触发
e.g. https://jsfiddle.net/hysunny/eywraw8t/232163/
6. 变量命名
变量名不要以_
、$
开头,因为名字以 _
或 $
开始的属性不会被 Vue
实例代理,
因为它们可能与 Vue
的内置属性与 API
方法冲突。
需要用 vm.$data._property
访问它们。
e.g: https://jsfiddle.net/hysunny/eywraw8t/224835/
7. vue 2.0 给组件绑定事件无效
对于一般的 html
元素,绑定自定义事件使用 v-on
即可,但是在某个组件的 根元素 上监听一个 原生事件 ,比如:
<my-component v-on:click="handleClick" />
我们会发现这样是不起作用的,可以使用 .native
修饰符(某些情况) 或是 $listeners
<my-component v-on:click.native="handleClick" />
e.g. 用.native给自定义组件绑定事件
哦啦~
以上部分内容参考了: Vue最佳实践,非常感谢!O(∩_∩)O~