Vue with TSX - the perfect duo!?
在所用可用的JavaScript框架中,我最喜欢的是Vue和React。React有庞大的生态、完美的TypeScript支持、JSX,Vue则是容易上手、性能,和在小细节上提高开发体验,比如说自动传递的Props,易用的转换系统,directives,slots等等。
那么,怎么能同时拥有它们的优点呢?在React中使用Vue的优点,意味着将React变成Vue,所以,别无选择。在Vue中怎么使用TypeScript和JSX。
Well,虽然Vue非常、重度依赖其自定义模板语法和SFC(单个文件组件),但是,它很灵活的提供了多种方式去实现同样的事情(However, it’s also very “unopinionated” and provides its users with many ways of doing the same thing. )。包括使用JSX,即使是TSX(JSX和TypeScript)!令人沮丧的是,当你什么都想做好的时候,通常不能将单个做到极致(一个也做不好)。让我来演示Vue2怎么做到支持TypeScript的。
Vue2 TSX 支持
让我们从JSX开始,可以快速的从Vue2文档查到以下的内容:
确定的是文档没有更多的了,但是我们需要的会可能只有这些?接下来的链接把我们指引Github READNE,详细的介绍了需要的Babel插件和JSX怎么使用Vue功能。
首先引入依赖(这里使用yarn):
yarn add --dev @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
配置Babel
module.exports = {
presets: ["@vue/babel-preset-jsx"],
};
现在,如果你比较幸运,应该就可以直接进入代码和编写JSX。否则,则是几个小错误。大多数都可以通过浏览Babel插件的Github issues或从其他地方搜索答案去修复。但是,相对于Vue的生态,相关的内容资源非常有限。
当你终于可以写Vue JSX代码,从这里开始还挺不错。JSX和SFC兼容,你甚至都不用使用component字段(属性)。
import HelloWorld from "./components/HelloWorld.vue";
export default {
render() {
return (
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<HelloWorld msg="Hello Vue in CodeSandbox!" />
</div>
);
},
};
TypeScript
JSX可用之后,接下来就是怎么使用TypeScript将J换成T做开发。使用(TS)的方法之前,我们需要先去设置TypeScript(的配置)。
现在,Vue2的文档有一,一——整页描述TypeScript的支持,所以我不会在这里吐槽。怎么配置、注意事项和多种你能在Vue使用Ts的方法文档描述非常详细。(“unopinionation” - remember?)
Ts结合Vue有两种主要的方式 - “基于类”是其中的一种。
对于“基于类”的方式,我必须承认我不太喜欢。可能是我更喜欢“函数式”编程,或者是因为不喜欢ES装饰器(ES decorators)。对我来说,它们还是太实验性了(ES RPC),在TS中也无法提供像其他能力一样明确的“自动补全”能力。总之,知识一个专门给使用TS“基于类”开发Vue组件的网站,如果你有兴趣可以随时查看。
至于另外一种基础的方法,只需要简单将组件包裹在Vue.extend()即可。不过你仍然需要分别为属性(props)、render()函数和所有的计算属性(computed)添加PropType<T>, VNode或者你自定义的注解。
import Vue, { PropType, VNode } from "vue";
import HelloWorld from "./components/HelloWorld.vue";
export default Vue.extend({
props: {
message: [String, Function] as PropType<string | (() => string)>,
},
render(): VNode {
return (
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<HelloWorld
msg={typeof this.message === "string" ? this.message : this.message()}
/>
</div>
);
},
});
上面这段代码使用TS对更前面代码改编。离开JSX,我需要处理几个问题。但在此之前,我想提供一个TS不会因为*.vue文件导入而提示异常的“垫片”文件。
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
你可以选择将这段代码的*.d.ts 文件放在根目录、任何在TS项目的目录或者直接在tsconfig.json types 属性值指定。
但是,如果你以前使用过TS的声明文件,可能会注意到上面的“垫片”文件是有缺陷的。TS会接收*.vueSFC(单文件组件),但是除了Vue属性以外,没有任何是type-safe安全类型。props、computed、methods、etc都不是!这里没法解决,你可能依旧可以从编辑器/IDE中获得一些代码补全,但是也只是边缘的修补 —— 没有 “纯TypeScript”。
TSX
看起来,结合了JSX和TS的配置,我们应该就准备好了TSX。可悲的是,没这么简单。
当然,在vue中你需要更多的垫片去适配JSX类型,如下所示:
import Vue, { VNode } from "vue";
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}
你还需要调整tsconfig.json来设置jsx为“保留”项,来确保遇到JSX代码不被TS相关内容处理,以至于能够被Babel处理(前提是配置没有任何的问题)。
好吧,让我们谈谈,现在你顺利享受的一流Vue TSX体验...
我刚刚向你介绍了通用的Vue2 TSX设置的原因,而不是怎么设置的方法。为了展示这个整个过程多么的存在不足。当然,在转换已有项目和样板的时候可能很有用,但大多数时候,你仅仅会去用更好的Vue Cli和全部设置的插件或者一个无配置像Parcel、Poi的工具。然而,这不能避免的存在设置问题和缺乏完整深入的文档。除此之外,你从React学习的TSX经验可能并不适用。
编辑器支持
说到体验,那是什么样的感觉?花了一些时间在这上面之后,我必须要承认 - 体验贼差。在编辑器方面,只有VS Code和Web Storm可选。我使用的是Vetur extension编辑器插件。然而,这扩展更多的是针对SFC的支持(在这一方面也缺乏)。还有一个VueDX extension插件在SFC上面支持比Vetur更好,但是它完全破坏了JSX/TSX支持,因此我不得不禁用它。
现在,两个编辑器都很接近,WebStorm略胜一筹。属性自动补全几乎不存在,运行在组件内更是完全没有。this的操作更是枣糕(this as any),然而WebStorm通常好一些。
Vue-tsx-support
现在,哪里有问题,哪里就有方案。对于Vue2 TSX的支持,有一个名叫 vue-tsx-support的库。这是一个只限于TS的库在类型,转换功能,公共工具等正确的类型,你的Vue组件都能非常好的结合TSX。在这里不会做太详细的介绍(GitHub README 非常的完善),我只想展示这个库的示例用法:
import HelloWorld from "./components/HelloWorld.vue";
import * as tsx from "vue-tsx-support";
interface HelloWorldProps {
msg?: string;
}
const TypedHelloWorld = tsx.ofType<HelloWorldProps>().convert(HelloWorld);
export { TypedHelloWorld as HelloWorld };
你可以看到很好的代码自动补全和怎么使用 ofType() 和 convert() 转换功能得到类型安全的Vue组件。
因此,要我说,vue-tsx-support库是在Vue2 使用TSX的最佳方式,当然,到这里有些长,但是这种设置是正确获得TSX体验的唯一方式。
Vue3 到来
现在,忘记目前为止阅读到的全部内容和观点,这已经无关紧要了。好吧,也许我在开玩笑,但由于Vue3 目前处于“文档Beta”版本,Vue-Tsx组合将在Vue变得更加的重要。
老实说,这就是标题的全部。在开发我的项目CodeWrite(开发者的博客工具项目)已经使用Vue3一段时间了。是的,我将它结合TSX一块使用,而且开发体验非常的棒!
Vue3现在就是使用TS编写的,从而最大限度的得的支持,并且借助新的Composition API,看起来this的问题得到了解决。当然,TS和JSX文档的清晰度没有什么改变,但是整个配置的处理流程没有这么麻烦了。
Vue3配置
简而言之,你必须再一次添加jsx: "preserve"在tsconfig.json并且在Babel配置做一些修改(这次是这个插件):
module.exports = {
plugins: ["@vue/babel-plugin-jsx"],
};
但是这次,就这样既可!现在你就可以使用TSX编写Vue组件,伴随着VSCode或WebStorm很棒的代码自动补全!
看一下示例(TSX + Composition API):
import Vue from "vue";
import HelloWorld from "./components/HelloWorld.vue";
export default Vue.extend({
props: {
message: [String, Function] as PropType<string | (() => string)>,
},
setup(props) {
return () => {
const message = props.message;
return (
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<HelloWorld msg={typeof message === "string" ? message : message()} />
</div>
);
};
},
});
为了使SFC正常运行,你仍然需要请求垫片(尽管这次不同),并且这部分的代码补全可能没有这么完美,但是需要在Vue3中设置这些。—— 我不知道您为什么还使用它们。
declare module "*.vue" {
import { ComponentOptions } from "vue";
const component: ComponentOptions;
export default component;
}
同样的,使用所有可用的工具,这些配置都不是什么问题。Vite是非常值得留意的新内容,直接来源于Vue的核心团队。它动态支持超快的ESM工作流,同时无需配置的支持TS,JSX和TSX。
是否应该使用?
现在,是否使用Vue3完全取决于你自己。我使用它,是因为大量改进的TS和JSX支持对我来说是非常有价值的。另外,我认为新的Composition API更优于以前的(Option API)并且比起React,我更信号Vue现在的这个响应式模型。
然而,由于Vue3是不向下兼容破坏性的升级,生态小得多,对我来说这不是什么问题,但是我知道这可能是很多人需要考虑的问题。所以,你自己选。
敬请关注!
因此,以我对Vue 3的经验,您可以确定还会有更多精彩的新内容。 要了解最新信息,请在Twitter,Facebook或我的时事通讯上关注我。 另外,如果您有兴趣创建自己的技术博客(并检查您可以使用Vue 3做些什么),请免费试用CodeWrite!
感谢您的阅读,并祝您编程愉快!