Vuejs渡劫系列一:日常开发中必须掌握的细节(keng)

日常开发中必须掌握的细节(keng)

Vuejs渡劫系列的首篇文章,主要针对一年多日常开发中的,对Vuejs的一些技术细节的总结,新手看了可保平安,老鸟大婶看了可温故知新!

从开始观望Vue 1.0到项目迁移到Vue 2.x,到现在项目改造成Vue-SPA已经有一年的时间,不得不说Vue带来了很多前所未有的新鲜感和卓越的开发效率,但Vue(或者说MVVM框架)总不像jQuery来的那样纯粹简单,因此,一些未知的新鲜感总会隐藏着未知的劫难(keng),因此该系列取名为 Vuejs渡劫———该系列不漫谈每个API,只是对博主开发过程中遇到问题和Vue学习需要注意的点进行简要描述。

下面的注意点,是本人日常工作遇到过的疏忽或者一些比较要注意的Vue细节,不一定是坑,但是总会需要值得注意。注意的点会根据项目的推进 不定期更新 喜欢的朋友们可以保持关注!

安装

谨慎使用命令行工具(CLI)

Vue-Cli是官方提供的命令行工具,个人不建议上手就用,因为其需要你对Node.js,Webpack等工具有相当一定程度的了解。个人先后使用过seajs/spm3,require.js,webpack 1.x等工具,但也无法说精通loaders的机制,而且CLI集成了Vue-loaders和其他loaders的依赖关系,错综复杂,新手可以通过script link的方式或者NPM的方式去使用:

script

1
<script type="text/javascript" src="https://unpkg.com/vue@2.4.2/dist/vue.js"></script>

NPM

1
npm install vue

全局 API

Vue.extend 创建“子类”

extend 是一个很实用的方法,尤其是在对.vue单文件进行unit-test(单元测试)的时候。由于单文件模式下,一般只会在 App.vue 的入口处对vue实例,各个子类的模块,通过import或者require的方式进行加载。

通过利用 Vue.extend 创建子类,并挂载到Dom元素上:

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import Hello from '@/components/Hello'
describe('Hello.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(Hello)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('#J_main div').textContent)
.to.equal('Welcome to Your Vue.js App')
})
})

nextTick: DOM更新完成回调

有时候,我们调用的方法需要DOM落地之后才可以执行,但是我们并不知道更改了data上的数据之后,DOM什么时候才就绪,这个时候我们就需要用 Vue.nextTickthis.$nextTick,其中后者为实例对象上的方法。

该方法可以去处理数据变更后,执行的DOM操作,如chart的绘制,一些jQuery基于DOM的插件等,下面是一个数据变更后,c3.js画图的应用:

1
2
3
4
5
6
7
8
9
10
this.trendChart.status = 'hasData';
this.$nextTick(function() {
c3.generate({
bindto: '#J_trend',
size: {
height: 350
}
……
});
});

选项/数据

data选项的一些禁忌

data选项是Vue定义中的一个最为重要的部分,虽然data是看似是简单json对象,但在使用的过程依旧有些需要注意的地方:

单文件中的使用

值得注意的是,单文件中,data 严格要求是一个 function,一个返回JSON对象的函数;

data的hook处理

博主在做一款数据平台的产品过程中,每个页面都拥有相同的data属性,重复定义,不仅会导致代码量提高,同时在vue单文件中也不够直观,此时我们可以通过外部的hook函数,进行初始化,在对外导出:

1
2
3
4
5
6
7
8
9
import hook from '@/components/hook';
var opts = {
data: {
//your datas
},
...rest
};
opts = hook(opts);
export default opts;

不要对data使用箭头函数

如data: () => { return { a: this.myProp }})。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例(同理 methodswatch 的观察函数定义也是),this.myProp 将是 undefined

使用computed 获取vuex公共变量

在使用 vuex 对状态进行管理的时候,在获取state上的值时,请在 computed 选项中声明获取,否则数据同步会有问题:

1
2
3
4
5
computed :{
viewType () {
return this.$store.state.user.viewType
}
}

