最近尝试为 vue 项目配合 TypeScript 不过发现毕竟 vue 不是原生 TypeScript 开发,在对 TypeScript 支持方面显然还问题多多,这里稍微记录两则
自动注入 h 问题
如果你在 vue 中是用 jsx 你会明显感受到和 react 有明显的一个差异就是 react 中你可以很方便的到处写下 jsx,而 vue 中则限定的比较死,要么写在组建的 render 函数中,要么主动传递 h 函数到你想要写 jsx 的地方比如
|
|
由于 h 函数是在编译后才会被使用到,而每次都要写 h,但实际少主动用到,所以 vue 官方为了进一步简化使用,开发了一个 babel 插件babel-sugar-inject-h
, 检测 jsx 自动插入 h 函数,比如这样
|
|
这让看起来让 vue/jsx 更加接近 react/jsx ,还稍微减轻了开发的工作。但我个人认为这样有些自做主张,并且关键是做的并不完美。如果你用了 vue-cli 那么他是自带且默认启动的,如果你没有用 vue-cli 自己配的环境,那么就有可能就忽略了这的插件,就容易导致同一段代码在两个环境下有不同的表现。而这如果不是用户主动去看文档,是不会被感知到的,一但出了问题就特别令人摸不着头脑,
哦,跑题了,这篇文章应该说的是跟 TypeScript 相关的问题,其实也就是它做得不够好的一个体现,假如你要配合 TypeScript 使用,很有可能遇到以下代码
|
|
其中 test 是你组件的内部变量,理论上这段代码应该等价于
|
|
然而假如你实际在 vue-cli 中跑这段代码,是可以跑起来的,但在浏览器中会报出类似以下错误,
|
|
有了前面的铺垫,不用说问题的关键显然是出在 babel-sugar-inject-h
自动注入 h 的问题上,为了了解为什么,不妨看一下这段代码编译后的样子
|
|
我撇去了无关的代码和不压缩,这里的问题就显而易见了。
尽管在 test 上我主动显式的传入了 h 函数,但是对于构造函数 data() 来说,似乎依然被判断为应当注入 h 的情况,这也其实没有关系,不过它注入 var h = _this.$createElement;
的位置就有些问题了 ,默认情况下,它一般事插入到 this 声明定义之后。
|
|
然而由于用了ts和vue-property-decorator,_this 的是先声明后定义,导致插入在声明之后的 h 是找不到的 _this.$createElement
的
解决方法
最好的方法当然是这个插件更新并考虑 ts 这种 edge case ,不过在此之前我其实建议直接关掉自动注入 h 的功能,毕竟 vue/jsx 本身就不是可以到处写 jsx ,本来就需要 h 却隐蔽起来并不会解决太多问题,反而会造成一些认知问题。
不过这里牵扯到另一个问题,如何关闭这个插件。
vue/jsx 的文档中写明的修改配置方法是在 babel config 中修改 @vue/babel-preset-jsx
的配置,假如你是自己配置环境那么直接如此就可以了。
|
|
但是,假如你用 vue-cli 构建的项目的话,你会发现这样写并没有用,这是因为 vue-cli 非常“聪明”的封装了另一个 @vue/babel-preset-app
,这 preset 内部引入了 @vue/babel-preset-jsx
自己包裹了一层配置的传递, 源码在这里
|
|
可以看到它根本不会去读取 babel config 中 @vue/babel-preset-jsx
的配置参数,因此正确的配置方法是
|
|
而关于 jsx 可配置 @vue/babel-preset-jsx
参数这一点,官方一开始并没有任何说明(现在有了),在文档中也只提到,jsx
是一个布尔值用于开启关闭 jsx 支持,如果不去看源码谁又知道,它还可以传入 object 来做进一步的配置?
所以说,为了搞清 babel-sugar-inject-h
这一个小问题,不得不跑去看 vue-cli/babel-preset-jsx/babel-preset-app 等一长串的相关源码配置方法才摸索清楚,这说不定就是封装之美?
真有你的啊 vue-cli
tsx 中组件属性类型推断问题
tsx 中无法正确给出自定义 component 的属性类型定义,甚至会报错。相比之下这个问题更加简单也更加恼人,假如你定义了一个组件,比如
|
|
这个问题如果是从官方角度来说,几乎除了等待 vue3 重构以后得到 TypeScript 更好的支持以外,看来别无其他折中的办法,详情可参见这个 issue 。
所幸这个 issue 中也给出了一个比较成熟的第三方解决方案 vue-tsx-support,这个库的文档已经非常详细了,这里没有必要赘述。总而言之这个库可以相当好的解决目前的 tsx component prop 缺少类型推导的问题,不过,代价是引入了他自己的一套定义组件的方式。
兼容?升级?
首先这个库虽然得到推荐,但官方对其的态度也停留在可以有的暧昧程度,不如 **vue-property-decorator**
那样得到官方认可并且整合到官方工具链中这个级别的对待。在这个问题上 vue 官方寄希望于 vue3 重构能解决,而没有给出现在的过度方案,显然你不可能寄希望于他们能去兼容现在的第三方过度方案……
因此另外一个不甚优雅的解决方案,那就是通过 d.ts 文件让 component 的参数允许任何属性
|
|
这样 component 实际上就可以接受任何参数而不报错,然而无法正确推导出对应的 component 有什么属性的问题并没有解决,所以依然的不到任何提示,这某种程度上就损失了 TypeScript 一个大优势。
而好处是,没有引入任何新的东西,也不需要魔改任何旧的代码,在兼容性上和其他的 vue2 代码处于一个级别。
总结
目前来说,ts + vue 当然可用,但是依然相当多的问题,没法如丝般顺滑的相互配合。对于他们完全打算通过 vue3 去彻底(?)解决于 ts 共用的的问题,而不打算优化一下 2.x 当前存在问题的决定……我是比较不赞同的