前言
欢迎关注同名公众号《
熊的猫
》,文章会同步更新,也可快速加入前端交流群!
大多数 Vue 开发者都习惯使用 template 模板语法 ,因为 template 模板语法 具有如下优点:
- 熟悉的类 HTML 结构
- 模板语法可以像 HTML 一样进行布局和设计,上手快、学习成本比低
- 更简洁的写法
- 例如,可以在模板中使用各种 修饰符 来达到简化编写代码的过程
- 结构与逻辑分离
- 元素结构和逻辑并没有杂糅在一起,因此结构上更简洁明了
- 提供更好的性能
- Vue3 中对模板语法在 编译阶段 进行的各种优化,使得其性能更好
但即便如此,在某些场景下还是不得不在 Vue 中使用 jsx 语法 实现需求, 例如开发 内部组件库 选择的编写形式就是 jsx 语法 等。
而对于习惯使用 template 模板语法 的开发者并不是轻易的就能转换到相应 jsx 语法 ,因此本文就列举一些 template 模板语法 中对应的 jsx 语法 应该怎么写。
从 template 到 jsx
插值表达式(文本插值)
template 语法
最基本的数据绑定形式就是 文本插值 ,它使用的是 Mustache 语法 (即 {{}}
),双大括号 中的内容最终 会将数据解释为纯文本。
js
<span>Message: {{ msg }}</span>
jsx 语法
而 JSX 中使用 文本插值 就从 双大括号 {{ }}
变成 单大括号 {}
js
<span>Message: { msg }</span>
原始 HTML
template 语法
双大括号 会将数据解释为纯文本,而不是 HTML ,因此若想在模板中 插入 HTML ,我们需要使用 v-html
指令:
js
// html 字符内容
rawHtml = '<span>hello!</span>'
// 最终变成纯文本
<p>Using text interpolation: {{ rawHtml }}</p>
// 最终渲染为 html 结构
<div>Using v-html directive: <p v-html="rawHtml"></p></div>
jsx 语法
而在 jsx 语法 中就更直接了,我们直接通过 变量 的形式来写 html 结构 配合上 文本插值 {}
即可:
js
// 最终变成纯文本
cosnt rawHtml = '<span>hello!</span>'
<p>Using text interpolation: { rawHtml }</p>
// 最终渲染为 html 结构
cosnt rawHtml = <span>hello!</span>
<div>Using v-html directive: { rawHtml }</div>
条件渲染
template 语法
在模板语法中和 条件渲染 相关的可以直接使用指令 v-if / v-show 来实现:
js
<p v-show="isShow">hello world!</p>
<p v-show="!isShow">hello bros!</p>
<p v-if="isShow">hello world!</p>
<p v-if-else>hello bros!</p>
jsx 语法
而在 jsx 语法 中我们就不能使用 指令形式 了,取而代之的是 JavaScript 中的 if-else、&&、||、三元表达式
等形式:
if-else
js
const content = (isShow) => {
if(isShow){
return <h1>hello world!</h1>
}else{
return <div>hello bros!</div>
}
}
&&
js
{ isShow && <div>hello world!</div> }
{ !isShow && <div>hello bros!</div> }
||
js
const content1 = <h1>hello world!</h1>
const content2 = <h1>hello world!</h1>
<div>{ content1 || content2 } </div>
三元表达式
js
const content1 = <h1>hello world!</h1>
const content2 = <h1>hello world!</h1>
<div>{ isShow ? content1 : content2 } </div>
列表渲染
tempalte 语法
在 template 模板 中可以通过 v-for 指令 来快速实现列表渲染:
js
<ul>
<li v-for="item in list" :key="item.key">{{ item.text }}</li>
</ul>
jsx 语法
在 jsx 语法 中通常是使用 Array.prototype.map() 方法来实现列表渲染,原因就在于这个遍历数组的方法返回值也是数组:
js
<ul>
{
list.map((item) => (
<li key={item.key}>{ item.text }</li>
))
}
</ul>
style 外部样式
template 语法
在 .vue 文件 中可以通过 <style>
标签来 编写样式 或 导入外部样式 ,还可以直接通过设置 scope
实现局部样式:
js
<script setup lang="ts">
import { ref } from "vue";
import ChcekBox from "./components/CheckBox";
const checkResult = ref(false);
</script>
<template>
<ChcekBox v-model="checkResult">选项</ChcekBox>
</template>
<style scope>
@import './index.less';
</style>
jsx 语法
而在一个 .jsx / .tsx
文件中由于不存在 <style>
元素,因此无法通过其来编写或导入样式,或者通过 scope
实现局部样式,可通过如下方式导入:
-
直接 import 导入
jsimport { defineComponent, ref } from 'vue' import './index.less' export default defineComponent({})
-
通过 CSS Module 导入
jsimport { defineComponent, ref } from 'vue' import styleModule from './index.module.less' export default defineComponent({ setup() { return () => ( <label class={styleModule.abs}>hello world!</label> ) })
事件绑定
tempalte 语法
在模板语法中绑定事件可以使用 v-on(简写 @) 来实现,并且可以在模板中 直接传递参数 给目标事件,也可以配合使用 事件修饰符,支持内联事件等等。
绑定处理函数
js
<!-- 方法处理函数 -->
<button v-on:click="doThis"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
使用修饰符
js
<!-- 链式调用修饰符 -->
<button @click.stop.prevent="doThis"></button>
传递参数
js
<!-- 传参 -->
<button @click="doThis($event, params)"></button>
内联事件
js
<!-- 传参 -->
<button @click="count++"></button>
jsx 语法
上述写法在 jsx 语法 中的对应写法具体如下:
绑定处理函数
需要使用 on + [eventName] 的形式来绑定事件,可使用 或 不使用 驼峰形式 ,但当使用 typescript 时建议使驼峰形式,否则会有提示:
js
<!-- 驼峰 -->
<button onClick={doThis}></button>
<!-- 非驼峰 -->
<button onclick={doThis}></button>
使用修饰符
在 jsx 语法 中不能直接使用 .stop 形式的事件修饰符,需要通过 withModifiers
函数来实现,其支持 事件和按键修饰符:
js
<!-- withModifiers -->
<button onClick={withModifiers(doThis, ['prevent'])}></button>
传递参数
在 jsx 语法 中不能像在模板中使用 handleAction($event, params)
的方式来实现传参,因为这种写法在 jsx 中属于调用,因此相当于把函数返回值作为事件绑定到目标元素上,大多数情况下会抛出异常(即返回值不一定为函数
):
-
使用
bind
实现传参jsconst bindEvent = doThis.bind(tarrget) <!-- 传参 --> <button onClick={bindEvent}></button>
-
使用
箭头函数
实现传参js<!-- 传参 --> <button onClick={ (parms) => bindEvent(parms)}></button>
内联事件
在 jsx 语法 并不支持内联事件的写法,因此可以使用箭头函数来包裹:
js
<!-- 方式一 -->
<button onClick={ () => count++ }></button>
<!-- 方式一 -->
const addCount = () => count++
<button onClick={ addCount }></button>
双向绑定 v-model
template 语法
v-model
可以在组件上使用以实现 双向绑定:
- 在 原生元素 上使用
v-model
会被编译为value 属性
和input 事件
- 在 组件 上使用
v-model
会被编译为modelValue 属性
和update:modelValue 事件
- 支持自定义
v-model
绑定的属性名
和事件名
js
// 常见表单
<input v-model="searchText" />
// 自定义组件
<CustomInput v-model="searchText" />
// 自定义 v-model 名
<MyComponent v-model:title="bookTitle" />
jsx 语法
正常绑定
js
<CustomInput v-model={searchText} />
自定义名称
jsx 语法 中不存在类似 v-model:title
的命名形式,因此我们给 v-model
一个数组,如 [title, 'titleAlias']
:
- 数组的第一个参数就是要绑定的 值
- 数组的第二个参数就是要绑定的 自定义名称
js
<Custom v-model={[title, 'titleAlias']} />
slot 插槽
template 语法
在模板语法中可以 <slot>
元素来定义 插槽出口 ,用于标示父元素提供的 插槽内容 的渲染位置:
默认插槽
js
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
具名插槽
js
<button class="fancy-btn">
<slot name="content"></slot> <!-- 插槽出口 -->
</button>
动态插槽
js
<div>
<!-- 动态插槽 -->
<template v-slot:[dynamicSlotName]> ... </template>
<!-- 缩写为 -->
<template #[dynamicSlotName]> ... </template> </base-layout>
</div>
作用域插槽
js
<!-- MyComponent 模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
<!-- 父组件模板 -->
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
jsx 语法
由于 jsx 语法 中不存在 <slot>
元素,因此只能通过如下方式来渲染插槽内容:
-
从 SetupContext 中获取
jsdefineComponent({ setup(props, { slots }) { return <div> { slots.default && slots.default() } { slots.nameSlot && slots.nameSlot() } </div> } })
-
使用 useSlot() 方法
jsimport { defineComponent, renderSlots } from 'vue' defineComponent({ setup(props, context) { const slots = useSlots(); return <div> { renderSlot(slots, 'default') } </div> } })
-
使用 renderSlot() 方法
jsimport { defineComponent, renderSlot } from 'vue' defineComponent({ setup(props, { slots }) { return <div> { renderSlot(slots, 'default') } </div> } })
-
使用 Scoped Slots 作用域插槽
js<!-- MyComponent 模板 --> defineComponent({ setup(props, { slots }) { const slotParams = { name: 'hello' }; return <div> { slots.default && slots.default(slotParams) } </div> } }) <!-- 父组件模板 --> defineComponent({ setup(props, { slots }) { return <div> <MyComponent v-slot="slotProps"> {{ slotProps.name }} </MyComponent> </div> } })
最后
欢迎关注同名公众号《
熊的猫
》,文章会同步更新,也可快速加入前端交流群!
以上就是本文的全部内容了,由于前段时间开始写 jsx 语法,期间总是需要来回查找对应的正确写法,于是就打算整理成文章顺便分享出来.
希望本文对你有所帮助!!!