Vue.js 2.0 最佳实践 & 踩坑记录

最近在团队的小组里做了一次关于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-ifv-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.jsv-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.$setObject.assign

e.g. https://jsfiddle.net/hysunny/eywraw8t/228152/

2. mixins同名选项混合问题

当我们想覆盖一个组件的一些东西或想扩展某个组件时,可以用 Vuemixins

不过要注意:

当组件和混合对象含有同名选项时,同名钩子函数将混合为一个数组,都会被调用;混合对象的钩子将在组件自身钩子之前调用。
值为对象的选项,例如 methods, componentsdirectives,将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。

e.g: https://jsfiddle.net/hysunny/eywraw8t/228225/

3. v-if 与 v-for 的优先级问题

v-ifv-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 的事件可在 createdmounted 注册

需在 beforeDestroydestoryed 的时候使用用 $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~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容