框架-Vue Class Component tsx 支持(vue-tsx-support V2.2.0)

总结

  • 这个库帮助我们在 tsx 中实现 props 等的提醒
  • 在 TS 中组件能够传递 props 建议都要定义好类型

class component

import * as tsx from "vue-tsx-support";
 
 
interface MyComponentProps {
    text: string;
    important?: boolean;
}
 
interface Events {
    onOk: void;
    onError: { code: number, detail: string }; // 参数只有一个,类型为接口 { code: number, detail: string }
}
 
interface ScopedSlots {
    default: { text: string }; // 参数只有一个,类型为接口 { text: string }
}
 
@component // 不是该库提供
class MyComponent extends tsx.Component<MyComponentProps, Events, ScopedSlots> {
    @Props({required:true}) text!:string; // 不是该库提供
    @Props() important!:boolean; // 不是该库提供
    /* snip */
}

modifiers 事件修饰符

  • esc, tab, enter, space, up, down, del, left, right 键盘事件
    • del 会被 DELETE 或 BACKSPACE 触发
    • m.enter.esc 的方式组合修饰符,并不能够成功
  • left, right, middle 鼠标事件
  • ctrl, shift, alt, meta 系统修饰符键
  • noctrl, noshift, noalt, nometa 仅当未按下该修饰符时才会被触发
  • self
  • prevent(不触发原生事件动作), stop
  • keys(...args) 多个按键之一触发
  • exact(...args) 系统修饰符精确匹配时触发
import { modifiers as m } from "vue-tsx-support";

<div onKeydown={m.enter(this.onEnter)} />;
<div onKeydown={m.enter.prevent(this.onEnter)} />;
<div onKeydown={m.esc.prevent} />;
<div onKeydown={m.keys("enter", "esc")(this.onEnterOrEsc)} />; // <div @keydown.enter.esc="onEnterOrEsc" /> 按下 enter 或 esc 时触发
<div onKeydown={m.keys(65).exact("ctrl", "alt")(this.onCtrlAltA)} />; // 65 代表 a 键
<div onClick={m.exact("ctrl", "shift")(handler)} />;

安装

  • 在 tsconfig.json 配置,启用 tsx 的类型提醒
npm install vue-tsx-support -S

{
  "compilerOptions": {
    "...snip...": "...snip..."
  },
  "include": [
    "node_modules/vue-tsx-support/enable-check.d.ts",
    "...snip..."
  ]
}
  • 要启用某个选项将它们导入即可,例如:allow-unknown-propsimport "vue-tsx-support/options/allow-unknown-props";
  • allow-element-unknown-attrs 在原生元素上允许非 attrs 属性
  • allow-unknown-props 在组件上允许非 props 属性
  • enable-html-attrs 在组件上允许非 props 但是是 attrs 的属性,并且遵循 attrs 的类型校验
  • enable-nativeon 在组件上允许使用 nativeOn 的方式绑定原生事件
  • enable-vue-router 添加 router-link 和 router-view 定义

不常用——————————————————————————————————

ofType 给现有组件添加类型提醒

import ThirdPartyComponent from "third-party-library";
import * as tsx from "vue-tsx-support";
 
interface MyComponentProps { /* ... */ }
const MyComponent = tsx.ofType<MyComponentProps>().convert(ThirdPartyComponent);
// const MyComponent = tsx.ofType<MyComponentProps, Events, ScopedSlots>().convert(ThirdPartyComponent);

Vue.component 或 Vue.extend 定义组件

props

  • 不支持 props 通过数组定义,例如:props: ["foo", "bar"]
  • 所有 props 都会被 ts 视为可选,即使设置了 required:true。有 3 种办法解决
    • 使用 as 的方式 text: { type: String, required: true as true }
    • 使用 vue-strict-prop 库,import p from "vue-strict-prop";text: p(String).required
    • 让必传 props 作为 tsx.componentFactory.create 的第二参数 tsx.componentFactory.create({...}, ["text"])

componentFactoryOf 使 event 和 scoped slots 具有 ts 提醒

interface Events {
    onOk: () => void; // 以 on 开头
    onError: { code: number, detail: string }; // 参数只有一个时的简易写法,onError 的参数的类型为接口 { code: number, detail: string }
}
interface ScopedSlots {
    default: { text: string }; // default 作用域插槽将接收到一个参数类型为接口 { text: string }
    optional?: string; // 不仅插槽的参数会有类型提醒,插槽自身也会有必填提醒
}

