平常在写 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
| 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
使用的是 className
,Vue
使用的是 class
- 事件监听是以
on
或 nativeOn
开头
经过编译后为
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' }) }
|
注意事项
- 在模板中引入
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>
|
- 如果反之,即在
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
| class VNode { constructor(tag, props = {}, children = []) { 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) } }
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 }, attrs: { 'x-placement': this.placement } }).push(createElement('div.dy-tooltip-popup-inner', {}, [this.content]))
return $wrapper.resolve(h) }
|
相关示例代码可见 ToolTip 和 Table