1. 程式人生 > >分享vue + vuex + typescript的一次專案程式碼重寫

分享vue + vuex + typescript的一次專案程式碼重寫

    俗話說一個專案,用程式碼重寫十次,每次肯定收益匪淺。後續還會持續重構ssr等。

在vue裡使用ts,一般分為兩種情況:

  1.     在vue-cli 3.0以下的老專案中。
  2.     在最新的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-componentvue-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-componentvue-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