关于 vue3 的 jsx 写法问题。
安装 @vitejs/plugin-vue-jsx vite 插件
yarn add @vitejs/plugin-vue-jsx -D
npm install @vitejs/plugin-vue-jsx -D
安装完成之后,在 vite.config.js 中进行配置:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [
vue(),
vueJsx(),
]
})
然后,即可编写 jsx 形式的 vue 单文件组件。
编写示例
基础实例
vue3 新增了 setup 这个组合式 api,这样就使得利用 jsx 编写 vue 组件时的风格非常类似于 react。在 setup 可以创建 state,接受 props,还可以直接返回 jsx。
import { defineComponent} from 'vue';
export default defineComponent({
setup() {
return <div>123456789</div>
}
});
props 和 state
在 vue3 中,存在 ref 和 reactive 这两个创建响应式状态的 api。由于内置的观察者模式,导致 vue 并不需要像 react 那样,提供 useMemo,useCallback 等根据条件判断是否更新的钩子。
ref 在 render 函数中,会自动解构,不需要再使用 ref.value 来获取里面的值。
推荐使用 ref,而不是 reactive。
props 则需要在 defineComponent 中使用 props 属性,指定 props,该组件才会接受对应的 props。 子组件不能在 setup 中返回 jsx,否则弹出警告,不渲染该子组件。
父组件接受子组件后,不可在 setup 中直接返回 ctx,或者 ctx.slots?.default?.() 计算后的值,第一种情况弹出警告,不渲染该子组件;第二种情况,该子组件不会响应式。正确的做法,应该是返回一个渲染函数。
子组件:
import { defineComponent, ref, reactive } from 'vue;
export default defineComponent({
name: 'TestB',
props: ['str'],
setup(this, props, ctx) {
const count = ref(0);
const { str } = props;
const reactiveCount = reactive({value: 0});
return {
str,
count,
reactiveCount,
}
},
render() {
const { count, reactiveCount, str } = this;
return <div>{count} - {reactiveCount.value} - {str}</div>
}
});
父组件:
import { defineComponent } from "vue";
export default defineComponent({
props: ['testStr'],
name: 'TestA',
setup(props, ctx) {
const { testStr } = props;
const renderFn = () => {
return <div>{ ctx.slots?.default?.() ?? "" }</div>
}
return {
testStr,
renderFn,
}
},
render() {
const { testStr, renderFn } = this;
return <div>你输入的字符串: {testStr}
<div>子组件</div>
<div>{ renderFn() }</div>
</div>
}
})
事件监听
@vitejs/plugin-vue-jsx 该插件默认将 @input 等相关模板语法,替换成了 onInput 等 jsx 形式。这里以点击事件举例。
import { defineComponent, ref, reactive } from "vue";
interface ITestAProps {
str: string;
}
export default defineComponent({
name: "TestA",
props: ["str"],
setup(this, props, ctx) {
const count = ref(0);
const { str } = props;
const reactiveCount = reactive({ value: 0 });
const onClick = () => {
count.value++;
};
const reactiveClick = () => {
reactiveCount.value++;
};
return {
str,
count,
reactiveCount,
onClick,
reactiveClick,
};
},
render() {
const { count, reactiveCount, str, onClick, reactiveClick } = this;
return (
<div>
<div onClick={onClick}>{count}</div>
<div onClick={reactiveClick}>{reactiveCount.value}</div>
<div>{str}</div>
</div>
);
},
});
插槽
vue3 中,可以使用快捷语法(以 element-plus 举例)
<el-popconfirm
title="确认删除最后一个配置内容?"
confirm-button-text="确认"
cancel-button-text="取消"
@confirm="
addNewValue.newConfigList.length > 1
? addNewValue.newConfigList.pop()
: ElMessage({
message: '不能删除所有配置内容',
type: 'error',
})
"
>
<template #reference>
<el-button type="danger"> 删除最后一个表单配置内容 </el-button>
</template>
</el-popconfirm>
jsx 形式中的 vue3 中,可以使用
import { defineComponent } from 'vue';
import { ElPopconfirm, ElButton } from 'element-plus';
export default defineComponent({
render() {
return <ElPopconfirm
title="确认删除最后一个配置内容?"
confirmButtonText="确认"
cancelButtonText="取消"
onConfirm={() => {
addNewValue.newConfigList.length > 1
? addNewValue.newConfigList.pop()
: ElMessage({
message: '不能删除所有配置内容',
type: 'error',
})
}}
v-slot={{
reference: () => <ElButton type="danger">删除最后一个表单配置内容</ElButton>
}}
>
</ElPopconfirm>
}
});
其余相关的属性
其余相关的属性,比如 v-model 啥的,还是按照原来的写法去写。
这里需要注意一点 v-model 如果使用 jsx 写法,不会自动双向绑定,需要自己去写对应的函数。
|