# [译] Vue & Tsx — 最好的组合!?

4,860 阅读8分钟

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文档查到以下的内容: The entirety of Vue 2 docs on JSX

确定的是文档没有更多的了,但是我们需要的会可能只有这些?接下来的链接把我们指引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的经验,您可以确定还会有更多精彩的新内容。 要了解最新信息,请在TwitterFacebook或我的时事通讯上关注我。 另外,如果您有兴趣创建自己的技术博客(并检查您可以使用Vue 3做些什么),请免费试用CodeWrite

感谢您的阅读,并祝您编程愉快!