JSX 是什么
JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。在Vue中可以更灵活的组装组件。
如何在 Vue 项目中使用
- 项目中安装
@vitejs/plugin-vue-jsx vite.config.ts中配置
import vueJsx from '@vitejs/plugin-vue-jsx'
// ...
plugins: [
vue(),
vueJsx(),
]
// ...
单文件组件使用 tsx/jsx 文件后缀。
ShowTime.tsx
import { defineComponent } from 'vue'
export default defineComponent({
name: 'ShowTime',
props: {
local: {
type: String,
default: 'en',
},
},
setup(props) {
return () => <div>{props.local} Hello, Vue!</div>
},
})
函数式组件
const showUser = props => {
return (
<div>
<i style={{ backgroundImage: `url(${face.value})` }}></i>
<span>{props.name}</span>
</div>
)
}
组件样式隔离
JSX 没有 scoped 隔离样式需要是用 CSS Modules (vite 脚手架已支持)来隔离组件之间的 class 名。
比如使用 scss 需要将文件后缀改成.module.scss
styles.module.scss
.user-wrap {
width: 160px;
height: 28px;
display: flex;
// ...
.face {
display: inline-block;
width: 24px;
// ...
}
}
在项目中使用:
import { computed } from 'vue'
import styles from './styles.module.scss'
export function useUser() {
const face = computed(
() =>
'//i2.hdslb.com/bfs/baselabs/79ba68b71fb31194833852a95799a81c3e16443b.jpg@100w_100h_1c_1s_!web-avatar-nav.avif'
)
const showUser = props => {
return (
<div class={styles['user-wrap']}>
<i class={styles.face} style={{ backgroundImage: `url(${face.value})` }}></i>
<span>{props.name}</span>
</div>
)
}
return {
showUser,
}
}
props 和 emit 使用
setup 函数默认接收两个参数 1. props 2. ctx 上下文 其中包含 slots、attrs、emit 等
export default defineComponent({
props: {
local: {
type: String,
default: 'en',
},
},
setup(props, ctx) {
return (
<div>
<span>{props.local}</span>
</div>
)
},
})
const showUser = (props, ctx) => {
const onClickAvatar = () => {
ctx.emit('handleAvatar')
}
return (
<div class={styles['user-wrap']}>
{/* ... */}
<span>{props.name}</span>
</div>
)
}
常用指令
- v-text、v-html、v-show 没变化
- v-if 可以使用三元或者函数组件返回
// ...
setup(props, ctx) {
const isShow = ref(true)
return () => (
<div>
{isShow.value ? <p>1</p> : <p>2</p>}
</div>
)
}
- v-for 使用原生 js 遍历
const data = [
{
id: 1,
name: '武汉',
},
{
id: 2,
name: '东高',
},
]
return () => (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
)
- v-model 语法支持 写法上有区别
// Parent.tsx
const ageValue = ref('0')
// 指定绑定值和修饰符
<CustomInput v-model={[ageValue.value, 'modelValue', ['trim']]} /> {ageValue.value}
// CustomInput.tsx
export default defineComponent({
name: 'CustomInput',
props: {
modelValue: {
type: String,
default: '',
},
},
emits: ['update:modelValue'],
setup(props, ctx) {
const value = computed({
get() {
return props.modelValue
},
set(value) {
ctx.emit('update:modelValue', value)
},
})
return () => <input v-model={value.value} />
},
})
事件
事件不使用前缀 @, 使用 on
事件修饰符需要使用 withModifiers 方法,withModifiers 方法接收两个参数,第一个参数是绑定的事件,第二个参数是需要使用的事件修饰符
<div onClick={() => clickBox('box wrap')}>
<span>box wrap</span>
<div onClick={withModifiers(() => clickBox('box2'), ['stop'])}>box2</div>
</div>
插槽
没有 slot 标签的,定义插槽需要使用{}或者renderSlot函数
// 子组件定义插槽
import { defineComponent, renderSlot } from 'vue'
export default defineComponent({
setup(props, { slots }) {
return () => (
<div>
{renderSlot(slots, 'default')}
{slots.title?.()}
</div>
)
},
})
// 父组件使用插槽
const slots = {
default: () => <div>default slot</div>,
title: () => <div>title slot</div>,
}
return () => (
<div>
{/* slot */}
<VSlot v-slots={slots} />
</div>
)
作用域插槽
// 子组件定义插槽
export default defineComponent({
setup(props, { slots }) {
return () => (
<div>
{renderSlot(slots, 'default', { uname: '召唤师', message: 'Hello' })}
{slots.title?.({ uname: '召唤师', message: 'Hello' })}
</div>
)
},
})
// 父组件使用插槽
const slots = {
default: slotProps => (
<div>
default slot {slotProps.uname} {slotProps.message}
</div>
),
title: slotProps => (
<>
<span>title slot {slotProps.uname}</span>
<span>{slotProps.message}</span>
</>
),
}
return () => (
<div>
{/* slot */}
<VSlot v-slots={slots} />
</div>
)