创建项目
使用vite创建vue3+ts项目
// npm
npm create vite@latest
// yarn
yarn create vite
Vite支持jsx
1、使vite支持jsx需要依赖插件@vitejs/plugin-vue-jsx
yarn add @vitejs/plugin-vue-jsx -D
2、插件配置
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import jsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), jsx()],
})
jsx组件的几种写法
1、箭头函数
1.无法定义响应式,只能做参数接收、事件处理
export default () => {
return (
<>
<div>jsx render</div>
</>
)
}
1.1、接收参数
import { Ref, defineComponent, ref } from 'vue'
export const RefExample = defineComponent({
setup() {
const count = ref(0)
return () => {
return (
<Counter count={count} />
)
}
},
})
// Counter.tsx
const Counter = ({count}: {count: Ref<number>}) => {
return (
<div>
{count.value}
</div>
)
}
1.2、事件响应
import { Ref, defineComponent, ref } from 'vue'
export const RefExample = defineComponent({
setup() {
const count = ref(0)
return () => {
return (
<Counter count={count} />
)
}
},
})
const Counter = ({count}: {count: Ref<number>}) => {
// 不能在此处定义响应式,无效
const countf = ref(0)
return (
<div>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={() => count.value++}>+</button>
{count.value}
{/* 此处响应事件无效 */}
<p>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={() => countf.value++}>+</button>
{countf.value}
</p>
</div>
)
}
1.3、事件回调
import { Ref, defineComponent } from 'vue'
export const RefExample03 = defineComponent({
setup() {
return () => (
<Counter count={count} onChange={(e) => console.log(e)} />
)
},
})
const Counter = ({count, onChange}: {count: Ref<number>, onChange: (e: number|string) => void}) => {
const handler = (e: Event) => {
onChange((e.target as HTMLInputElement).value)
}
return (
<div>
<input type='text' onInput={handler}>+</input>
</div>
)
}
1.4、数据双向绑定
import { Ref, defineComponent, ref } from 'vue'
export const RefExample04 = defineComponent({
setup() {
const count = ref(0)
return () => (
<Counter count={count} onChange={(e) => count.value = +e} />
)
},
})
const Counter = ({count, onChange}: {count: Ref<number>, onChange: (e: number|string) => void}) => {
const handler = (e: Event) => {
onChange((e.target as HTMLInputElement).value)
}
return (
<div>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={() => count.value++}>+</button>
<p>{count.value}</p>
<input type='text' onInput={handler}>+</input>
</div>
)
}
1.5、插槽
箭头函数无法使用slots, 使用slots需要使用setup或者render
import { defineComponent } from "vue";
export const SlotsExample = defineComponent({
setup() {
return () => (
<div class="flex space-x-4">
<SlotComp
header={<h1>header</h1>}
footer={<h1>footer</h1>}>
<h1>content</h1>
</SlotComp>
<SlotComp
header={<h1>header</h1>}>
<h1>content</h1>
</SlotComp>
</div>
)
}
})
const SlotComp = ({ header, footer }: { header: JSX.Element, footer?: JSX.Element }) => {
return (
<div class="bg-sky-200 inline-block p-4 rounded-md">
<div class="header">{header}</div>
{/* 箭头函数无法使用slots, 使用slots需要使用setup或者render */}
<div>content</div>
<div class="footer">{footer && footer}</div>
</div>
)
}
效果
1.6、模版语法
1.6.1、v-html
export const SlotsExample2 = () => {
return(
<div v-html="<button style='color: red'>hello h1</button>"></div>
)
}
2、setup
1.可以使用vue3的组合式api
2.可以使用响应式、生命周期、watch等钩子函数
3.可以使用箭头函数、render函数
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const count = ref(0)
return () => (
<div>
count: {count.value}
</div>
)
}
})
2.1、接收参数
import { Ref, defineComponent, ref } from 'vue'
export const RefExample = defineComponent({
setup() {
const count = ref(0)
return () => {
return (
<Counter count={count} />
)
}
},
})
// Counter.tsx
const Count = defineComponent({
// 进行参数绑定
props: {
count: {
type: Object as PropType<Ref<Number>>,
required: true
}
},
setup(props) {
return () => (
<div>
{props.count.value}
</div>
)
}
})
2.2、事件响应
import { Ref, defineComponent, ref } from 'vue'
export const RefExample = defineComponent({
setup() {
const count = ref(0)
return () => {
return (
<Counter count={count} />
)
}
},
})
// counter.tsx
const Count = defineComponent({
props: {
count: {
type: Object as PropType<Ref<number>>,
required: true
}
},
setup({ count }) {
return () => (
<div>
<p>{count.value}</p>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={() => count.value++}>+</button>
</div>
)
}
})
2.3、事件回调
export const RefExample02 = defineComponent({
setup() {
const count = ref(0)
return () => (
<Count count={count} onClickAction={(val) => console.log('--', val)} />
)
},
})
const Count = defineComponent({
props: {
count: {
type: Object as PropType<Ref<number>>,
required: true
}
},
// 定义向外暴露的函数
emits: ['clickAction'],
setup({ count }, { emit }) {
const handler = () => {
// 组件内触发函数
emit('clickAction', count.value + 1)
}
return () => (
<div>
<p>{count.value}</p>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={handler}>+</button>
</div>
)
}
})
2.4、响应式数据
export const RefExample = defineComponent({
setup() {
const count = ref(0)
const obj = reactive({
name: 'mz',
age: 18
})
// 监听数据变化
watch(count, (oldVal, newVal) => {
console.log('count发生改变', oldVal, newVal);
})
const changeInfo = () => {
count.value++
obj.name = 'hello mz'
}
return () => {
return (
<div>
<p>{JSON.stringify(obj)}</p>
<button onClick={changeInfo}>add</button>
</div>
)
}
},
})
2.5、数据的双向绑定
export const RefExample05 = defineComponent({
setup() {
const count = ref(0)
return () => (
<div>
<Input />
{/* 自定义数据双向绑定 */}
<InputField v-model:value={count.value} />
</div>
)
},
})
// 数据双向绑定
const Input = defineComponent({
setup() {
const inputRef = ref('')
return () => <div>
<p>{inputRef.value}</p>
<input type="text" v-model={inputRef.value} />
</div>
}
})
// 自定义数据双向绑定
const InputField = defineComponent({
props: ['value'],
setup(props, { emit }) {
const handler = () => {
let newValue = props.value
console.log(newValue++);
emit('update:value', ++newValue)
}
return () => <div>
<p>{props.value}</p>
<button class="px-8 py-2 mr-2 rounded bg-sky-300" onClick={handler}>+</button>
</div>
}
})
2.6、向组件外暴露方法
import { defineComponent, onMounted, ref } from "vue";
export const ExposeExample = defineComponent({
setup() {
const exposeRef = ref<any>(null)
onMounted(() => {
exposeRef.value.testF()
})
return () => (
<Expose ref={exposeRef}>
<h1>dd</h1>
</Expose>
)
}
})
const Expose = defineComponent({
setup(_, { expose, slots }) {
// 向组件外暴漏方法
expose({
testF: () => {
console.log('test');
}
})
const Child = (slots.default) ? (slots.default) as any as () => JSX.Element : () => null
return () => (
<div>
<Child />
</div>
)
}
})
2.7、插槽
2.7.1、默认插槽
export const Slots = defineComponent({
setup() {
return () => (
<SlotDefaultComponent>
<h1>default slot</h1>
</SlotDefaultComponent>
)
}
})
const SlotDefaultComponent = defineComponent({
setup(_, { slots }) {
return () => (
<>
{slots.default?.()}
</>
)
}
})
2.7.2、具名插槽
import { defineComponent } from "vue";
export const Slots = defineComponent({
setup() {
// 对象形式
return () => <SlotNameComponent>
{{
default: () => <h1>default</h1>,
header: () => <h1>header</h1>,
footer: () => <h1>footer</h1>,
}}
</SlotNameComponent>
// slots形式
// return () => (
// <SlotNameComponent v-slots={{
// default: () => <h1>default</h1>,
// header: () => <h1>header</h1>,
// footer: () => <h1>footer</h1>,
// }}>
// </SlotNameComponent>
// )
}
})
const SlotNameComponent = defineComponent({
setup(_, { slots }) {
return () => (
<div>
<div>
{slots.header?.()}
</div>
<div>
{slots.footer?.()}
</div>
<div>
{slots.default?.()}
</div>
</div>
)
}
})
2.7.3、插槽传参
export const Slots = defineComponent({
setup() {
return () => <SlotComponent>
{{
default: () => <h1>default</h1>,
header: () => <h1>header</h1>,
footer: ({ msg }: { msg: string}) => <h1>footer-{msg}</h1>,
}}
</SlotComponent>
// return () => (
// <SlotComponent v-slots={{
// default: () => <h1>default</h1>,
// header: () => <h1>header</h1>,
// footer: ({ msg }: { msg: string}) => <h1>footer-{msg}</h1>,
// }}>
// </SlotComponent>
// )
}
})
const SlotComponent = defineComponent({
setup(_, { slots }) {
return () => (
<div>
<div onClick={withModifiers(() => {
console.log(this)
}, ['self'])} />
<div>
{slots.header?.()}
</div>
<div>
{slots.footer?.({ msg: 'footer msg' })}
</div>
<div>
{slots.default?.()}
</div>
</div>
)
}
})
3、render渲染函数
1.可以与vue2、vue3语法配合使用
2.render中访问属性或者事件需通过
this
const RenderExample = defineComponent({
render() {
return (
<>
<p>RenderExample</p>
</>
)
}
})
3.1、与setup配合
export const RenderExample2 = defineComponent({
setup() {
const inputVal = ref(12)
const obj = reactive({
name: 'mz',
age: 19
})
const say = (msg: string) => {
console.log('msg: ', msg);
}
return {
inputVal,
...toRefs(obj),
say
}
},
render() {
return (
<div>
<div>{this.inputVal}</div>
<div>{this.name}</div>
<div>{this.age}</div>
<button onClick={() => this.say('hello')}>hello</button>
</div>
)
},
})
3.2、与vue语法配合
export const RenderExample3 = defineComponent({
data() {
return {
inputVal: 12,
obj: {
name: 'mz',
age: 19
}
}
},
methods: {
say(msg: string) {
console.log('msg: ', msg);
}
},
render() {
const { name, age } = this.obj;
return (
<div>
<div>{this.inputVal}</div>
<div>name: {name}</div>
<div>age: {age}</div>
<button onClick={() => this.say('hello')}>hello</button>
</div>
)
},
})