# Vue JSX
# render渲染函数 (opens new window)
# createElement
createElement参数 (opens new window)
第二个参数: https://juejin.cn/post/7030362678199582734#heading-2
render(h) {
h('div',{
class: {}, // 类名
domProps: {}, // DOM 属性
props: {},
attrs: {},
style: {}, // 样式
on: {}, // 绑定事件
})
}
2
3
4
5
6
7
8
9
10
在vue文件中写render时不要写template,否则不会渲染。
<script>
export default {
name: 'Home',
render(h) {
return h('h1', 'some') // h即createElement
},
}
</script>
2
3
4
5
6
7
8
当使用render函数渲染一个函数式组件时,渲染函数还会接收一个额外的 context 参数。
<script>
export default {
name: 'Func',
functional: true,
render(h, { props }) {
console.log(props)
return (
<div>
<p>这是一个函数组件</p>
</div>
)
},
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# h auto-injection
Starting with version 3.4.0 we automatically inject
const h = this.$createElementin any method and getter (not functions or arrow functions) declared in ES2015 syntax that has JSX so you can drop the(h)parameter.
从3.4.0开始h会被自动注入
# 在Vue2.x中使用jsx
yarn add @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props -D
按照教程 (opens new window)中安装了@vue/babel-preset-jsx和 @vue/babel-helper-vue-jsx-merge-props后报错
Duplicate declaration "h"。
官网有这样一段话
我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入
const h = this.$createElement,这样你就可以去掉(h)参数了。
而babel-preset-jsx应该是也注入了h所以导致报错。
设置babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
['@vue/babel-preset-jsx', { injectH: false }]
]
}
2
3
4
5
6
如果是Webpack构建的项目,则设置injectH: true
# ESLint报错
https://eslint.vuejs.org/user-guide/#faq
如果您使用 JSX,则需要在 ESLint 配置中启用 JSX。
"parserOptions": {
"ecmaVersion": 2020,
"ecmaFeatures": {
+ "jsx": true
}
}
2
3
4
5
6
# Vue jsx接收父组件传递过来的参数
在SFC中我们在子组件中通过定义props来接收,而在jsx中我们一样可以通过定义props来接收,而如果不想用props接收,则属性默认挂载在$attrs上
关于props和$attrs,Vue官网的说明 (opens new window)
Vue jsx 中事件名第一个单词可以大写,后面必须小写,比如:
onMousemove 、 onDblclick
或者统一小写 onmousemove、 ondblclick
# jsx中如何向上传递数据
emit传递,通过setup第二个参数ctx 中的emit向上传递
// 子组件
const handleInput = (e) => {
ctx.emit('do-input', e)
}
<el-input
vModel={this.name}
class="input"
onInput={this.handleInput}
></el-input>
// 父组件
const name = ref('name')
const doInput = (e) => {
name.value = e
}
<Mouse
render={(mouse) => <Cat mouse={mouse} />}
ondo-input={this.doInput}
></Mouse>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
emit event name 事件名最好用小写
# jsx语法使用elementui的el-table-column插槽功能
<template>
<el-table-column label="性别">
<template>
<el-input v-model="name"></el-input>
</template>
</el-table-column>
</template>
// 使用jsx
<el-table-column>
{<el-input vModel={this.name}></el-input>}
</el-table-column>
2
3
4
5
6
7
8
9
10
11
12
13
# JSX 具名插槽
JSX中使用插槽与createElement (opens new window)
// 子组件
<template>
<div>
<div class="header">
<slot name="header">header</slot>
</div>
<div class="body">
<slot></slot>
</div>
<div class="footer">
<slot name="footer">footer</slot>
</div>
</div>
</template>
// 父组件 使用插槽
<ChildVue>
{<div slot="header">this is body</div>}
{<div>this is body</div>}
{<div slot="footer">this is footer</div>}
</ChildVue>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
子组件使用jsx render
render() {
return (
<div>
<div class="header">{this.$slots.header}</div>
<div class="body">{this.$slots.default}</div>
<div class="footer">{this.$slots.footer}</div>
</div>
)
},
2
3
4
5
6
7
8
9
作用域插槽
<user :obj="'123'">
<template v-slot:default="slotProps">
{{ slotProps }}
{{ slotProps.obj }}
</template>
</user>
// 子组件
<slot name="default" :user="user" :obj="obj">header</slot>
props: {
obj: String
}
2
3
4
5
6
7
8
9
10
11
12
JSX作用域插槽
// 父组件 使用插槽
<UserVue
obj="this is obj1"
scopedSlots={{
default: (props) => {
console.log(props)
return <div style="backgroundColor:orange">{props.obj}今年</div>
},
footer: (props) => `text${props.obj}`,
}}
>
<div slot="header">header</div>
</UserVue>
// 子组件 定义插槽
// 这里使用?.() 去调用,是为了避免作用域插槽未使用时会报错
render() {
return (
<div>
<div class="header">{this.$slots.header}</div>
<div class="body" obj={this.obj}>
{this.$scopedSlots.default?.(this.$props)}
</div>
<div class="footer" obj={this.obj}>
{this.$scopedSlots.footer?.(this.$props)}
</div>
</div>
)
}
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
jsx中的v-if
{this.isShow ? <div>123</div> : <div>456</div>}
v-show(v-show正常生效)
<div vShow={true}>v-show指令</div>
v-for
{[1, 2, 3].map((item, index) => { return <p key={index}>{item}</p> })}
# JSX函数组件
import { ref } from '@vue/composition-api'
import { Button } from 'element-ui'
const b = ref('b')
/* eslint-disable */
const ComponentA = (props) => {
const a = ref('123')
console.log('a: ', a.value)
return (
<div>
<h3>{a.value}</h3>
<h4>{b.value}</h4>
<button
onClick={() => {
console.log(123)
}}
>
click me
</button>
<el-button
onClick={() => {
console.log('ele click')
}}
>
ele click
</el-button>
<Button
onClick={() => {
console.log('ele click')
}}
>
ele click
</Button>
</div>
)
}
export default ComponentA
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
# 使用TSX
报错:JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.ts(7026)
因为不存在全局类型 "JSX.Element"。
解决办法有俩种:
- 不使用严格的类型检查,即tsconfig.json 中设置 "strict": false
- "noImplicitAny": false, // 是否在表达式和声明上有隐含的any类型时报错
2.报错Cannot find name 'React'
tsconfig中"jsx"需要设置为"preserve"
# 在Vue3.0中使用JSX
Babel Plugin JSX for Vue 3.0 (opens new window)
yarn add @vue/babel-plugin-jsx -D
Vite使用JSX => https://cn.vitejs.dev/guide/features.html#jsx
vite.config.ts
export default defineConfig({
plugins: [
vue(),
vueJsx(),
})
2
3
4
5
此外编辑器不认识tsx标签,报类型错误,需要在tsconfig.json中配置
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "vue",
}
}
2
3
4
5
6
tsx组件
import { defineComponent, ref } from "vue"
export default defineComponent({
setup() {
const count = ref(100)
const handleClick = () => {
count.value++
}
return () => (
<div>
<span>{count.value}</span>
<button onClick={handleClick}>click</button>
</div>
)
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
需要注意的事项:
- return需要返回一个Function,这与React不同
- 通过ref声明的变量在JSX中需要通过
.value去访问
# 接收props有两种方式
defineComponent方式
import { defineComponent, ref } from "vue" export default defineComponent({ props: { name: String }, setup(props) { const count = ref(100) const handleClick = () => { count.value++ } return () => ( <div> <span>{props.name}</span> <span>{count.value}</span> <button onClick={handleClick}>click</button> <input type="text" v-model={count.value} /> </div> ) } })1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在vue3中使用tsx需要在tsconfig中配置
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "vue",
}
2
3
4
5
那
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
2
这段配置呢
这段配置是 Vue2/手写 runtime JSX 的旧写法,在 Vue3 里正常情况下 不需要再写,甚至写了之后会和 @vitejs/plugin-vue-jsx 产生冲突。