Code前端首页关于Code前端联系我们

Vue3为什么推荐用TSX?和SFC相比有啥优势劣势?实际开发怎么选?

terry 15小时前 阅读数 228 #Vue
文章标签 Vue3 TSXVue3 SFC

最近在前端技术群里逛,发现好多刚从Vue2转3、或者从React跳过来练手Vue的小伙伴,都在纠结组件写作用法:官方文档明明说SFC还是主流,但Vue3对TSX的支持突然变这么好,甚至在某些高级场景里专门提了TSX更顺手,到底该不该学TSX、什么时候用它呢?今天咱们就从底层逻辑、实际编码体验、具体场景适配几个方面,好好聊透这个事儿。

为什么Vue3突然“推”起了TSX?

很多人可能会误解,以为Vue3是为了抢React的用户才硬加的TSX,其实根本不是——Vue3核心团队早在设计框架架构的时候,就把JSX/TSX当成了核心渲染API的“原生入口”之一,只是前期为了照顾老用户的迁移体验,重点还是放在了SFC的优化上。

我们都知道Vue2的JSX支持是靠@vue/babel-preset-app或者单独的插件实现的,那时候叫“Experimental JSX”,各种限制很多:插槽语法用render函数式写法、v-model得拆成modelValue和onUpdate:modelValue、ref自动解包只在模板里生效,TSX里还要手动加.value,体验确实拉胯。

但Vue3不一样,核心有几个颠覆性的改动给TSX铺了路: 第一个是VNode结构的重构,Vue2的VNode是个“全能选手”,既要存组件类型、props、插槽,还要存一堆指令的元信息,JSX插件很难直接和这种复杂结构对接;Vue3把VNode拆成了更轻量的扁平结构,指令也变成了编译时或者运行时的“辅助函数调用”,甚至连template编译出来的代码,本质上都是用TSX/JSX风格的h函数(不对,现在Vue3更推荐用createVNode,但SFC编译后会自动处理成内部优化过的版本)写的,这么一来,TSX就不再是“外部插件凑出来的东西”,而是和template同等级的渲染方式了。

第二个是TypeScript的深度集成,Vue2对TypeScript的支持是通过类组件或者Options API的装饰器实现的,类型提示弱,维护起来麻烦;Vue3的Composition API本身就是为TypeScript设计的,完全用函数式写法,类型推导非常顺滑,但SFC的模板是纯HTML+指令,虽然Vue3的Volar插件做了很多类型检查的优化,但还是有“天花板”——比如模板里调用函数的时候,参数的类型有时候推导不准,嵌套太深的组件props可能会丢,这些问题在纯TypeScript的TSX里根本不存在,因为TSX就是TypeScript的语法糖,所有的类型检查都是TypeScript原生做的,比Volar插件的黑盒检查要可靠得多。

第三个是Vite的普及,Vite支持预编译各种前端文件,包括TSX,而且编译速度比Webpack快太多了,以前用Webpack写Vue2的JSX,启动项目要等好几分钟,改个小地方也要等几秒热更新;现在用Vite写Vue3的TSX,启动和热更新几乎都是秒级的,开发体验提升了不止一个档次,这也是很多人现在愿意尝试TSX的重要原因。

Vue3 TSX和SFC的具体优劣势对比

聊完了为什么推荐,咱们再来实打实对比一下两者的优劣势,这样大家才能结合自己的项目情况做选择。

先说说SFC的优势

第一个优势是“上手快、团队协作门槛低”,不管是刚学前端的新手,还是从jQuery时代转过来的老前端,看到HTML+CSS+JS(TS)分离在同一个文件的三个块里,都会觉得很亲切:template写结构,script写逻辑,style写样式,分工明确,一目了然,而且现在大部分前端开发岗位的招聘需求里,都明确要求会写Vue SFC,找队友、找开源组件都很方便。

第二个优势是“内置优化多,性能下限高”,Vue3的编译器(包括Volar插件里的编译部分)会对template做很多静态优化:比如把静态的HTML节点提取出来,避免重复创建;比如给动态绑定的props、事件做标记,减少diff算法的工作量;比如把v-for和v-if放在一起的时候自动调整顺序(虽然这个调整有时候会有坑,但大部分情况下是对的),这些优化SFC用户是不需要自己动手的,编译器会自动帮你搞定,所以哪怕你是个刚入门的新手,写出来的SFC性能也不会太差;但TSX用户如果不懂这些优化,写出来的代码性能可能会比SFC差一些。

