一、什么是 JSX / TSX
JSX / TSX 语法
JSX 是一种将XML语法嵌入到JavaScript中的语法。在 Vue3 中,我们可以使用JSX语法来编写组件的模板。使用JSX语法可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用JavaScript的全部语言特性。
let jsx = <h1>hello world</h1>;
二、在vue3中使用
1、基本使用
// index.tsx
const tsxDom = () => {return (<><div class="container"><h1>Hello, world!</h1><p>This is a tsx.</p></div></>);
};export default tsxDom;
// tsx.vue
<template><div><tsxDom></tsxDom></div>
</template><script setup lang="ts">
import tsxDom from "@/tsx/index";
</script><style scoped></style>
2、使用 vue 中的语法
1)插值表达式
// index.tsx
// tsx 中使用{}语法
import { ref } from "vue";let hello = ref<string>("Hello, world!");
const tsxDom = () => {return (<><div><h1>{hello.value}</h1><p>This is a tsx.</p></div></>);
};export default tsxDom;
2)v-model
注意:使用 v-model 时,需要 .value 获取值
// index.tsx
// tsx 中使用 v-model 语法
import { ref } from "vue";
let hello = ref<string>("Hello, world!");const tsxDom = () => {return (<><input type="text" v-model={hello.value} /></>);
};export default tsxDom;
- v-if
注意:在jsx中不支持v-if的指令,需要使用三元表达式
// tsx 中模拟 v-if 语法
import { ref } from "vue";
let flag = ref<boolean>(false);const tsxDom = () => {return <>{flag.value ? <div>显示</div> : <div>隐藏</div>}</>;
};export default tsxDom;
4) v-for
注意:jsx中不支持v-for语法,需要使用js的array.map函数实现
// tsx 中模拟 v-for 语法
import { reactive } from "vue";
interface Data {name: string;age: number;
}
let list = reactive<Data[]>([{ name: "张三", age: 12 },{ name: "李四", age: 18 },
]);const tsxDom = () => {return (<>{{/* 此处完全遵循 react 语法即可 */}list.map((item) => {return <div>{item.name}</div>;})}</>);
};export default tsxDom;
5)v-bind
注意:jsx语法中不支持 v-bind,可以采用{}包裹变量来实现
// tsx 中模拟 v-bind 语法
import { reactive } from "vue";interface List {name: string;age: number;
}let list = reactive<List[]>([{name: "张三",age: 10,},{name: "李四",age: 18,},
]);const tsxDom = () => {return (<>{list.map((item) => {return <div key={item.name}>{item.age}</div>;})}</>);
};export default tsxDom;
- v-on
注意:jsx语法中不支持v-on,jsx/tsx中事件的使用与 react 一致
以 on 开头,接收一个回调函数,不能使用vue中的修饰符
// tsx 中模拟 v-on 语法const handleClick = () => {console.log("被点击了");
};const tsxDom = () => {return (<><button onClick={() => handleClick()}>点击</button></>);
};export default tsxDom;
7)Props
- 父子组件传值
- 通过父组件传递参数
- jsx/tsx接收props并使用
// tsx.vue
<template><div><tsxDom :userName="userName" :userAge="userAge"></tsxDom></div>
</template><script setup lang="ts">
import { ref } from "vue";
import tsxDom from "@/tsx/props";const userName = ref<string>("张三");
const userAge = ref<number>(18);
</script><style scoped></style>
// props.tsx
// tsx 中接收及使用props
interface Props {userName?: string;userAge?: number;
}const handleClick = (props: Props) => {console.log(props);
};const tsxDom = (props: Props) => {return (<><div>{props.userName}</div><div>{props.userAge}</div><button onClick={() => handleClick(props)}>点击</button></>);
};export default tsxDom;
8)Emit
- jsx/tsx组件中通过第二个参数 ctx 拿到 emit 方法
- 通过 emit() 方法传递一个自定义事件以及参数
- 父组件通过 v-on:自定义事件 拿到传递过来的值
// tsx.vue
<template><div><tsxDom:userName="userName":userAge="userAge"@on-click="handleClick"></tsxDom></div>
</template><script setup lang="ts">
import { ref } from "vue";
import tsxDom from "@/tsx/emit";const userName = ref<string>("张三");
const userAge = ref<number>(18);const handleClick = (value: string) => {console.log(value); // 张三
};
</script><style scoped></style>
// emit.tsx
interface Props {userName?: string;userAge?: number;
}const handleClick = (props: Props, ctx: any) => {ctx.emit("on-click", props.userName);
};const tsxDom = (props: Props, ctx: any) => {return (<><div>{props.userName}</div><div>{props.userAge}</div><button onClick={() => handleClick(props, ctx)}>点击</button></>);
};export default tsxDom;
9)v-slot
- 子组件通过 centex.slots 传参
- 父组件通过 v-slots={} 接收参数
import { defineComponent, reactive } from "vue";const state = reactive({ name: "张三" });
const list = reactive([1, 2, 3]);export const tsxDom = (props: any, ctx: any) => (<>{/* 默认插槽 */}<div>{ctx.slots.default ? ctx.slots.default() : "默认"}</div>{/* 声明名称为state的插槽 并传值,不传值为ctx.slots.state() */}<div>{ctx.slots.state && ctx.slots.state(state)}</div>{/* 声明名称为list的插槽 并传值,不传值为slots.list() */}<div>{ctx.slots.list && ctx.slots.list(list)}</div></>
);export default defineComponent({setup() {type State = {name: string;};// 使用插槽const slots = {// default: () => <div>匿名插槽</div>,state: (state: State) => <div>{state.name}</div>,list: (list: number[]) => list.map((item) => <div>{item}</div>),};return () => (<>{/* 引用组件, 使用插槽:v-slots={slots} */}<tsxDom v-slots={slots} /></>);},
});
3、ts中报错
需要在 tsconfig.json 增加:
{"compilerOptions": {"jsx": "preserve","jsxImportSource": "vue"}
}