vue + typescript 進階篇

三命發表於2017-11-06

本文是繼 Vue + TypeScript 新專案起手式 之後的進階 + 踩坑配置,所以推薦先閱讀前文

完整閱讀完之後,基本可以順利在新專案中使用vue + typescript

另外特別注意!!!

不推薦在已有專案上強加 typescript, 因ts寫法的元件跟之前的元件不相容,若上的話需要修改之前寫的元件

配置完整版可參考 vue-typescript-starter,若沒配置出來,也可以對照修改配置

直接進入正題:

概覽

  • ts 支援 render jsx 寫法
  • ts 支援 es6 / es67
  • 配置 vuex
  • vue 識別全域性方法/變數
  • 支援 mixin
  • 支援 ProvidePlugin 的全域性變數,比如 lodash_

支援 render jsx 寫法

這裡一共分兩步

  1. 首先得先讓 vue 支援 jsx 寫法
  2. 再讓 vue 中的 ts 支援 jsx 寫法

讓 vue 支援 jsx

按照官方做法,安裝Babel 外掛

安裝依賴

npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  babel-preset-es2015\
  --save-dev複製程式碼

.babelrc中新增:

{
  "plugins": ["transform-vue-jsx"]
}複製程式碼

之後就可以這些寫render,如下圖:

讓 ts 支援 jsx

首先配置 webpack
找到./build/webpack.base.conf.js

  • 找到resolve.extensions 裡面加上.tsx 字尾
  resolve: {
    extensions: ['.js', '.vue', '.json', '.ts', '.tsx']
  }複製程式碼
  • 找到module.rules 修改webpack對.tsx .ts 的解析
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
        // 從這裡複製下面的程式碼就可以了
        // 如果之前按照起手式配置的同學,請替換配置
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        enforce: 'pre',
        loader: 'tslint-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: Object.assign(vueLoaderConfig, {
          loaders: {
            ts: "ts-loader",
            tsx: "babel-loader!ts-loader"
          }
        })
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          "babel-loader",
          {
            loader: "ts-loader",
            options: { appendTsxSuffixTo: [/\.vue$/] }
          }
        ]
      },
      // 複製截止
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },複製程式碼

上面的配置,主要意思是 vue 檔案識別ts/tsx程式碼的時候,先過一遍ts-loader,在過一遍babel-loader,我知道這聽起來有點蠢,但是jsx不能不要對吧?

然後在 tsconfig.json中,新增對jsx的支援

  "compilerOptions": {
    "jsx": "preserve"
    }複製程式碼

之後就可以順利在.vue單檔案中的tsjsx程式碼了,如下圖所示:

敲黑板,這裡又有重點,使用 jsx 寫法的話, 一定要使用 .tsx,不要用.ts了,切記!!!

支援es6 / es7

tsconfig.json中,新增對es6 / es7的支援,更多的配置請見tsconfig - 編譯選項

    "lib": [
      "dom",
      "es5",
      "es6",
      "es7",
      "es2015.promise"
    ]複製程式碼

不然的話,連Object.assign 這種最基本的函式也會在ts中報錯,真的令人難過

配置 vuex

這裡就比較簡單了

# 安裝依賴
npm i vuex vuex-class --save複製程式碼
  • vuex:在 vue 中集中管理應用狀態
  • vuex-class :在 vue-class-component 寫法中 繫結 vuex

Store的配置跟原來一模一樣,引用的時候有一點區別,下面的例子介紹了用法,應該一看便知,這裡我不做贅述

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const ModuleGetter = namespace('path/to/module', Getter)

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @ModuleGetter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}複製程式碼

讓 vue 識別全域性方法/變數

在專案中使用 ui 元件是很正常的操作

比如使用 Element-uImeesage,用法如下圖:

  this.$message({
    message: '恭喜你,這是一條成功訊息',
    type: 'success'
  })複製程式碼

但是在配置了 typescript之後

那是因為 $message屬性,並沒有在 vue例項中宣告

解決辦法也非常簡單,那我們就宣告一下唄

在之前文章中建立的 src/vue-shim.d.ts檔案中,增加如下程式碼:

// 宣告全域性方法
declare module 'vue/types/vue' {
  interface Vue {
    $Message: any,
    $Modal: any
  }
}複製程式碼

這樣,之後再使用this.$message()的話就不會報錯了

支援 mixin

我在vue-property-decorator裡裡外外找了好幾圈,缺沒有找到mixin這個修飾器

 // 如果全域性mixin,那也太蠢了
 Vue.mixin( mixin )複製程式碼

找非常多的 ts + vue 專案,但是沒有找到我理想的mixin的方式,
那麼就自己進行探索咯,下圖是我自己使用的目前最佳mixin方式:

宣告瞭一個mixin元件,如下圖:

其實就是我在mixin中宣告瞭宣告屬性 / 方法,那麼我就在vue例項中宣告這個屬性 / 方法

使用方式如下圖:

支援 ProvidePlugin 的全域性變數,比如 lodash 的 _

如果我們在專案中有使用 jquery,lodash 這樣的工具庫的時候,肯定不希望在所有用到的地方都import _ from ‘lodash’
@types/lodash

那我們就來配置一下:

首先還是在webpack.base.conf.js 中新增一個外掛、並把這個 vendor拉出來


  entry: {
    app: './src/main.ts',
    vendor: [
      "lodash"
    ]
  }

  plugins: [
    new webpack.ProvidePlugin({
      _: 'lodash'
    })
  ]複製程式碼

上面的意思是,當模組使用這些變數的時候wepback會自動載入

然後,你需要告訴eslint這個 _ 是全域性的

.eslintrc.js中新增

  globals: {
    _: true
  },複製程式碼

接下來,你還需要告訴ts這個 _ 是全域性的

vue-shim.d.ts

declare global {
  const _: typeof lodash
}複製程式碼

如果沒有上面這段宣告,但是在 ts 中使用的話,會報如下的錯誤:

這個問題Consider allowing access to UMD globals from modules · Issue #10178 · Microsoft/TypeScript · GitHub

有一個很簡單的解釋,就是害怕你全域性宣告的_import _ from 'lodash' 的行為不一致,這樣的話,之後會留下隱患

到這裡,本文的配置就到此結束

最後

本文的這些配置都是在新專案開發中,一步步用血汗踩出來的

目測已經涵蓋了大部分的使用問題,如果有其他的意見或建議的話,歡迎在本文下面評論~~

再發一次,配置完整版可參考 vue-typescript-starter,若沒配置出來,也可以對照修改配置

在剛上typescript的時候,我是拒絕的,嫌棄每個地方都要宣告型別,不然就走不下去,但是如果讓你們做以下一個選擇題:

  • 在編譯時發現問題
  • 還是執行時發現問題

我會毫不猶豫選擇前者,這是ts強型別帶給我最大的亮點

參考連結/推薦閱讀

相關文章