JSX语法让程序员体验到前所未有的舒爽,而在Vue3中,快速过渡到TSX也是轻而易举的。除了下文介绍的TSX用法外,其他方面与Vue3的使用方式并无二致。TSX所需学习的内容并不繁杂,只是稍微改变了开发习惯,取消了模板语法,让我们能够像编写JavaScript一样书写页面。
通过执行npm init vue
来创建一个Vue工程。
工程地址
你可以在以下地址找到本文使用的工程示例:gitee.com/z-shichao/v…
组件定义:defineComponent
在Vue3中,defineComponent
是一个用于定义组件的函数API。它的作用主要有三个方面:
- 创建组件选项对象:
defineComponent
允许你定义一个包含组件配置信息的选项对象,如组件的属性、方法、生命周期钩子等。 - 类型推断支持:使用
defineComponent
可以帮助TypeScript更好地推断组件的类型,从而提高开发效率和代码的健壮性。 - 提供组件定义上下文:
defineComponent
内部对组件选项对象进行一些处理,包括生命周期钩子的转换、模板的编译等,从而提供了一个统一的组件定义上下文,使得组件的定义更加清晰和一致。
这里有个专门讲解defineComponent的文章 :juejin.cn/post/699461…
tsx有种书写模式:函数语法,选项语法
1
2import { defineComponent } from "vue"
3export default defineComponent({
4 setup() {
5 return () => (<div>vue3+tsx</div>)
6 }
7})
1
2export default defineComponent(() => {
3 return () => (<div>vue3+tsx</div>)
4})
使用选项语法还是函数语法主要取决于个人习惯。下文示例将使用选项式进行讲解。
注意:返回的是一个函数,在函数中返回的是一个JSX元素。JSX元素中必须有一个根标签。如果不想使用根标签,可以使用<></>
代替,该标签不会在页面中渲染。
插值语法
1export default defineComponent({
2 setup() {
3 let text = ref<string>('我是文本内容')
4 return () => (<div>{text.value}</div>)
5 }
6})
注意:使用ref
定义响应式数据时,在模板中需要使用.value
来访问。
事件绑定
1export default defineComponent({
2 setup() {
3 let text = '我是文本内容'
4 return () => (<>
5 <div >{text}</div>
6 <button onClick={() => { alert('您点击了我')}}>点我</button>
7 </>)
8 }
9})
事件修饰符
后边加驼峰事件修饰符
1export default defineComponent({
2 setup() {
3 let text = '我是文本内容'
4 return () => (<>
5 <div >{text}</div>
6 <button onClickStop={() => { alert('您点击了我')}}>点我</button>
7 </>)
8 }
9})
注意:使用大括号{}
进行插值,使用on+事件名
(小驼峰命名法)进行事件绑定,自定义事件也是一样的。
jsx中移除了部分指令:v-bin、v-for、v-if
v-bind
:使用大括号{}
进行包裹
1export default defineComponent({
2 setup() {
3 let text = '我是文本内容'
4 let style = {
5 background: 'red'
6 }
7 return () => (<>
8 <div style={style} >{text}</div>
9 <button onClick={() => { alert('您点击了我') }}>点我</button>
10 </>)
11 }
12})
v-for
:使用数组方法map
1export default defineComponent({
2 setup() {
3 let items = ['张三', '李四', '王五']
4 return () => (<>
5 {items.map((item) => <div>{item}</div>)}
6 </>)
7 }
8})
v-if
:使用三元表达式
1export default defineComponent({
2 setup() {
3 let isShow = ref<boolean>(true)
4 return () => (<>
5 <div>{isShow.value ? <div>我出来饿了</div> : ''}</div>
6 <button onClick={() => { isShow.value = !isShow.value}}>点我</button>
7 </>)
8 }
9})
注意:js要写在{}
里面,返回值是是一个可渲染的元素或元素组成的数组。
插槽
默认插槽
1import { defineComponent, ref } from "vue"
2export default defineComponent({
3 setup(props, { slots }) {
4 let text = ref<string>('我是默认插槽')
5 return () => (<Child>{text.value}</Child>)
6 }
7})
8let Child = (props: any, { slots }: any) => {
9 return <div>{slots.default()}</div>
10}
具名插槽
1import { defineComponent, ref } from "vue"
2export default defineComponent({
3 setup(props, { slots }) {
4 let text = ref<string>('我是默认插槽')
5 return () => (<Child>
6 {{
7 default: () => text.value,
8 name1: () => '我是插槽1',
9 name2: () => '我是插槽2'
10 }}
11 </Child>)
12 }
13})
14let Child = (props: any, { slots }: any) => {
15 return <div>
16 <div>{slots.default()}</div>
17 <div>{slots.name1()}</div>
18 <div>{slots.name2()}</div>
19 </div>
20}
作用域插槽
1export default defineComponent({
2 setup(props, { slots }) {
3 let text = ref<string>('我是默认插槽')
4 return () => (<Child>
5 {{
6 default: () => text.value,
7 name1: () => '我是插槽1',
8 name2: (parms: string) => <div>
9 {parms}
10 </div>
11 }}
12 </Child>)
13 }
14})
15let Child = (props: any, { slots }: any) => {
16 let parms: string = '我是参数'
17 return <div>
18 <div>{slots.default()}</div>
19 <div>{slots.name1()}</div>
20 <div>{slots.name2(parms)}</div>
21 </div>
22}
还可以通过props传参向子组件传递元素
1import { defineComponent, ref } from "vue"
2export default defineComponent({
3 setup(props, { slots }) {
4 let text = ref<string>('我是默认插槽')
5 return () => (<Child
6 childs={[
7 <div >我是一</div>,
8 <div >我是二</div>
9 ]}></Child>)
10 }
11})
12let Child = (props: any, { slots }: any) => {
13 return <div>
14 <div>{props?.childs[0]}</div>
15 <div>{props?.childs[1]}</div>
16 </div>
17}
使用css scoped
在Vue 3 中,如果想要在 TSX 文件中使用 CSS scoped 样式,通常不能直接在 TSX 文件中实现 scoped 样式。你可以在 .vue
单文件组件中的 <script>
标签中编写 TSX 代码,并且将 <script>
标签的 lang
属性设置为 'tsx'
,然后在 <style>
标签中添加 scoped
属性来实现 scoped 样式。
1<script lang="tsx">
2import { defineComponent } from "vue"
3export default defineComponent({
4 setup() {
5 return () => (<>
6 <div class='text'>css scoped</div>
7 </>)
8 }
9})
10</script>
11<style scoped>
12.text{
13 background: red;
14}
15</style>
vue3+tsx中定义props:
1import { defineComponent, PropType } from "vue"
2export default defineComponent({
3 setup() {
4 return () => (<>
5 <div>我是父组件</div>
6 <Text name={'我 是 传进来的name'} />
7 </>)
8 }
9})
10
11const Text = defineComponent({
12 props:{
13 name:{
14 type:String as PropType<string>,
15 required:true
16 }
17 },
18 setup(props) {
19 return () => (<>
20 <div>{props.name}</div>
21 </>)
22 }
23
24})
25import { defineComponent ,PropType} from "vue"
26export default defineComponent({
27 setup() {
28 return () => (<>
29 <Text name='我 是 传进来的name' />
30 </>)
31 }
32})
注意:在tsx中定义类型时,只能定义比较笼统的Object、String等,这种类型为什么比较笼统呢,因为在js语言中Object在任何变量的原型上都存在,当你定义一个Object时他就会和any类型时类似的,其他类型同理,当我们想要定义某个类型时,我们只能使用as+ PropType的形式。
简化props的使用:
首先要了解定义string会报错呢,因为我们使用了defineComponent帮我们进行了类型推断定义。那我们可不可以不使用,自己使用ts来规范我们的类型呢。当然可以!!!
方案一:
不适用defineComponent 自己使用ts来进行类型定义,但只支持函数语法和react用法几乎一样
可以看到使用ts也可以进行类型定义,并且更为简便。当然参数在子组件里也是可以定义接收的。缺点就是jsx中是不能使用这种方式的。
1import { defineComponent } from "vue"
2export default defineComponent({
3 setup() {
4 return () => (<>
5 <div>我是父组件</div>
6 <Text name='张三'/>
7 </>)
8 }
9})
10interface Props {
11 name: string,
12 age?: number
13}
14const Text = (props: Props, ctx: any) => {
15 return <div>{props.name}</div>
16}
方案二:
混编 这个有篇文章可以看一下 juejin.cn/post/714305…
方案三:
使用# Vue Macros构建项目
vue-macros.dev/zh-CN/guide…
vue中jsx语法和模板语法的优缺点:
JSX语法:
优点:
- 更灵活的语法: JSX允许在模板中使用JavaScript表达式,使得模板更加灵活和表达能力更强,更容易阅读和理解JSX的代码。
- 组件逻辑更直观,易维护: JSX让组件的结构和逻辑更加紧密,因为你可以在组件中直接编写JavaScript逻辑,并且在同一个文件中可以写多个组件,将相关性较强的组件放在一起更易维护。
- 性能较好: 使用JSX编写Vue 3组件会比使用模板语法的组件具有更好的性能。因为JSX可以直接将组件转换为纯JavaScript 代码,而模板语法需要在运行时进行解析和编译。性能差异不大。
缺点:
- 学习曲线较陡: 对于新手来说,学习JSX语法可能需要一定的时间,特别是对于那些不熟悉React或其他使用JSX的框架的人来说。
- 与HTML混合: 有些开发者可能觉得在JavaScript中嵌入HTML的方式不够直观,因为它打破了HTML和JavaScript的分离原则。
- 与模板的差异:TSX(或 JSX)语法与 Vue 的模板语法有一定的差异,可能需要一定的适应时间,并且不支持一些模板特有的语法和指令。
模板语法:
优点:
- 易于学习和上手: 模板语法更接近传统的HTML,因此对于初学者来说更容易理解和上手。
- HTML代码直观: 模板语法使得HTML代码更加直观,可以更清晰地看到组件的结构。
- 编辑器支持良好: 大多数编辑器对Vue模板语法有良好的支持,提供语法高亮、自动补全等功能。
缺点:
- 表达能力受限: 模板语法相比于JSX在表达能力上受到一定的限制,因为因为它不能像JSX那样直接在模板中使用JavaScript表达式。
- 组件与逻辑分离: 使用模板语法时,组件的结构和逻辑可能会分离得较为明显,因为一些逻辑需要放在JavaScript中,而不是直接嵌入模板中。
总的来说,选择JSX还是模板语法取决于个人的偏好、团队的技术栈以及项目的需求。在Vue 3中,JSX语法的支持更加完善,因此如果你对JavaScript更为熟悉,或者项目需要更高的灵活性和表达能力,那么可以考虑使用JSX语法。而对于简单的项目或团队中大多数成员都熟悉HTML的情况下,模板语法可能更为适合。