Vue.jsをTypeScriptで書く環境を構築する (Sublime Text編)


スラマッパギ!この記事は Retty Inc. Advent Calendar 2017 19日目です。
昨日は @makoto-nagai さんによる Core Imageを使ったiOS上での画像フィルタの実装 でした。

今日はフロントエンド開発者をいつも悩ませる開発環境ネタです。

TL;DR

Language Server Protocolを使う

Vue.js TypeScript

去る2017年10月前後、TS + Vue界隈に激震が走ったことは皆様も記憶に新しいものだと思います。

そうです。 Vue 2.5 release 「Better TypeScript Integration」 です。
これまでTS + Vueを書く際、オブジェクトリテラルのメソッド内でthisの型推論がうまく行われないため、jsの書式とは一風変わった以下のような vue-class-component を利用したクラスベースのシンタックスで書かざるを得ませんでした。

ClassBase.ts
// https://github.com/vuejs/vue-class-component/blob/master/README.md より引用
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  props: {
    propMessage: String
  }
})
export default class App extends Vue {
  // initial data
  msg = 123

  // use prop values for initial data
  helloMsg = 'Hello, ' + this.propMessage

  // lifecycle hook
  mounted () {
    this.greet()
  }

  // computed
  get computedMsg () {
    return 'computed ' + this.msg
  }

  // method
  greet () {
    alert('greeting: ' + this.msg)
  }
}

対して下記が上記のコードを通常のJSのシンタックスに直したものです。

ObjectLiteral.js
export default {
  props: {
    propMessage: String,
  },
  data() {
    return {
      msg: 123,
    };
  },
  computed: {
    computedMsg: {
      get() {
        return 'computed' + this.msg;
      },
    },
  },
  methods {
    greet() {
      alert('greeting: ' + this.msg);
    },
  },
};

上の比較でもわかるように、クラスベースのシンタックスではJSのオブジェクトリテラルのシンタックスとは大きく異なったAPIを利用する必要がありました。
自分はプライベートではTSで書くことが多いので、TSで初学をしようとしましたが、JSのドキュメントを読みながらだと、全くインターフェースが違う書き方にいつまでも慣れず、諦めてしまいました

そんな中、TypeScriptチームのやっていきなPRでthisの型推論問題が解決されたことをきっかけに、Vue 2.5のリリースへの第一歩が踏み出されました1

Vue 2.5以降では

ObjectLiteral.ts
import Vue from 'vue';

export default Vue.extend({
  props: {
    propMessage: String,
  },
  data() {
    return {
      msg: 123,
    };
  },
  computed: {
    computedMsg: {
      get() {
        return 'computed' + this.msg;
      },
    },
  },
  methods {
    greet() {
      alert('greeting: ' + this.msg);
    },
  },
});

のように Vue.extend の引数にオブジェクトを入れる以外違いのない形で書けるようになっています。
コントリビューターの皆さんありがとうございます!!

We Sublime Text

Sublime Text をお使いの皆様、お待たせしました。
TypeScript-Sublime-Plugin が入ったSublime Textで.vue ファイルを書こうと思い、Type Hintingが効かずに な気持ちになった方も少なからずいらっしゃるのではないでしょうか。

以前まではエディタで正しく補完をさせるには下記のように .vue ファイルを分割するのがデファクトでした。
Separating the html, css, and js into separate files

Component.vue
<template src="./template.pug"></template>
<scripts src="./scripts.ts"></scripts>
<style src="./style.stylus"></style>

ですが、Single File Component の利点である関心のスコープとファイルスコープが一致する点を捨ててまでこんなことしたくない!!

ということでLaungage Server Protocolの導入でこれを解決します。

Language Server Protocolとは

Language Server Protocolは2016年6月にMicrosoftが公開した、エディタ・IDEが開発支援機能だけに分離されたサーバとJSON-PRCで通信する際のプロトコルです。

詳細は Microsoft/language-server-protocollanguage server protocolについて (前編) を参照してください。

今回利用したいVueのSingle File Componentの開発支援をするLanguage Serverは、VSCodeのVue.js開発支援ツールvetur の中の /server としてリポジトリがホスティングされています。
また、これは vue-language-server としてnpmパッケージが登録されています。

以下手順です。

手順

1. Package Control で LSP を入れる

以下 tomv564/LSP を利用して話を進めます。

2. vue-language-server を入れる

npm install -g が許せるタイプの人であれば

$ npm install -g vue-language-server

をしてしまっていいと思います。

自分は許せないので

$ cd path/to/repository/
$ npm install -D vue-language-server

して、npm scriptsに以下のように追記します。

package.json
{
  "scripts": {
    "vls": "vls"
  }
}

どちらでもよいですが、

$ vls # or npm run vls

> [email protected] lsp /Users/xuesuli/src/vue-ts-template
> vls

となればOKです。

3. SublimeでLSPの設定をする

ドキュメントは Sublime LSP Plugin Documentation を確認してください。

// LSP.sublime-settings -- User
{
    "clients": {
        "vue": {
            "command": ["vls"], // or ["npm", "run", "vls"]
            "scopes": ["text.html.vue"],
            "syntaxes": ["Packages/Vue Syntax Highlight/vue.tmLanguage"],
            "languageId": "vue"
        }
    }
}

で完了です。

はかどる

明日は @shinichi-nakagawa-r さんの REST API提供者と自分にやさしいAPIクライアントをPythonでいい感じに作る方法 です。

また明日!スラマッマラム!