第三个优势是“生态支持更完善”,比如Vue的官方调试工具DevTools,对SFC的支持比TSX好得多——DevTools里可以直接看到SFC的template结构、data/computed属性、事件监听,甚至可以直接在DevTools里修改data的值看效果;但TSX的组件在DevTools里有时候只会显示一个函数名,结构、属性这些信息需要自己去翻代码,还有很多UI组件库,比如Element Plus、Ant Design Vue,都是专门为SFC设计的,虽然也支持TSX,但文档里大部分例子都是用SFC写的,TSX的例子要么很少,要么藏得很深,新手找起来会很麻烦。

再说说SFC的劣势

第一个劣势是“模板的类型检查有天花板”,刚才已经提过一点了,比如模板里调用一个接受联合类型参数的函数,Volar插件有时候会把参数类型推导成any;比如嵌套三层以上的第三方组件props,如果组件库的类型定义写得不够好,Volar插件可能会直接失去类型提示;比如自定义指令的参数、修饰符,Volar插件的类型检查也很弱,甚至有时候完全不检查,这些问题在TSX里都不存在,因为TSX就是TypeScript的一部分,所有的类型检查都是原生的,非常严格。

第二个劣势是“复杂逻辑的复用和抽象比较麻烦”,比如你要写一个“通用的弹窗组件”,弹窗的标题、内容、底部按钮都可以自定义,而且标题、内容、底部按钮的逻辑还可以复用其他组件,用SFC写的话,你可能会用很多插槽,包括具名插槽、作用域插槽、动态插槽,嵌套多了之后模板会变得非常乱,逻辑也很难理清;用TSX写的话,你可以直接用函数式组件或者渲染函数,把复杂的逻辑拆成一个个小的函数,复用和抽象起来都很方便,代码结构也会更清晰。

第三个劣势是“对动态渲染的支持不够灵活”,比如你要写一个“根据后端返回的JSON数据动态生成表单”的组件,后端返回的数据里可能会有各种各样的表单字段:文本框、下拉框、日期选择器、单选框、复选框,甚至还有嵌套的分组,用SFC写的话,你可能会用v-if/v-else-if/v-else或者component is,嵌套多了之后模板会变得很长,而且很难维护;用TSX写的话,你可以直接用switch/case或者对象映射,把每个表单字段的渲染逻辑写在对应的函数里,动态渲染起来非常灵活,代码也会更简洁。

接下来是TSX的优势

第一个优势是“TypeScript类型检查100%原生,无天花板”,这个是TSX最大的优势,没有之一,不管是组件的props、emits、slots,还是函数的参数、返回值,甚至是自定义指令的参数、修饰符,TypeScript都会给你最严格的类型检查,只要有一点类型不匹配,就会直接报错,不会等到运行时才发现问题,这对于大型项目、多人协作项目来说非常重要,可以大大减少线上bug的数量,提高开发效率。

第二个优势是“复杂逻辑的复用和抽象非常方便”,刚才也举过通用弹窗的例子,用TSX写的话,你可以把弹窗的结构拆成Header、Content、Footer三个小的函数式组件,每个组件只负责自己的逻辑,然后在主组件里组合起来;你也可以用Composition API的use函数,把通用的弹窗逻辑(比如打开、关闭、确认、取消)封装成一个useModal钩子,然后在任何需要弹窗的地方调用这个钩子,复用性非常强,而且TSX里的函数式组件可以直接返回JSX元素,不需要像SFC那样必须返回一个组件实例,写起来非常灵活。

第三个优势是“对动态渲染的支持极其灵活”,比如刚才举的动态表单的例子,用TSX写的话,你可以先定义一个对象映射,把后端返回的字段类型(比如text、select、date)映射到对应的渲染函数(比如renderTextInput、renderSelectInput、renderDateInput),然后遍历后端返回的数据,根据字段类型调用对应的渲染函数,生成对应的表单元素,如果后端返回的字段类型有新增,你只需要在对象映射里加一个新的键值对就可以了,不需要修改模板的其他部分,维护起来非常方便,而且TSX里可以直接用JavaScript/TypeScript的各种语法,比如数组方法、条件运算符、逻辑运算符,动态渲染起来比SFC的模板语法要灵活得多。

第四个优势是“更容易和React的生态对接”,比如你之前用React写过很多组件、钩子、工具函数,现在要转成Vue3,用TSX的话可以直接把这些东西搬过来,只需要做少量的修改(比如把React.createElement换成Vue的createVNode或者JSX语法糖,把useState换成ref,把useEffect换成watch/watchEffect),不用重新写一套;比如你想用React的某个高级UI组件库,用TSX的话可以直接通过Vue的@vitejs/plugin-vue-jsx或者@vue/runtime-dom的createApp挂载,虽然性能可能会比原生Vue组件库差一些,但至少可以用。

