Vue JSX中组件名大小写的问题

Vue 官方风格指南

在 Vue 官方的风格指南中,强烈推荐在 JS/JSX 中组件名应该是 PascalCase 的。

JS/JSX 中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用 Vue.component 进行全局组件注册时,可以使用 kebab-case 字符串。

我原以为在 JSX 中组件名都写成 PascalCase 就好了,然而在使用一些 UI 组件库时,在 render 函数中常常会报某个组件未注册的问题。

例如 iView ,在其官方文档的 快速上手 章节底下,有个 组件使用规范 ,是这样写的:

在非 template/render 模式下(例如使用 CDN 引用时),组件名要分隔,例如 DatePicker 必须要写成 date-picker。

看到这个,我以为这只是这个组件库的规范,于是在报错的时候直接把报错的组件名改成 kebab-case 就好了,也就没再深究。

然而一知半解的下场就是,下次遇到同样的问题,还是会一脸懵逼。

官方示例代码大小写使用情况

我们看到 Vue 官网解释 JSX 的部分有这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
import AnchoredHeading from './AnchoredHeading.vue'

new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})

其中 JSX 部分就是 PascalCase 。那为什么在 JSX 中使用 iView 组件时却要 kebab-case ?

而且在 iView 的 table 组件示例中,可以看到这样的代码:

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
render: (h, params) => {
return h('div', [
h('Button', {
props: {
type: 'primary',
size: 'small'
},
style: {
marginRight: '5px'
},
on: {
click: () => {
this.show(params.index)
}
}
}, 'View'),
h('Button', {
props: {
type: 'error',
size: 'small'
},
on: {
click: () => {
this.remove(params.index)
}
}
}, 'Delete')
]);
}

我们知道, JSX 最后也会被解析成 h 函数,那么,其中组件名的大小写一定跟 JSX 的解析有关。

所以,我们来看看 Vue 的 JSX 插件 babel-plugin-transform-vue-jsx 怎么说。

来自官方的解释

在 babel-plugin-transform-vue-jsx 的 github README 中,有一段 Component Tip :

If a custom element starts with lowercase, it will be treated as a string id and used to lookup a registered component. If it starts with uppercase, it will be treated as an identifier, which allows you to do:

1
2
3
4
5
6
7
import Todo from './Todo.js'

export default {
render (h) {
return <Todo/> // no need to register Todo via components option
}
}

意思就是如果自定义元素是小写开头,就会被当做字符串 id ,会从注册的组件中去查找;如果是大写开头,会被当成一个标识符。

什么意思呢?其实说得不是特别直观,我们直接去看看 PascalCase 跟 kebab-case 分别会被转换成什么样就会明白那段解释了。

Babel Try it out

让我们打开 Babel 官网,点击顶部的 Try it out 。在左侧底部 Add Pluginbabel-plugin-transform-vue-jsx 添加进去。

然后输入:

1
2
const a = <my-component></my-component>
const b = <MyComponent></MyComponent>

马上,我们可以在右侧结果中看到:

1
2
3
4
"use strict";

var a = h("my-component");
var b = h(MyComponent);

这下清楚了:

  1. kebab-case 组件名解析成 h 函数时会解析成字符串,自然会去看当前组件有没有注册相应的组件,或者去搜索有没有全局注册的组件。
  2. PascalCase 组件名解析成 h 函数时会解析成一个标识符,也就是变量,需要你在当前上下文中就定义或者引入这个变量。

结论

那么,在 Vue JSX 里,组件名什么时候用 PascalCase ,什么时候用 kebab-case ,就很明确了:

  • 当组件只在全局注册时,一般是使用了组件库,在入口用了 Vue.use 这类,或者自己用了 Vue.component 注册的组件,这时候没在当前组件引入时,需要用 kebab-case

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// main.js
import iView from 'iview'
import CustomComponent from './some-local-path'

Vue.use(iView)
Vue.component('CustomComponent', CustomComponent)


// MyComponent.vue
export default {
render (h) {
return (
<div>
<i-button></i-button>
<tooltip></tooltip>
<custom-component></custom-component>
</div>
)
},
}

  • 当组件在当前组件有引入过时,或者在当前组件定义了的组件,可以直接用 PascalCase ,因为这是直接使用的变量。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MyComponent.vue
import { Tooltip } from 'iview'
import CustomComponent from './some-local-path'

export default {
render (h) {
return (
<div>
<Tooltip></Tooltip>
<CustomComponent></CustomComponent>
</div>
)
},
}