const MyComponent = tsx.componentFactoryOf<Events, ScopedSlots>().create({
    ...
})

componentFactory 构建 vue 的子类

  • tsx.componenttsx.componentFactory.create 的简写
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
    props: {
        text: { type: String, required: true },
        important: Boolean,
    },
    computed: {
        className(): string {
            return this.important ? "label-important" : "label-normal";
        }
    },
    methods: {
        onClick(event) { this.$emit("ok", event); }
    },
    render(): VNode {
        return <span class={this.className} onClick={this.onClick}>{this.text}</span>;
    }
});

extendFrom 继承

import * as tsx from "vue-tsx-support";
// 注释:const Base = Vue.extend({/* snip */})
// This is equivalent to `const MyComponent = Base.extend({ /* snip */ });`
const MyComponent = tsx.extendFrom(Base).create({
    /* snip */
});

mixin 混入

import * as tsx from "vue-tsx-support";
const StorageMixin = {
    ... // vue options
}
const MyComponent = tsx.componentFactory.mixin(StorageMixin).create({
    ... // vue options
})

const tsx.componentFactory.mixin(FirstMixin).mixin(SecondMixin).create({
    /* snip */ 
})

————————————————————————————————————————————————————————————————

https://www.npmjs.com/package/vue-tsx-support

  • V2.2.0

Install and enable

npm install vue-tsx-support -S
  • And refer vue-tsx-support/enable-check.d.ts from somewhere to enable compiler check. 类型说明文档在 vue-tsx-support/enable-check.d.ts 可以通过 import 引入或 tsconfig.json 配置
  • 注释:启用后在 @vue/cli 生成的项目中,会和 shims-tsx.d.ts 的 IntrinsicElements 出现重复定义
import "vue-tsx-support/enable-check"
{
  "compilerOptions": {
    "...snip...": "...snip..."
  },
  "include": [
    "node_modules/vue-tsx-support/enable-check.d.ts",
    "...snip..."
  ]
}

Using intrinsic elements 使用原生元素

  • 注释:不需要这个库也能够在 tsx 中为原生元素提供提醒

Using custom component 使用自定义组件

  • By default, vue-tsx-support does not allow unknown props. vue-tsx-support 默认时不允许在 tsx 中向元素传入未定义的 props
  • You must add types to the component by apis memtions below, or enable allow-unknown-props option.可以通过设置 allow-unknown-props 不出现这个报错(不建议)

available APIs to add type information 用于添加 TSX 类型的 API

componentFactory

  • componentFactory.create can infer types of props from component options same as Vue.extend. 能够自动推断类型,例如下例中的 props
  • shorthand props definition(like props: ["foo", "bar"]) is currently not supported. 不支持 props 通过数组定义,例如:props: ["foo", "bar"]
  • all props are regarded as optional even if required: true specified. 但是所有 props 都会被 ts 视为可选,即使设置了 required:true。有 7 种办法解决?
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
    props: {
        text: { type: String, required: true },
        important: Boolean,
    },
    computed: {
        className(): string {
            return this.important ? "label-important" : "label-normal";
        }
    },
    methods: {
        onClick(event) { this.$emit("ok", event); }
    },
    render(): VNode {
        return <span class={this.className} onClick={this.onClick}>{this.text}</span>;
    }
});
  • 1.Instead of required: true, specify required: true as true. 使用required: true as true代替required: true
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
    props: {
        text: { type: String, required: true as true },
        important: Boolean,
    },
    /* snip */
});
import * as tsx from "vue-tsx-support";
import p from "vue-strict-prop";
const MyComponent = tsx.componentFactory.create({
    props: {
        text: p(String).required,
        important: Boolean,
    },
    /* snip */
});
  • 3.Specify required prop names as second argument 让必传 props 作为 tsx.componentFactory.create 的第二参数
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
    props: {
        text: { type: String, required: true },
        important: Boolean,
    },
    /* snip */
}, ["text"]);

component

  • Shorthand of componentFactory.create tsx.componenttsx.componentFactory.create的简写

