-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
使用Vue 3.0做JSX(TSX)风格的组件开发 #11
Comments
hi,大佬。请问哪里可以看到 demo 源码呢? |
这两天我整理一下放上来 |
希望可以放个demo,非常感谢 |
q求 demo |
先来抛转引玉了,demo 和 babel-plugin 写的都比较简陋。vue3-tsx |
@zouhangwithsweet 一定要 .vue 文件吗, 不能 .tsx 文件吗? |
@xialvjun 可以看看我写的base-vue-tsx-template,基于vue-cli3做的tsx |
preact-cli之前的版本用了一个babel插件实现了静态树提升 babel-plugin-transform-react-constant-elements, 不过后续版本没有集成这个plugin不知道为什么 |
@deepkolos 应该是转换后某些边界情况会出错。可以看这个讨论 facebook/react#3226 |
我现在用vue3.0开发,最麻烦的就是tsx会报 jsx element type does not have any constructor or call signatures |
@hujiulong 求解,为什么我这里拿不到 |
@lanshanmao 看文章最后那段话 |
占个位置 |
貌似tsx组件内defineComponent里面不定义props是有问题的吧,这样的话setup函数内拿不到props的(ps:我的vue版本是3.0.4) |
@841440416 看文章最后那段文字。这个功能已经被移除了 |
看到了,谢谢 |
tsx 会让热更新失效 遇到过吗 怎么解决 |
JSX 的语法需要单独的 loader 去支持,https://github.com/vueComponent/vue-jsx-hot-loader |
现在的这种写法,声明了ts类型还要声明 props 类型,写起来就像是脱裤子放屁一样难受。 |
使用 |
这个文章非常棒 |
您好,您的邮件我已收到。
|
前言
我日常工作都是使用React来做开发,但是我对React一直不是很满意,特别是在推出React Hooks以后。
不可否认React Hooks极大地方便了开发者,但是它又有非常多反直觉的地方,让我难以接受。所以在很长一段时间,我都在尝试寻找React的替代品,我尝试过不少别的前端框架,但都有各种各样的问题或限制。
在看到了Vue 3.0 Composition-API的设计,确实有眼前一亮的感觉,它既保留了React Hooks的优点,又没有反复声明销毁的问题,而Vue一直都是支持JSX语法的,3.0对TypeScript的支持又非常好,所以我开始尝试用Vue + TSX来做开发。
简单示例
先来看看用Vue3.0 + TSX写一个组件是什么什么样子的。
实现一个Input组件:
可以看到写法和React非常相似,和React不同的是,一些内部方法,例如
handleChange
,不会在每次渲染时重复定义,而是在setup
这个准备阶段完成,最后返回一个“函数组件”。这算是解决了React Hooks非常大的一个痛点,比React Hooks那种重复声明的方式要舒服多了。
Vue 3.0对TS做了一些增强,不需要像以前那样必须声明
props
,而是可以通过TS类型声明来完成。这里的
defineComponent
没有太多实际用途,主要是为了实现让ts类型提示变得友好一点。Babel插件
为了能让上面那段代码跑起来,还需要有一个Babel插件来转换上文中的JSX,Vue 3.0相比2.x有一些变化,不能再使用原来的vue-jsx插件。
我们都知道JSX(TSX)实际上是语法糖,例如在React中,这样一段代码:
实际上会被babel插件转换为下面这行代码:
Vue 3.0也提供了一个对应
React.createElement
的方法h
。但是这个h
方法又和vue 2.0以及React都有一些不同。例如这样一段代码:
在vue2.0中会转换成这样:
可以看到vue会将传入的属性做一个分类,会分为
class
、style
、attrs
、on
等不同部分。这样做非常繁琐,也不好处理。在vue 3.0中跟react更加相似,会转成这样:
基本上是传入什么就是什么,没有做额外的处理。
当然和
React.createElement
相比也有一些区别:children
这个名字在props
中传入,而是通过slots
去取,这个下文会做说明。所以只能自己动手来实现这个插件,我是在babel-plugin-transform-react-jsx的基础上修改的,并且自动注入了
h
方法。实际使用
在上面的工作完成以后,我们可以真正开始做开发了。
渲染子节点
上文说到,子节点不会像React那样作为
children
这个prop
传递,而是要通过slots
去取:例如实现一个Button组件
然后我们就可以使用它了:
渲染结果:
Reactive
配合vue 3.0提供的
reactive
,不需要主动通知Vue更新视图,直接更新数据即可。例如一个点击计数的组件Counter:
渲染结果:
这个Counter组件如果用React Hooks来写:
对比之下可以发现Vue 3.0的优势:
在React中,
useState
和定义handleClick
的代码会在每次渲染时都执行,而Vue定义的组件重新渲染时只会执行setup
中最后返回的渲染方法,不会重复执行上面的那部分代码。而且在Vue中,只需要更新对应的值即可触发视图更新,不需要像React那样调用
setCount
。当然Vue的这种定义组件的方式也带来了一些限制,
setup
的参数props
是一个reactive
对象,不要对它进行解构赋值,使用时要格外注意这一点:例如实现一个简单的展示内容的组件:
这样写是有问题的,我们在
setup
的参数中直接对props
做了解构赋值,写成了{ content }
,这样在后续外部更新传入的content
时,组件是不会更新的,因为破坏了props
的响应机制。以后可以通过eslint之类的工具来避免这种写法。正确的写法是在返回的方法里再对
props
做解构赋值:生命周期方法
在Vue 3.0中使用生命周期方法也非常简单,直接将对应的方法import进来即可使用。
vue 3.0对tree-shaking非常友好,所有API和内置组件都支持tree-shaking。
如果你所有地方都没有用到
onMounted
,支持tree-shaking的打包工具会自动将起去掉,不会打进最后的包里。指令和过渡效果
Vue 3.0还提供了一系列组件和方法,来使JSX也能使用模板语法的指令和过渡效果。
使用
Transition
在显示/隐藏内容块时做过渡动画:渲染结果:
也可以通过
withDirectives
来使用各种指令,例如实现模板语法v-show
的效果:这样写起来有点繁琐,应该可以通过babel-jsx插件来实现下面这种写法:
优缺点
在我看来Vue 3.0 + TSX完全可以作为React的替代,它既保留了React Hooks的优点,又避开了React Hooks的种种问题。
但是这种用法也有一个难以忽视的问题:它没办法获得Vue 3.0编译阶段的优化。
Vue 3.0通过对模板的分析,可以做一些前期优化,而JSX语法是难以做到的。
例如“静态树提升”优化:
如下一段模板(这是模板,并非JSX):
如果不做任何优化,那么编译后得到的代码应该是这样子:
那么每次重新渲染时,都会执行3次
h
方法,虽然未必会触发真正的DOM更新,但这也是一部分开销。通过观察,我们知道
h('span', 'static')
这段代码传入的参数始终都不会有变化,它是静态的,而只有h('span', this.dynamic)
这段才会根据dynamic
的值变化。在Vue 3.0中,编译器会自动分析出这种区别,对于静态的节点,会自动提升到
render
方法外部,避免重复执行。Vue 3.0编译后的代码:
这样每次渲染时就只会执行两次
h
。换言之,经过静态树提升后,Vue 3.0渲染成本将只会和动态节点的规模相关,静态节点将会被复用。除了静态树提升,还有很多别的编译阶段的优化,这些都是JSX语法难以做到的,因为JSX语法本质上还是在写JS,它没有任何限制,强行提升它会破坏JS执行的上下文,所以很难做出这种优化(也许配合prepack可以做到)。
考虑到这一点,如果你是在实现一个对性能要求较高的基础组件库,那模板语法仍然是首选。
另外JSX也没办法做
ref
自动展开,使得ref
和reactive
在使用上没有太大区别。后话
我个人对Vue 3.0是非常满意的,无论是对TS的支持,还是新的Composition API,如果不限制框架的话,那Vue以后肯定是我的首选。
更新:
本文中通过TS的interface声明props类型的依赖vue3的Optional props decalration,但后续版本中这个功能被废除了,原因可以查看#154, 在#1155中也有一些替代方案的讨论
The text was updated successfully, but these errors were encountered: