分享vue + vuex + typescript的一次項目代碼重寫(ssr+pwa+vuex-class)

    俗話說一個項目,用代碼重寫十次,每次確定收益匪淺。後續還會持續重構ssr等。javascript

在vue裏使用ts,通常分爲兩種狀況:html

  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,去官方文檔看下就知道了。若是你想把這種配置做爲默認配置,文檔也給出了相應的配置以下,這樣你直接點第一個選項就會自動配置了。vue

被保存的 preset 將會存在用戶的 home 目錄下一個名爲 .vuerc 的 JSON 文件裏。若是你想要修改被保存的 preset / 選項,能夠編輯這個文件。java

在項目建立的過程當中,你也會被提示選擇喜歡的包管理器或使用淘寶 npm 鏡像源以更快地安裝依賴。這些選擇也將會存入 ~/.vuercnode

項目初始化完後,就能夠開箱即用了。看它的package.json文件,typescirpt 以及ts-loader幫你配置好了,還有vue-class-componentvue-property-decorator(點擊進入github地址查看文檔)這兩個添加vue對ts支持的插件也安裝好了,後者是前者的拓展版,通常選擇後面那個便可,tsconfig.json和.d.ts文件也配置好了,差很少例子也會幫你寫好了。固然例子所涉及的是遠遠不夠的,能夠去這兩個插件的github倉庫看下文檔,使用起來很是簡單,差很少是把之前vue那種黑盒子的寫法去掉了一層包裝加上了一層語法修飾器。詳細的配置就放到下面的vue-cli 3.0如下步驟中寫吧,也不用這麼累贅了,畢竟下面的須要手動去配置。webpack

vue-cli 3.0如下

首先初始化一個vue-cli 2.0的項目,文檔也給出了相應的方法:git

Vue CLI 3 和舊版使用了相同的 vue 命令,因此 Vue CLI 2 (vue-cli) 被覆蓋了。若是你仍然須要使用舊版本的 vue init 功能,你能夠全局安裝一個橋接工具:es6

npm install -g @vue/cli-init
# `vue init` 的運行效果將會跟 `vue-cli@2.x` 相同
vue init webpack my-project複製代碼

沒什麼特殊要求,一路回車就行了,初始完項目,打開package.json文件看下其依賴,是否是特別的多,在看下script下的dev命令,是基於webpack-dev-server,也就是仍是使用webpack加vue-loader就構成了vue-cli 2.0,因此插件須要一個一個裝,並自動配置在package.json裏。接下來就安裝各類依賴了。github

npm install typescript ts-loader --save-dev複製代碼

裝好以後,如何配置的教程,在ts-loader的npm包文檔ts的官方文檔中寫的很詳細,下面我說說我本身重構項目所用到的吧!web

先要把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+ssr+pwa的github地址(使用了pwa離線緩存技術,歡迎start)

我的感受用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。

相關文章
相關標籤/搜索