搭建vue-tsx風(fēng)格的開發(fā)模板
項(xiàng)目創(chuàng)建
使用vue-cli3+創(chuàng)建一個(gè)基于ts的模板:
vue create vue-tsx-template
# 或者使用vue ui進(jìn)入GUI模式創(chuàng)建
vue ui
創(chuàng)建的時(shí)候記得勾選typescript,css預(yù)處理器看各自喜好選擇,選擇內(nèi)容如下:

等待npm/yarn安裝結(jié)束后就是一個(gè)基于ts的vue模板了。
vue-tsx-support
上一步中已經(jīng)創(chuàng)建完了基于ts的vue模板,但是開發(fā)方式還是如同之前的template一樣,只是將script中的js部分改成了ts來書寫。接下來就將模板(template)方式改成tsx的方式,這里需要借助一個(gè)庫 -- vue-tsx-support
首先安裝vue-tsx-support:
npm install vue-tsx-support --save
# or
yarn add vue-tsx-support
安裝結(jié)束后,我們需要對我們的文件做點(diǎn)小改動,首先我們在主入口文件main.ts中引入:
// main.ts
import "vue-tsx-support/enable-check";
// 省略。。
然后刪掉src/shims-tsx.d.ts文件,避免和vue-tsx-support/enable-check聲明重復(fù)沖突。
最后在我們的vue.config.js文件里的configureWebpack屬性下增加一項(xiàng)resolve:
// vue.config.js
module.exports = {
// ...
configureWebpack: {
resolve: {
extensions: [".js", ".vue", ".json", ".ts", ".tsx"] // 加入ts 和 tsx
}
}
}
這樣就可以了,接下來就可以開始開發(fā)了。
我們在/components下新建一個(gè)button文件夾,并創(chuàng)建一個(gè)文件button.tsx。然后開始書寫我們tsx風(fēng)格的vue代碼:
// components/button/button.tsx
import { Component, Prop } from "vue-property-decorator";
import * as tsc from "vue-tsx-support";
interface ButtonClick {
(value: string): void
}
interface ButtonProps {
text: string;
btnClick?: ButtonClick
}
@Component
export default class ZButton extends tsc.Component<ButtonProps> {
@Prop() text!: string;
public btnClick(value: string): void {
console.log("value is: ", value);
}
protected render() {
return (
<div>
<button onClick={() => this.btnClick("click")}>{this.text}</button>
</div>
)
}
}
這樣我們就完成了一個(gè)簡單的tsx組件了。
接下來我們需要去views/Home.tsx中使用這個(gè)組件,刪掉原來的Home.vue,并創(chuàng)建一個(gè)Home.tsx:
// views/Home.tsx
import { Component, Vue } from "vue-property-decorator";
import { Component as tsc } from "vue-tsx-support";
import ZButton from "@/components/button/button.tsx";
@Component
export default class HomeContainer extends tsc<Vue> {
protected render() {
return <Zbutton text="點(diǎn)我!"></Zbutton>;
}
}
最后將App.vue改成App.tsx:
// App.tsx
import { Component, Vue } from "vue-property-decorator";
@Component
export default class App extends Vue {
protected render() {
return (
<div id="app">
<router-view></router-view>
</div>
);
}
}
然后運(yùn)行,能看到以下效果:

就這樣完成了一個(gè)簡單的tsx風(fēng)格的vue項(xiàng)目了。
mixins
新建mixins/index.ts,在index.ts中寫一個(gè)vue mixin:
// mixins/index.ts
import { Vue, Component } from "vue-property-decorator";
// 這里一定要做個(gè)聲明 不然在組件里使用的時(shí)候會報(bào)不存在的錯(cuò)誤
// 要對應(yīng)mixin中的屬性和方法
declare module "vue/types/vue" {
interface Vue {
mixinText: string;
showMixinText(): void;
}
}
@Component
export default class MixinTest extends Vue {
public mixinText: string = "我是一個(gè)mixin";
public showMixinText() {
console.log(this.mixinText);
}
}
然后在component/button/button.tsx中使用:
// component/button/button.tsx
import { Component, Prop } from "vue-property-decorator";
import * as tsc from "vue-tsx-support";
import MixinTest from "@/mixins";
interface ButtonClick {
(value: string): void;
}
interface ButtonProps {
text: string;
btnClick?: ButtonClick;
}
// 在Component裝飾器上注入mixin
@Component({
mixins: [MixinTest]
})
export default class ZButton extends tsc.Component<ButtonProps> {
@Prop() text!: string;
public btnClick(value: string): void {
console.log("value is: ", value);
}
// 點(diǎn)擊事件中調(diào)用mixin的方法
protected render() {
return (
<div>
<button onClick={() => this.showMixinText()}>{this.text}</button>
</div>
);
}
}
vuex
vuex的ts改造主要有兩種方案,一種是基于vuex-class的方式,一種是基于vue-module-decorators的方式。
因?yàn)榫幋a習(xí)慣的原因,喜歡在書寫vuex的時(shí)候,一個(gè)module store的各個(gè)小模塊都單獨(dú)寫成一個(gè)文件,而vue-module-decorators則是一個(gè)module store對應(yīng)一個(gè)文件。所以在選擇上,我選擇了vuex-class,有需要的朋友也可以了解下vuex-module-decorators。
安裝vuex-class:
npm install vue-class --save
#or
yarn add vuex-class
新建一個(gè)system的module,針對system的store建立各自文件
state.tsgetter.tsmutation.tsmutation-type.tsactions.ts
編寫一個(gè)簡單的例子,在vuex中存儲user信息。
先來編寫state中的內(nèi)容:
// store/modules/system/state.ts
interface SystemState {
user: Object
}
const state: SystemState = {
user: {}
}
export default state;
mutation-type.ts:
// store/modules/system/mutation-type.ts
interface SystemMutationType {
SET_USER_INFO: String;
}
const Mutation_Type: SystemMutationType = {
SET_USER_INFO: "SET_USER_INFO"
}
export default Mutation_Type;
mutation.ts:
// store/modules/system/mutation.ts
import type from "./mutation-type";
const mutation: any = {
[type.SET_USER_INFO as string](state: SystemState, user: Object) {
state.user = user;
}
}
export default mutation;
action.ts:
import type from "./mutation-type";
import { Commit } from "vuex";
export const cacheUser = (context: { commit: Commit }, user: Object) => {
context.commit(type.SET_USER_INFO as string, user);
}
然后建立一個(gè)index.ts將這些外拋出去:
// store/modules/system/index.ts
import state from "./state";
import mutations from "./mutation";
import * as actions from "./action";
import * as getters from "./getter";
export default {
namespaced: true,
state,
getters,
mutations,
actions
};
最后在store的入口文件處引用該module:
// store/index.ts
import Vue from "vue";
import Vuex from "vuex";
import system from "./modules/system";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
system
}
});
接著我們?nèi)ソM件button.tsx中使用:
// components/button/button.tsx
import { Component, Prop } from "vue-property-decorator";
import * as tsc from "vue-tsx-support";
// 引入store命名空間 方便使用某個(gè)模塊
import { namespace } from "vuex-class";
// 通過namespace(module name)的方式使用某個(gè)模塊的store
const systemStore = namespace("system");
@Component
export default class ZButton extends tsc.Component<ButtonProps> {
@Prop() text!: string;
// store使用state和action 其他getter和mutation類型
@systemStore.State("user") user!: Object;
@systemStore.Action("cacheUser") cacheUser: any;
public btnClick(value: string): void {
console.log("value is: ", value);
// 點(diǎn)擊調(diào)用store的action方式存儲user信息
// 而state中的user信息會同步 可通過vue-tools查看
this.cacheUser({ name: "張三", phone: "13333333333" });
}
// 點(diǎn)擊事件中調(diào)用mixin的方法
protected render() {
return (
<div>
<button onClick={() => this.btnClick()}>{this.text}</button>
</div>
);
}
}
三方組件庫
目前主流的三方組件庫都是支持ts的,且官方文檔上都會提供ts下的demo以及配置。這里以有贊的vant作為例子。
安裝:
npm install vant --save
#or
yarn add vant
在ts下如果想要按需加載vant的話,就不能使用babel-plugin-import了,而是要使用ts-import-plugin。
安裝ts-import-plugin:
npm install ts-import-plugin --save-dev
#or
yarn add ts-import-plugin -D
安裝結(jié)束后,需要在vue.config.js中注入到webpack中使用:
// vue.config.js
const merge = require("webpack-merge");
const tsImportPluginFactory = require("ts-import-plugin");
// 將ts-import-plugin合并到webpack配置中
const webpackMergeConfig = config => {
config.module
.rule("ts")
.use("ts-loader")
.tap(options => {
options = merge(options, {
transpileOnly: true,
getCustomTransformers: () => ({
before: [
tsImportPluginFactory({
libraryName: "vant",
libraryDirectory: "es",
style: true
})
]
}),
compilerOptions: {
module: "es2015"
}
});
return options;
});
}
module.exports = {
chainWebpack: config => {
webpackMergeConfig(config);
// ...省略
}
}
然后就可以在組件文件中使用vant三方組件庫了:
// components/index.ts
import Vue from "vue";
import { Button } from "vant";
Vue.use(Button);
button.tsx:
// components/button/button.tsx
import { Component, Prop } from "vue-property-decorator";
import * as tsc from "vue-tsx-support";
@Component
export default class ZButton extends tsc.Component<ButtonProps> {
@Prop() text!: string;
public btnClick(value: string): void {
console.log("value is: ", value);
}
// 點(diǎn)擊事件中調(diào)用mixin的方法
protected render() {
return (
<div>
// 使用van-button
<van-button type="primary">我是vant button</van-button>
</div>
);
}
}
這里有個(gè)問題需要注意,在ts下打包的應(yīng)用會丟失三方組件庫的樣式,而在開發(fā)環(huán)境是沒有問題的。而導(dǎo)致該問題的發(fā)生是
ts-loader不支持多線程打包。
解決方式為在vue.config.js中配置parallel: false即可。
歡迎star vue-tsx-template