数组更新问题

由于Vue使用的是defineProperty的方式进行监测变化,因此Vue无法监测到数组以下的变动:

1.当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue

2.当你修改数组的长度时,例如: vm.items.length = newLength

针对第一个问题,可以通过以下方式触发更新:

1
2
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
1
2
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)

针对第二个问题,可以通过以下方式触发更新

1
example1.items.splice(newLength)

二维(N维)数组更新方式

如果我们需要更新一个二维数组的某个变量的值,如需要更新 items[i][j]数组下的name属性,我们需要把更改的 index 参考数组更新第一个问题的方式设置为最前面的维度数组索引,即

1
2
3
4
// Vue.set
let newVal = items[i][j];
newVal[i][j].name = "new";
Vue.set(example1.items, i, newVal);

对象更新问题

如题,博主说的并不是你的对象更新[窃笑],这里说的是Vue中data的一个对象更新需要注意的点:我们不能后续异步的建立新的对象属性,如我们data中,有个man对象只有 name 的属性:

1
2
3
man:{
name: "momo"
}

此时,如果我们需要添加一个age的属性,我们不能直接写 this.man.age = 26 而是需要通过 set方法进行增加监测对象:

1
Vue.set(vm.man, 'age', 26);

1
this.$set(this.man, 'age', 26);

除此以外,如果你执意要用 this.man.age = 26 这个方式更改,你可以通过提前配置这个属性为缺省值,如:

1
2
3
4
man:{
name: "momo",
age: ""
}

组件

组件props命名

HTML 特性是不区分大小写的。所以,当使用的不是字符串模板,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:

1
2
3
4
5
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
1
2
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>

单向数据流

Vue使用的是单向数据流,提倡 props down,event up方式,通过使用Props传递数据:

1
2
3
4
5
6
7
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 可以用在模板内
// 同样也可以在 vm 实例中像“this.message”这样使用
template: '<span>{{ message }}</span>'
})
1
<child message="hello!"></child>

子组件通过 $emit自定义方式,去唤起父组件变更数据:

1
this.$emit('say', message);
1
<child @say="toSay" message="hello!"></child>

通过 @say 对其自定义事件进行侦听,事件触发后,执行父组件的toSay()去驱动数据更新

1
2
3
4
5
methods:{
toSay () {
this.message = otherVal;
}
}

点击子组件触发方法

如你使用了vue-router,并希望在 <router-link></router-link> 被点击的时候,调用当前的方法

1
<router-link v-on:click.native="doSomething"></router-link>

指令

v-for多层循环的时候,注意对象和索引值的命名

如你写了一个三层循环的组件,第一层为(items,index)那么第二层为 (item, idex) 然后第三层为 (itm, idx),…… [这么长,我哭了T T]

要注意,每层的变量都是当前整个data的全局环境,因此每一层的对象和索引值都要有所不同,否则变量名被覆盖,就无法取到正确的对象或索引。

不要通过 v-model与 表单控件的checked属性绑定

如题,博主曾经通过v-model的值 true/false去控制checked切换, 这里已经做了很多次尝试,最后还是没有好的成效,最后通过模拟 CheckBox或者 radio的方式解决。

增加v-cloak指令,免除渲染过程出现 的出现

由于数值渲染会依赖Vuejs和其他组件加载的后,才会正确显示,才过程中,如果html页面中通过表达式的方式引用,网速过慢的情况下,可能会看到双花括号表达式,这时可以参考官方写入一个 v-cloak 指令,并对指令增加属性样式:

1
2
3
[v-cloak] {
display: none;
}
1
2
3
<div v-cloak>
{{ message }}
</div>

Vue在数据准备完成后,会主动移除 v-cloak 这个指令(属性),数据就会在正确的时间被正确地显示了。

总结

由于博主人老了,脑瓜er不灵光了,有些遇到的坑被填过就忘了,后续想起来,或者遇到其他问题,会继续更新文章,也欢迎小伙伴或者大婶们,多多指正和关注后续的系列文章。