extendFrom

  • When you want to extend your component from other than Vue, you can use extendFrom 从 Vue 子类创建构造函数
import * as tsx from "vue-tsx-support";
// 注释:const Base = Vue.extend({/* snip */})
// This is equivalent to `const MyComponent = Base.extend({ /* snip */ });`
const MyComponent = tsx.extendFrom(Base).create({
    /* snip */
});

mixin

import * as tsx from "vue-tsx-support";
 
const StorageMixin = {
    methods: {
        getItem(string name): string {
            return localStorage.getItem(name);
        },
        setItem(string name, string value): void {
            localStorage.setItem(name, value);
        }
    }
}
 
const MyComponent = tsx.componentFactory.mixin(StorageMixin).create(
    {
        props: {
            name: String
        },
        data() {
            return { value: "" }
        },
        mounted() {
            this.value = this.getItem(this.name);
        },
        render(): VNode {
            return (
                <button onClick={() => this.setItem(this.name, this.value)}>
                    SAVE
                </button>
            );
        }
    }
);
 
// You can add 2 or more mixins by method chain
const tsx.componentFactory.mixin(FirstMixin).mixin(SecondMixin).create({
    /* snip */ 
})

componentFactoryOf

  • Return componentFactory with additional types (events and scoped slots) 使 event 和 scoped slots 具有 ts 提醒
import * as tsx from "vue-tsx-support";
 
interface Events {
    // all memebers must be prefixed by 'on' 必须以 on 开头,这是 jsx 的要求
    onOk: () => void;
    // If event handler has only one parameter, you can specify parameter type as a shorthand.
    // For example, this is equivalent to `onError: (arg: { code: number, detail: string }) => void`
    // 注释:如果该 event 只有一个参数,可以使用简易写法,这里的 onError 的参数类型为 { code: number, detail: string } 这样的一个接口
    onError: { code: number, detail: string };
}
 
const MyComponent = tsx.componentFactoryOf<Events>().create({
    render(): VNode {
        return (
            <div>
              <button onClick={() => this.$emit("ok")}>OK</button>
              <button onClick={() => this.$emit("error", { code: 9, detail: "unknown" })}>Raise Error</button>
            </div>
        );
    }
});

<MyComponent onOk={() => console.log("ok")} />;
<MyComponent onError={p => console.log("ng", p.code, p.detail)} />;
import * as tsx from "vue-tsx-support";
 
interface ScopedSlots {
    default: { text: string };
    optional?: string;
}
 
const MyComponent = tsx.componentFactoryOf<{}, ScopedSlots>().create({
    props: {
        text: String
    },
    render(): VNode {
        const { default, optional } = this.$scopedSlots;
        return <ul>
                 <li>{ default({ text: this.text || "default text" }) }</li>
                 <li>{ optional ? optional(this.text) : this.text }<li>
               </ul>;
    }
});
 
<MyComponent scopedSlots={{
        default: p => <span>p.text</span>
    }}
/>;
 
// NG: 'default' is missing in scopedSlots 不仅插槽的参数会有类型提醒,插槽自身也会有必填提醒
<MyComponent scopedSlots={{
        optional: p => <span>p</span>
    }}
/>;

Component

import component from "vue-class-component";
import * as tsx from "vue-tsx-support";
 
interface MyComponentProps {
    text: string;
    important?: boolean;
}
 
@component({
    // 这是旧的写法了,推荐使用 @Prop
    props: {
        text: { type: String, required: true },
        important: Boolean
    },
    /* snip */
})
class MyComponent extends tsx.Component<MyComponentProps> {
    /* snip */
}
  • If you want, you can specify event types and scoped slot types as 2nd and 3rd type parameter 参数分别是 props、events、scoped slot 的类型
import component from "vue-class-component";
import * as tsx from "vue-tsx-support";
 
interface MyComponentProps {
    text: string;
    important?: boolean;
}
 
interface Events {
    onOk: void;
    onError: { code: number, detail: string };
}
 
interface ScopedSlots {
    default: { text: string };
}
 
 
@component({
    props: {
        text: { type: String, required: true },
        important: Boolean
    },
    /* snip */
})
class MyComponent extends tsx.Component<MyComponentProps, Events, ScopedSlots> {
    /* snip */
}

