为什么 Vue 组件需要一个 name

无论是通过 Vue.component 的方式,还是在写 .vue 文件的方式,官方都会推荐你写一个 name 属性。但是好像这个 name 在渲染时又几乎用不到,那么它实际能干嘛呢?

方便调试

Vue 有一款官方强力推荐的浏览器上的调试插件: vue-devtools。通过打开调试工具,我们可以很轻松的看到已经渲染到页面上的各个组件,以及对应的组件的内部状态。

例如以下组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// Demo.vue
export default {
name: 'Kawaii',
props: ['val'],
render (h) {
return (
<div>
{ this.val }
</div>
)
}
}
</script>

如果在 App.vue 里引用的话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<Demo val="foo" />
</template>

<script>
import Demo from './components/Demo'

export default {
name: 'app',
components: {
Demo
}
}
</script>

那么可以在 chrome 的调试界面可以看到:

其中 Kawaii 就是刚才起了名字的组件,如果将其改名为 看不见看不见

1
2
3
4
5
6
7
<script>
// Demo.vue
export default {
name: '看不见看不见',
// ...
}
</script>

那么在 chrome 界面则可以直接看到其显示名字就变为 看不见看不见 了。

但如果不设置名字,那么 Vue.js 默认会用 tag 名称,也就是这个组件在 template 或者 jsx 里的名字。比如:

1
2
3
4
5
6
7
8
<script>
// Demo.vue
export default {
// 不设置名字
// name: '看不见看不见',
// ...
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<D val="foo" />
</template>

<script>
import Demo from './components/Demo'

export default {
name: 'app',
components: {
'D': Demo
}
}
</script>

那么此时就会显示为 D:

Note: functional 组件不会显示在 devtools 中,因为 vue 并不会为 functional component 创建实例

递归应用

在有些时候,我们可以会递归使用自身组件,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
{{ val }}
<Recursive v-if="val" :val="val - 1" />
</div>
</template>

<script>
// Recursive.vue
export default {
name: 'Recursive',
props: {
val: {
required: true,
type: Number,
default: 5
}
}
}
</script>

在这种情况下,如果不设置 name,那么 console 就会报错,提示在 Recursive.vue 中找不到名为 Recursive 的组件。因此,如果想要递归应用自身组件的话,就必须设置 name 属性。

keep-alive

keep-alive 是 Vue 内置的组件,可以将 keep-alive 的子组件运行缓存,用于保存切换 (例如 v-if 的切换) 过程前的状态。

而 keep-alive 有两个 props 是 include 以及 exclude。前者指明需要缓存的组件的名字。而后者则指明不需要缓存的组件的名字。

下述例子中,Foo, Bar 两个组件的名字分别为 vue-foo, vue-bar, 且组件均包含一个 input。因为通过 input 可以很轻松的观察到切换前后的状态是否有保存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
{{ val }} <input type="text" v-model="m" />
</div>
</template>

<script>
// Foo.vue
export default {
name: 'vue-foo',
data () {
return {
val: 'vue-foo',
m: 'vue-foo'
}
}
}
</script>

Bar.vue 跟 Foo.vue 内容几乎一样,除了名字,因此这里就不贴 Bar.vue 的代码了。

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
<template>
<div>
<keep-alive include="vue-foo">
<Foo v-if="visible" />
<Bar v-else />
</keep-alive>
<Button @click="visible = !visible">
Visible {{ visible }}
</Button>
</div>

</template>

<script>
import Foo from './components/Foo'
import Bar from './components/Bar'

export default {
name: 'app',
data () {
return {
visible: true
}
},
components: {
Foo,
Bar
}
}
</script>

通过点击 button 来切换。因为设置了 include 指令,那么只有 Foo 的状态才会被保存:

通过这张效果图可以看到,在切换过程中,Foo 的状态被保存了,而 Bar 则没有,而是重新生成。

如果使用 exclude 指令:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>
<keep-alive exclude="vue-foo">
<Foo v-if="visible" />
<Bar v-else />
</keep-alive>
<Button @click="visible = !visible">
Visible {{ visible }}
</Button>
</div>

</template>

那么就会得到一个完全相反的结果: 即 Bar 的状态被保存了,而 Foo 则没有,而是重新生成。

References

  1. api/#keep-alive
  2. api/#name
  3. vue-devtools/issues/280