vue最佳实践

公司主要业务是基于Vue.js开发的,在实际开发中,有许多可以沉淀下来的最佳实践。本文即是关于这些最佳实践的记录,会持续更新。

props传递异步数据

很多时候,子组件依赖于父组件通过props传递过来的数据,但有些时候,这些数据在父组件中是异步获取的,也就是说这些数据会发生变化,而这些变化可能无法在子组件中同步。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// parent.vue
<son1 :state="state1">
<son2 v-if="state2DataInitSuccess" :state="state2">
<son3 :state="state3">

{
data () {
return {
state1: 0,
state2: 0,
state3: 0,
state2DataInitSuccess: false,
}
},
async init () {
const { state1, state2 } = await getData()
this.state1 = state1
this.state2 = state2
this.state2DataInitSuccess = true
this.state3 = state3
}
}

// son1.vue

{
props: {
state: Number
},
created: {
if (this.state > 0) {
console.log('success')
}
}
}

// son2.vue

// 代码同son2.vue


// son3.vue

{
props: {
state: Number
},
watch: {
state (val) {
if (val > 0) {
console.log('success')
}
}
}
}

son1.vue中,代码执行顺序是:parent被创建 -> son1被创建 -> getData数据返回。所以,不会有任何信息会被打印出来。因为异步获取数据的不确定性,打乱了程序预期的执行顺序。

son2.vue中,新增了state2DataInitSuccess字段,并和v-if结合使用,保证数据获取之后,再去渲染子组件son2。这种解决方案,其实是保证异步请求的数据不会打乱程序预期的执行顺序。可以在合适的应用场景中使用。

son3.vue中,使用了wacth去监听props传入值的变更,然后根据值的变化执行相应操作。这种写法的好处是,很清晰的解决了问题,但是副作用很多,比如props是双向传递的,props的值类型是Object需要监听obj.key。总之,也需要选择在合适场景中使用。

组件生命周期

路由的变动,可能会引起组件的创建和销毁。组件销毁意味着组件中数据占据的内存会被释放,但是浏览器的底层并不是完全的模块化,有几个内置的对象,这些对象的数据会常住于内存之中,比如window对象。就如很多js最佳实践中提到的,不推荐在这些全局对象中附加很多数据,但是有时候又不能不这么做。

而我们遇到的问题就是,在组件销毁的同时,应该也要去卸载那些附加在这些全局对象的数据。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// comp1.vue -- /abc/comp1

{
created () {
window.pageName = 'comp1'
}
}

// comp2.vue -- /abc/comp2
{
created () {
console.log(window.pageName) // comp1
}
}

// comp3.vue -- /abc/comp3

{
created () {
window.pageName = 'comp3'
}
desoryed () {
window.pageName = undefined
}
}

comp1.vue中,挂载数据在window上,在路由跳转后,在comp2依然可以获取到,但这个值可能并不是我们的预期。正确的做法就是comp3:在组件销毁的时候,在全局变量上卸载这些可能有副作用的数据,避免不必要的错误和释放内存。

使用props初始化data

有时候,我们会用父组件传来的数据初始化子组件的数据,代码如下:

1
2
3
4
5
6
7
8
9
10
{
props: {
dataFromParent: Number
},
data () {
return {
child: this.dataFromParent
}
}
}

这样写没什么错,但是不够优雅。通过阅读Vue的源码,我们知道,data(方法)选项第一个参数传递递正是this– 当前实例。所以,我们修改代码如下:

1
2
3
4
5
6
7
8
9
10
{
props: {
dataFromParent: Number
},
data ({ dataFromParent }) {
return {
child: dataFromParent
}
}
}

使用参数结构的方法,代码变得更优雅易懂。