ofType

  • If you can't modify existing component definition, wrap it by ofType and convert 给现有组件添加类型提醒
import ThirdPartyComponent from "third-party-library";
import * as tsx from "vue-tsx-support";
 
interface MyComponentProps { /* ... */ }
 
const MyComponent = tsx.ofType<MyComponentProps>().convert(ThirdPartyComponent);
// const MyComponent = tsx.ofType<MyComponentProps, Events, ScopedSlots>().convert(ThirdPartyComponent);

Other attributes 其他属性

Native event listeners and dom properties 原生 event 和属性

  • To avoid compilation error, you must use kebab-case attribute name. 在组件上绑定原生属性和方法不支持 JSX 的写法,会出现编译错误。例如nativeOnClick={} domPropInnerHTML={}会报错
  • 注释:以下两种方法都没有类型提醒
<Component nativeOn-click={ ... } />
<Component domProp-innerHTML={ ... } />
  • Or use JSX-spread style. 或者 JSX-spread 的方式
<Component { ...{ nativeOn: { click: ... } } } />
<Component { ...{ domProps: { innerHTML: ... } } } />

HTML attributes attached to the root element 附加到组件上的 HTML 属性

<SomeInputComponent { ...{ attrs: { min: 0, max: 100 } } } />

Options

  • To enable each options, import them somewhere 要启用某个选项将它们导入即可,例如:allow-unknown-props
import "vue-tsx-support/options/allow-unknown-props";

allow-element-unknown-attrs

  • Make enabled to specify unknown attributes to intrinsic elements 在原生元素上允许非 attrs 属性
<div foo="foo" />;

allow-unknown-props

  • Make enabled to specify unknown props to Vue component. 在组件上允许非 props 属性

enable-html-attrs

  • Make enabled to specify HTML attributes to Vue component. 在组件上允许非 props 但是是 attrs 的属性,并且遵循 attrs 的类型校验
<MyComponent foo="foo" min={ 0 } max={ 100 } />;

enable-nativeon

  • Make enabled to specify native event listeners to Vue component. 在组件上允许使用 nativeOn 的方式绑定原生事件
<MyComponent foo="foo" nativeOnClick={ e => ... } />; // and `e` is infered as MouseEvent e 将被推断为 MouseEvent 事件对象

enable-vue-router

  • Add definitions of router-link and router-view 添加 router-link 和 router-view 定义

Utility

modifiers

  • Event handler wrappers which work like some event modifiers available in template 事件处理包装函数,等同于在模板中为事件添加修饰符。例如 .enter 等
import { modifiers as m } from "vue-tsx-support";

<div onKeydown={m.enter(this.onEnter)} />;
<div onKeydown={m.enter.prevent(this.onEnter)} />;
<div onKeydown={m.esc.prevent} />;
<div onKeydown={m.keys("enter", "esc")(this.onEnterOrEsc)} />; // <div @keydown.enter.esc="onEnterOrEsc" /> 按下 enter 或 esc 时触发
<div onKeydown={m.keys(65).exact("ctrl", "alt")(this.onCtrlAltA)} />;

Available modifiers 可以用的修饰器

  • esc, tab, enter, space, up, down, del, left, right 键盘事件
    • del allows not only DELETE, but also BACKSPACE. del 会被 DELETE 或 BACKSPACE 触发
    • left and right have another behavior when specified to mouse event 在鼠标事件中使用 left 和 right 会在点击鼠标左键或右键时触发
    • combination of key modifiers (e.g. m.enter.esc) does not work. m.enter.esc 的方式组合修饰符,并不能够成功
  • left, right, middle 鼠标事件
  • ctrl, shift, alt, meta 系统修饰符键
  • noctrl, noshift, noalt, nometa
    • Execute event handler only when specified system modifier key is not pressed. 仅当未按下该修饰符时才会被触发
  • self
  • prevent(不触发原生事件动作), stop
  • keys(...args)
    • Known key name("esc", "tab", "enter", ...) or number can be specified. 支持数字,例如<div onKeydown={m.keys(65)(handler)} />; // 65 代表 a 键
  • exact(...args) 系统修饰符精确匹配时触发,例如:<div onClick={m.exact("ctrl", "shift")(handler)} />;
原文地址:https://www.cnblogs.com/qq3279338858/p/12762021.html