總結
- 這個庫幫助我們在 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-props
import "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.component
是 tsx.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 */
})
————————————————————————————————————————————————————————————————
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 不出現這個報錯(不建議)
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.component
是tsx.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)} />;