分享vue + vuex + typescript的一次專案程式碼重寫
俗話說一個專案,用程式碼重寫十次,每次肯定收益匪淺。後續還會持續重構ssr等。
在vue裡使用ts,一般分為兩種情況:
- 在vue-cli 3.0以下的老專案中。
- 在最新的vue-cli 3.0中。
vue-cli 3.0
在最新的vue-cli 3.0中使用typescript,可以說是非常方便了,因為添加了對ts的支援,用vue create 專案時,選第二項自定義配置新增對ts的支援就行了,另外別忘了把ts-lint語法檢測也加上。至於如何安裝vue-cli 3.0,去官方文件看下就知道了。如果你想把這種配置作為預設配置,文件也給出了相應的配置如下,這樣你直接點第一個選項就會自動配置了。
被儲存的 preset 將會存在使用者的 home 目錄下一個名為
.vuerc
的 JSON 檔案裡。如果你想要修改被儲存的 preset / 選項,可以編輯這個檔案。在專案建立的過程中,你也會被提示選擇喜歡的包管理器或使用淘寶 npm 映象源以更快地安裝依賴。這些選擇也將會存入
~/.vuerc
。
專案初始化完後,就可以開箱即用了。看它的package.json檔案,typescirpt 以及ts-loader幫你配置好了,還有vue-class-component,vue-property-decorator(點選進入github地址檢視文件)這兩個新增vue對ts支援的外掛也安裝好了,後者是前者的拓展版,一般選擇後面那個即可,tsconfig.json和.d.ts檔案也配置好了,差不多例子也會幫你寫好了。當然例子所涉及的是遠遠不夠的,可以去這兩個外掛的github倉庫看下文件,使用起來非常簡單,差不多是把以前vue那種黑盒子的寫法去掉了一層包裝加上了一層語法修飾器。詳細的配置就放到下面的vue-cli 3.0以下步驟中寫吧,也不用這麼累贅了,畢竟下面的需要手動去配置。
vue-cli 3.0以下
首先初始化一個vue-cli 2.0的專案,文件也給出了相應的方法:
Vue CLI 3 和舊版使用了相同的
vue
命令,所以 Vue CLI 2 (vue-cli
) 被覆蓋了。如果你仍然需要使用舊版本的vue init
功能,你可以全域性安裝一個橋接工具:npm install -g @vue/cli-init # `vue init` 的執行效果將會跟 `[email protected]` 相同 vue init webpack my-project
沒什麼特殊要求,一路回車就好了,初始完專案,開啟package.json檔案看下其依賴,是不是特別的多,在看下script下的dev命令,是基於webpack-dev-server,也就是還是使用webpack加vue-loader就構成了vue-cli 2.0,所以外掛需要一個一個裝,並自動配置在package.json裡。接下來就安裝各種依賴了。
npm install typescript ts-loader --save-dev
裝好之後,如何配置的教程,在ts-loader的npm包文件和ts的官方文件中寫的很詳細,下面我說說我自己重構專案所用到的吧!
先要把ts-loader配置進webpack中,去webpack.config.js用vue-cli2.0也就是build資料夾裡找到webpack.base.conf.js:
resolve: {
extensions: ['.js', '.vue', '.json', '.ts'], // 這裡加入.ts檔案的解析,如果使用了tsx,一併加上
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
然後新增ts-loader:
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/],
}
},
由於使用了語法檢測,所以還需要這個檔案裡配置,再把入口檔案改成.ts,去src目錄下面把main.js改成main.ts:
const createLintingRule = () => ({
test: /\.(js|vue|ts|tsx)$/, // 配置好ts,tsx的語法檢測支援
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
entry: {
app: './src/main.js' //改成.ts
},
接下來要把專案重新執行起來,就要去ts官方文件看看了。同上講下專案所涉及到的。
TypeScript使用tsconfig.json
檔案管理工程配置,所以需要在vue-cli第一層檔案中加上這個檔案。
檔案的配置在ts官網專案配置也是做了很耐心的講解,下面我講下我所寫的吧。
// tsconfig.json
{
// 需要編譯的檔案
"include": [
"src/**/*"
],
// 需要忽略編譯的檔案
"exclude": [
"node_modules"
],
// 編譯器
"compilerOptions": {
// 包含的型別宣告檔案路徑列表
"typeRoots": [
"node_modules/@types"
],
// 以嚴格模式解析
"strict": true,
// 允許從沒有設定預設匯出的模組中預設匯入
"allowSyntheticDefaultImports": true,
// 啟用裝飾器
"experimentalDecorators": true,
// 禁用函式引數雙向協變檢查。
"strictFunctionTypes": false,
// 允許編譯javascript檔案
"allowJs": true,
// 採用的模組系統
"module": "esnext",
// 編譯輸出目標 ES 版本
"target": "es5",
// 如何處理模組
"moduleResolution": "node",
// 在表示式和宣告上有隱含的any型別時報錯
"noImplicitAny": true,
// 編譯過程中需要引入的庫檔案的列表。
"lib": [
"dom",
"es5",
"es6",
"es7",
"es2015.promise"
],
"sourceMap": true,
// 給錯誤和訊息設定樣式,使用顏色和上下文。
"pretty": true
}
}
compilerOptions選項可以按照它給那個列表按需求加入,比如新增一些程式碼常量檢測之類的。
如果你的專案裡要使用一些npm包,第三方外掛之類的,要為它們在ts中建立申明檔案,比如你使用了vue。則需要建立個vue.vue-shims.d.ts。
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
詳細使用可以看下文件。
然後把上面提到的vue-class-component,vue-property-decorator(點選進入github地址檢視文件)這兩個外掛裝好就可以了,編寫規則在它們的github倉庫有說明,後者是前者的拓展版,一般選擇後面那個即可。
到這裡基本上就能瘋狂的擼ts程式碼了,是不是感覺很刺激,個人還是推薦直接用vue-cli 3.0,先別說對ts的支援,整個專案看起來也是非常的乾淨。
下面我貼幾部分用ts寫的.vue檔案作為參考吧!詳細的還請各位大爺移步github倉庫檢視。
<template>
<div class="hello">
<form id="loginFrom" method="get" action="#" @submit.prevent>
<div class="input" id="nkdiv">
...
<div id="login-btn">
<my-button
:disabled="button.disabled"
:value="button.value"
:btnStyle="button.btnStyle"
@click.native="login"
/>
</div>
<router-link to="/reset"><span class="button">忘記密碼?</span></router-link>
<router-link to="/register"><span class="button" id="register-btn">註冊新使用者</span></router-link>
</form>
</div>
</template>
<script lang='ts'>
import { Vue, Component } from "vue-property-decorator";
/**
* 這裡使用一個vuex-class的外掛,其實它並不支援用class類去寫vuex,只是單純的做了下vuex對ts的支援而已,
* https://github.com/ktsn/vuex-class
* 所以我自己做了一下簡單的封裝,讓vuex擁有繼承屬性,但是在它github的下面有我真大佬寫的vuex-class.js,是
* 支援類的寫法的,歡迎大家去檢視。https://github.com/lzxb/vuex-class.js
*/
// 這裡用的是裝飾器,分別對應vuex裡面那幾個map...的對映方法
import { Action, Mutation, Getter, namespace } from 'vuex-class';
import { Toast } from '../common/comjs';
// 表示連線的是login module。
const loginModule = namespace('login');
@Component({
components: {
// 這裡寫你引用的元件
},
/*由於vue-property-decorator並沒有開放新增屬性的許可權,所以你使用的與之前data,methods等同級
的屬性,只放到這裡面才能生效,這裡就相當與不用ts寫的那個export裡的環境,但是寫在這裡面,底下類
裡的this上有該屬性,但是拿不到*/
})
export default class login extends Vue {
// 表示對映的是login模組下mutation裡的$isEmpty方法,其他類推
@loginModule.Mutation('$isEmpty') $isEmpty: any;
@loginModule.Getter('_isEmpty') isEmpty: any;
@loginModule.Action('userLogin') userLogin: any;
@loginModule.Mutation('$assignParams') $assignParams :any;
@loginModule.Getter('_res') res: any;
nickname: string = '';
password: string = '';
button: MyButton.Button<MyButton.BtnStyle> = {
disabled: false,
value: '登陸',
btnStyle: {
width: '7.75rem',
height: '1.175rem',
fontSize: '0.5rem'
}
}
created () {
if (!this.$route.query.nickname) return;
this.nickname = this.$route.query.nickname
}
async login () {
let params = {
nickname: this.nickname,
password: this.password
}
this.$isEmpty(params);
if (this.isEmpty) return Toast('', '使用者名稱密碼不能為空');
this.$assignParams(params);
this.button.disabled = true;
await this.userLogin();
setTimeout(() => {
this.button.disabled = false;
}, 1000);
if (this.res.data.token) {
...
}
Toast('', this.res.data);
}
}
基於vue-cli3.0的github地址(專案正在重構中,歡迎fork參與,討論)
基於vue-cli2.0的github地址(敬請期待,srr中...)
個人感覺用ts之後,程式碼風格很爽,程式碼提示齊全,基本只要在那條鏈上,只要一路點點點就可以寫完了,用js的話時常會寫錯函式名,變數名之類的,因為提示很少。可以貼一段程式碼出來看看,真的賊爽。下面是vuex類的寫法,特別推薦狼族大佬的vuex-class.js外掛。
class View extends BaseLoaderData<ChatRoom.View.RequestParams, string> {
readonly namespaced: boolean = true;
public readonly state: ChatRoom.View.State = {
params: {
id: ''
},
res: { code: 0, data: '' },
requestStatus: 'unrequest'
};
async saveView(): Promise<this> {
this.$RequestStart();
const res = await this.api.saveView(this.state.params);
this.$RequestSuccess(res);
return this;
}
}
class ChatRoom extends VuexClass {
readonly namespaced: boolean = true;
articList: ArticList;
view: View;
modules: {
articList: ArticList;
view: View;
};
constructor() {
super(new chatroom());
this.articList = new ArticList();
this.view = new View(new chatroom());
this.modules = {
articList: this.articList,
view: this.view
};
}
}
export default ChatRoom;
歡迎交流,如果覺得有幫助,歡迎start。