您现在的位置是:首页 >学无止境 >Vue 3 第二十二章:组件十(组件高级特性-组件的渲染函数和JSX/TSX语法)网站首页学无止境
Vue 3 第二十二章:组件十(组件高级特性-组件的渲染函数和JSX/TSX语法)
文章目录
1. 渲染函数
渲染函数是一种将组件的模板转换成虚拟 DOM 的方法。在 Vue 中,我们可以选择使用渲染函数来代替模板语法。
渲染函数可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用JavaScript
的全部语言特性。在Vue3
中,我们可以使用 h
函数来创建虚拟 DOM 元素。h
函数接受三个参数:元素名、元素的属性和子元素。
下面是一个简单的示例代码:
// ChildComponent.vue
<script lang="ts">
import { h } from "vue";
export default {
render() {
return h("div", { class: "container", style: { color: "red" } }, [
h("h1", "Hello, world!"),
h("p", "h函数的基本使用"),
]);
},
};
</script>
// Parent.vue
<template>
<div>
<child-component></child-component>
</div>
</template>
<script setup lang="ts">
import ChildComponent from "./ChildComponent.vue";
</script>
渲染到页面上的效果如下:
在上面的代码中,我们定义了一个渲染函数,并使用 h
函数来创建 div
元素、h1
元素和 p
元素。我们将这些元素作为子元素传递给 div
元素,并将 div
元素作为渲染函数的返回值。当我们在组件中使用渲染函数时,我们可以将其返回值渲染到页面上。
需要注意的是,在Vue3
中,我们可以选择使用模板语法或渲染函数来定义组件的模板。使用哪种方式取决于我们的个人喜好和项目的需求。
2. JSX / TSX 语法
JSX 是一种将XML
语法嵌入到JavaScript
中的语法。在 Vue3 中,我们可以使用JSX
语法来编写组件的模板。使用JSX
语法可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用JavaScript
的全部语言特性。
要在 Vue3 中使用 JSX 语法,我们需要安装 @vitejs/plugin-vue-jsx
插件,并在 vite.config.ts
文件中配置该插件。然后,我们就可以在组件中使用 JSX 语法了。
下面是一个简单的示例代码:
2.1. 基本使用
1)安装 @vitejs/plugin-vue-jsx
npm install @vitejs/plugin-vue-jsx -D
2)配置vite.config.ts
3)配置 tsconfig.json 文件
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
3)使用TSX
// 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.2. 使用 vue 中的语法
2.2.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.2.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;
2.2.3. v-show 使用
注意:使用 v-show 时,需要 .value 获取值
// index.tsx
// tsx 中使用 v-show 语法
import { ref } from "vue";
let flag = ref<boolean>(false);
const tsxDom = () => {
return (
<>
<div v-show={flag.value}>显示</div>
<div v-show={!flag.value}>隐藏</div>
</>
);
};
export default tsxDom;
2.2.4. v-if 不支持,实现v-if功能
使用v-if时,报错:
此时需要采用三元运算来实现 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;
2.2.5. v-for 不支持,实现 v-for 功能
// 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;
2.2.6. v-bind 不支持,模拟 v-bind
- 使用时,用 {} 包裹
- 以 data- 开头的自定义属性
// 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 data-name={item.name}>{item.age}</div>;
})
}
</>
);
};
export default tsxDom;
渲染效果如下:
2.2.7. v-on 使用
- jsx/tsx中事件的使用与 react 一致
- 以 on 开头
- 接收一个回调函数
注意:不能使用vue中的修饰符
// tsx 中模拟 v-on 语法
const handleClick = () => {
console.log("被点击了");
};
const tsxDom = () => {
return (
<>
<button onClick={() => handleClick()}>点击</button>
</>
);
};
export default tsxDom;
测试效果如下:
2.2.8. 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;
测试效果如下:
2.2.9. 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>
tsx 中通过emit给父组件传参
// 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;
2.2.10. 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} />
</>
);
},
});
测试效果如下:
总结
在 Vue3 中,我们可以选择使用模板语法、渲染函数或 JSX 语法来定义组件的模板。使用哪种方式取决于我们的个人喜好和项目的需求。渲染函数可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用 JavaScript 的全部语言特性。JSX 语法是一种将 XML 语法嵌入到 JavaScript 中的语法,可以让我们更加灵活地定义组件的模板。