TSX的劣势

第一个劣势是“上手慢、团队协作门槛高”,TSX需要你同时掌握Vue3的Composition API、TypeScript的语法、还有JSX/TSX的语法,这对于刚学前端的新手来说,压力会比较大;而且现在大部分前端开发岗位的招聘需求里,虽然也会提TSX,但还是以SFC为主,找队友、找开源组件的时候可能会遇到一些困难。

第二个劣势是“需要自己做静态优化,性能上限高但下限低”,刚才也说过,Vue3的编译器会对SFC做很多静态优化,但TSX是纯TypeScript/JavaScript代码,编译器不会自动做这些优化,需要你自己动手,比如你要把静态的HTML节点提取出来,可以用Vue的h函数的缓存功能,或者用memo函数(不过Vue3的memo函数是对组件进行缓存的,不是对VNode进行缓存的,这点和React不一样);比如你要给动态绑定的props、事件做标记,可以用Vue的createVNode的flag参数,或者用JSX的修饰符,如果不懂这些优化,写出来的TSX代码性能可能会比SFC差一些;但如果懂这些优化,写出来的TSX代码性能可以比SFC更好,因为你可以根据具体的业务场景做更精细的优化。

第三个劣势是“生态支持不如SFC完善”,比如刚才说的Vue DevTools,对TSX的支持比SFC差得多;比如很多UI组件库,虽然也支持TSX,但文档里的例子大部分都是用SFC写的,TSX的例子要么很少,要么藏得很深;比如Vue的一些第三方插件,比如vue-router、vuex/pinia,虽然也支持TSX,但用起来可能会比SFC稍微麻烦一点。

实际开发中到底该怎么选?

聊完了优劣势,咱们再来看看实际开发中到底该怎么选,其实没有绝对的“谁好谁坏”,只有“谁更适合当前的场景”,我给大家总结了几个选型的原则:

看团队的技术栈和人员水平

如果团队里大部分人都是刚学Vue3的新手,或者技术栈以Vue2 SFC为主,那毫无疑问应该选SFC,因为上手快、协作门槛低,可以让大家快速进入开发状态;如果团队里大部分人都是有多年经验的前端老炮,或者技术栈以React+TypeScript为主,那可以考虑选TSX,因为可以充分发挥TypeScript的类型检查优势,还可以复用之前React的经验和生态。

看项目的规模和复杂度

如果项目是一个小型的、个人的、或者复杂度不高的项目(比如一个简单的博客系统、一个简单的管理后台),那选SFC就够了,因为SFC的内置优化可以让你快速写出性能不错的代码,而且开发效率也很高;如果项目是一个大型的、多人协作的、或者复杂度很高的项目(比如一个电商平台的前端、一个企业级的管理系统),那可以考虑选TSX,因为TSX的原生类型检查可以大大减少线上bug的数量,复杂逻辑的复用和抽象也会更方便,维护起来会更轻松。

看项目的具体场景

如果项目里大部分组件都是“静态结构多、动态逻辑少”的展示型组件(比如页面的头部、底部、导航栏、卡片组件),那选SFC就够了,因为SFC的template语法写展示型组件非常直观,开发效率也很高;如果项目里有很多“动态结构多、复杂逻辑多”的功能型组件(比如动态表单、动态表格、通用弹窗、富文本编辑器),那可以考虑选TSX,因为TSX对动态渲染和复杂逻辑的支持更灵活,写起来会更顺手。

可以混合使用,不用非此即彼

很多人可能会觉得,选了SFC就不能用TSX,选了TSX就不能用SFC,其实根本不是——Vue3完全支持混合使用SFC和TSX,你可以在同一个项目里,大部分组件用SFC写,少数复杂的功能型组件用TSX写,这样可以兼顾两者的优势,比如你可以把页面的头部、底部、导航栏用SFC写,把动态表单、动态表格、通用弹窗用TSX写,这样开发效率高,维护起来也轻松。

写在最后

Vue3的TSX不是为了取代SFC,而是为了给用户提供一个更灵活的选择,SFC适合新手、小型项目、展示型组件,TSX适合老手、大型项目、功能型组件,两者可以混合使用,不用非此即彼。

如果你现在还在纠结要不要学TSX,我的建议是:先把Vue3的Composition API和SFC学扎实了,然后再去学TSX,这样学起来会更快,而且即使你现在不用TSX,学了TSX也对你理解Vue3的核心渲染API有帮助,因为template编译出来的代码本质上就是用TSX风格写的。

希望这篇文章能帮到正在纠结的你,如果有什么疑问或者不同的看法,欢迎在评论区留言讨论。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门