在 Vue 中使用 JSX

在 Vue 中使用 JSX

平常在写 Vue 组件的时候一般使用的都是模版,但是在一些需要自定义内容的场景下就会用到 Vue 中的 render 函数,但是 render 函数的语法非常繁琐,通常一个非常简单的模版在写成 render 函数之后就会变得十分繁琐

所以在这种情况之下可以考虑采用 JSX 的语法来进行编写,如果需要使用 JSX ,那么就需要事先安装一个 Babel 插件 babel-plugin-transform-vue-jsx,如果是使用 Vue-Cli 构建的项目的话,只需要安装下面三个依赖

1
2
3
4
5
npm install\
babel-plugin-syntax-jsx\
babel-plugin-transform-vue-jsx\
babel-helper-vue-jsx-merge-props\
--save-dev

然后在 .babelrc 文件当中配置一下即可

1
'plugins': ['transform-runtime', 'transform-vue-jsx'],

简单示例

配置完成之后,我们就可以在 Vue 当中编写 JSX 了,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// test 组件
export default {
props: ['onClick', 'isShow'],
data() {
return {
test: 123
}
},
render() {
return (
<div class='test' onClick={this.onClick}>
{this.test}
{this.isShow + ''}
</div>
)
}
}

不过还是有一些需要注意的地方

  • render 方法是 Vue 2.0 才支持的
  • Vue 当中的 JSX 语法和 React 当中的 JSX 语法存在一定的区别

下面是一个涵盖大部分语法的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
render(h) {
return (
<div
id='foo'
domPropsInnerHTML='bar'
onClick={this.clickHandle}
nativeOnClick={this.nativeClickHandle}
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
key='key'
ref='ref'
refInFor
slot='slot'
></div>
)
}

一些区别如下

  • DOM 属性需要加上 domProps 前缀(style 等不需要)
  • React 使用的是 classNameVue 使用的是 class
  • 事件监听是以 onnativeOn 开头

经过编译后为

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
render(h) {
return h('div', {
attrs: {
id: 'foo'
},
domProps: {
innerHTML: 'bar'
},
on: {
click: this.clickHandler
},
nativeOn: {
click: this.nativeClickHandler
},
class: {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
key: 'key',
ref: 'ref',
refInFor: true,
slot: 'slot'
})
}

注意事项

  1. 在模板中引入 JSX 的组件,可以通过 components 引用,另外 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
<template>
<div class='wrapper'>
<Text
:on-click='clickHandle'
:is-show='show'
></Text>
</div>
</template>

<script>
import Test from './Test.vue'

export default {
name: 'hello',
components: {
Test
},
data() {
return {
msg: 'welcome',
show: true
}
},
methods: {
clickHandle() {
this.show = !this.show;
}
}
}
</script>
  1. 如果反之,即在 JSX 里面引入 Vue 模版组件,除了连接符的属性转换为驼峰式,还有一个需要注意的就是指令
    • 如果使用了 JSX,那么内置的指令都不会生效(除了 v-show
    • 可以手动使用 JSX 来进行描述,使用 v-name={value} 语法
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
56
<script>
import Vue from 'vue'

Vue.directives('my-directive', {
inserted: function(el) {
el.style.fontWeight = 900
}
})

export default {
props: ['onClick', 'isShow'],
data() {
return {
test: 123
}
},
methods: {
afterLeave() {
console.log('afterLeave')
}
},
render() {
const directives = [{
name: 'my-directive',
value: 666,
modifiers: {
abc: true
}
}];

return (
<transition onAfterLeave={this.afterLeave} name='fade'>
<div
class='test'
onClick={this.onClick}
v-show={this.isShow}
v-my-directive
>
{ this.test }
{ this.isShow + '' }
</div>
</transition>
)
}
}
</script>

<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}

.fade-enter, .fade-leave-to {
opacity: 0
}
</style>

另外还可以使用原生 vNode 的数据格式使用自定义指令

1
2
3
4
5
const directives = [
{ name: 'my-dir', value: 123, modifiers: { abc: true } }
]

return <div {...{ directives }}/>

简化 Vue 中的 createElement 嵌套写法的函数

一般用于创建带有附加元素的组件,比如 Tooltip 等,代码如下所示

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
56
57
58
59
60
61
62
63
64
65
66
// 简化 createElement 嵌套写法
class VNode {
constructor(tag, props = {}, children = []) {
// 初始化容器,传递过来的 props 和子元素
this.tag = tag
this.props = props
this.children = children
}

// 用于添加子元素
push(vnode) {
if (this.isArray(vnode)) {
this.children.push(...vnode)
} else {
if (vnode) {
this.children.push(vnode)
}
}

return this
}

isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]'
}

// 渲染
resolve(h) {
var children = this.children.map(child => {
if (child instanceof VNode) {
return child.resolve(h)
}
else {
return child
}
})
return h(this.tag, this.props, children)
}
}

// div.dy-tooltip-popup ==> [div, dy-tooltip-popup]
function createElement(tag, props = {}, children = []) {
if (tag.indexOf('.') !== -1) {
var [realTag, className] = tag.split('.')
tag = realTag

if (className !== '') {
var classList = className.split()

if (!props['class']) {
props['class'] = {}
}

classList.forEach(el => {
props['class'][el.trim()] = true
})
}
}

return new VNode(tag, props, children)
}


export {
createElement
}

使用方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createElement } from './createElement'

// ...
render(h) {
var $wrapper = createElement('div.dy-tooltip-popup', {
style: {
top: this.top + 'px',
left: this.left + 'px',
visibility: this.visibility,
opacity: this.opacity
},
// 用于标记 Tooltip 弹出方向,这里可以忽略
attrs: {
'x-placement': this.placement
}
}).push(createElement('div.dy-tooltip-popup-inner', {}, [this.content]))

return $wrapper.resolve(h)
}

相关示例代码可见 ToolTipTable

# Vue

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×