サーバー系エンジニアがVue.jsとTypeScriptに入門して10日ほど勉強した内容をまとめてみました。

普段の業務では主にサーバーサイドを担当することが多いのですが、Vue.jsとTypeScriptを使ったフロントエンドの作業を担当する機会がありましたので、10日ほどの間でざっと勉強した内容をまとめてみました。

(フロントエンドに関しては初学者のため理解の浅い部分が多々あると思います。誤り等ありましたらご指摘頂けますと大変ありがたいです)

ES5

ECMAScript5。要するに古いJavaScript。モダンなブラウザであればほとんど動作する。モジュールという概念が存在しないので、ファイルの依存関係の管理がとても大変。

CommonJS/AMD

ES5の欠点を補うため、つまりJavaScriptのコードをモジュールとして扱えるようにするために、CommonJSやAMDという仕様が作成された。CommonJSは主にサーバーサイドでの用途を想定(node.jsもCommonJS方式)。AMDはブラウザでの使用を前提としているらしい。

CommonJS
// hello.js
module.exports.greet = function () {
  console.log('Hello!');
};

// index.js
var hello = require('hello');
hello.greet(); // Hello!
AMD
// hello.js
define(function () {
  return function greet() {
    console.log('Hello!');
  };
});

// index.js
define(["hello"], function (hello) {
  hello.greet(); // Hello!
});

AMDに関しては、依存するモジュールの数が多くなると記述が大変になる。AMDに対応しているライブラリであるRequireJSはこの問題に対応しており、下記のような記法を使用可能。

RequireJS
define(function(require, exports, module) {
  var hello = require('hello');
  var foo = require('foo');
  var bar = require('bar');
  ...
  hello.greet(); // Hello!
});

CommonJSは(Webpack等を使用した)ビルド時に依存関係を解決する方式だが、AMD(RequireJS)は実行時に依存関係を解決する方式である。

CommonJSはツールやライブラリではなく仕様のため、ビルド時に依存関係を解決する何らかのツールが必要となる。それが後述するBrowserifyWebpackである。

AMDも仕様のため、それに対応するライブラリが必要である。それがRequireJSであり、さらにRequireJSはAMDには存在しない便利記法を導入している。

コードの中でmodule.exportsrequireが使われていたらCommonJS形式、defineが使われていたらRequireJS(AMD)と考えて良いのだが、両方にrequireという用語が登場するので初学者にはここがちょっと分かりにくい。(学習を始めた当初は、requireというキーワードが使われていたらRequireJSが使われているのだと勘違いしていました)

最近は後述のWebpackを使ってビルド&バンドルする方式が主流になっており、AMDやRequireJSに関してはあまり使われなくなっているようなので、Webpackだけ勉強しておけば良いようである。

ES6(ES2015)

新しいバージョンのJavaScript。一部の機能はモダンなブラウザでも動作しないため、後述するbabel等のトランスパイラを使用して、ES2015で記述したコードをES5に変換する方式が主流になっている。

ES6で導入された機能は色々あるが、モジュール管理用に導入されたimport/export機能の理解と、CommonJSのmodule.exports/requireとの区別が初学者にとっては重要。(Vue.jsのコードの中でも両方のキーワードが登場するので混乱するが、設定ファイル系はCommonJS方式のmodule.exports/require、コンポーネント等のコードはES2015のimport/exportを使うと考えておけば良いと思われる)

babel

ES2015のコードをES5にトランスパイルする際に使用される。設定ファイルとして.babelrcが使用される。

presetという概念がある。要するに新しいJavaScriptの機能をコード内でどこまで使用可能にするかをこれで決定するようである。(指定したpresetで対応出来ている機能であれば問題なくES5形式にトランスパイルしてくれるが、そうでなければエラーになるようである)

Vue.jsの場合はstage-2envというpresetが.babelrc内でデフォルトで指定されている。babel-preset-stage-2に関しては、下記の記事で記載されているような機能がES2015のコード内で使用可能になるようである。(babel-preset-env等とは異なり、一部の最新機能だけをチェックするpresetだと思われる)

babel-preset-envに関しては少々分かりにくいが、targetsを指定しない場合はbabel-preset-latestと同様な挙動になるらしい。(babel-preset-latestは、最新のES6の構文を認識してES5に変換してくれるが、ターゲットブラウザやnode.jsのバージョン等は指定出来ないので、既にほとんどのブラウザが対応済みな機能であっても全て古いES5に変換すると考えてよいらしい)

targetsで例えばターゲットブラウザとバージョンを指定した場合は、そのブラウザのバージョンが対応している機能に関しては、(機能の対応テーブルを見て)その機能に関してはコードを古くし過ぎないように変換してくれるようである。多分。

ちなみに、Vue.jsのデフォルトの.babelrcでは、babel-preset-envに関して、下記のように"modules": falseという指定が行われている

  "presets": [
    ["env", {
      "modules": false
    }],
    ...
  ],

これは何かというと、下記の記事で説明されているように、WebpackのTree Shakingを有効にするためである。babelはデフォルトの設定だと、ES2015のimport/exportをCommonJSのmodule.exports/requireに変換してしまうので、"modules": falseを指定することでこの挙動を防止しているようである。

Browserify/Webpack

基本的には、JavaScriptモジュールやCSS等のアセットのバンドル(ファイルを一つにまとめる)を行うためのツールである。

babelはES2015からES5への変換は行うが、JavaScriptのモジュール管理システムには対応していないため、モジュール間の依存関係の解決等は別のツールが行う必要がある。それを行うのがBrwoserifyやWebpack。

Browserifyと異なり、WebpackはAMD(RequireJS)形式の記述にも対応していたり、JavaScriptに加えてCSS等のアセットのバンドル、および単一でなく複数ファイルへの出力にも対応している等の違いがあるようである。ローカルWebサーバー使用時のLive Reload的な機能も備わっている。

Vue.jsではデフォルトでWebpackが使用されるので、特別な理由が無ければWebpackだけ勉強しておけば良いと思われる。

Gulp等のタスクランナーとの違いはなにかという点に関してはいまいち理解出来ていない。タスクランナーと一緒に使うことも可能なようであるが、Vue.jsではタスクランナーは使用せずにnpm-scriptsを使用している。下記の記事にも記載されているが、タスクランナーは使わずにnpm-scriptsWebpackだけでタスクを管理するのが主流(?)になってきているのかもしれない。

Vue.js

ReactやAngular等と同じく、JavaScriptのフロントエンドのフレームワーク。MVVMパターンを採用している。

初学者の学習リソースとしては下記がお薦めと思われる。(ステップバイステップ形式の公式チュートリアル等があると助かるのですが、そういうものはまだ存在していないようです)

(私の場合は、とりあえずVue.js入門でざっと勉強した後は、実際に手を動かしてアプリケーションを作りつつ、分からないことは逐次ネットで調べていくというスタイルで覚えていきました)

エディタとしては、TypeScriptを使うことが前提であれば、公式ドキュメントもお薦めしているVSCodeVeturプラグインの組み合わせが無難と思われる。

現時点ではまだTypeScript用のscaffoldは用意されていないので、vue-cliによるプロジェクト作成後に、独自にTypeScript化を行う必要がある。

Vue.js用のボタンやDatetimePicker等のUIライブラリに関しては、ElemeFE/elementというパッケージが広く使われているようである。

ステート管理にはVuexというライブラリを使用する。Fluxパターンの実装であるとのこと。Reduxに似ているらしい。とりあえず下記等でざっと勉強しておけばよいと思われる。

VuexとLocalStorageを連携させたい場合は、robinvdvleuten/vuex-persistedstateというライブラリが使える。(LocalStorageは、こちらの記事でも記載されているように、モダンブラウザであれば基本的に問題なく使用可能なようである)

Vue.jsでSPAなBingoカードアプリを作ってみてわかったこと - Qiita

TypeScript

TypeScriptの学習リソースに関しては色々あると思いますが、私はとりあえずドットインストールさんの動画でざっと学習しました。

TypeScriptには型定義ファイルというものが存在する。当初からTypeScriptで記述されているモジュールであれば不要だが、JavaScriptで記述されたモジュールをTypeScriptから使用するためには型定義ファイルが必要。

型定義ファイルに関しては初学者向けの学習リソースが見当たらなかったが、下記の記事をじっくりと読んで内容を理解出来れば大体オッケーではないかと思われる。

下記の記事等で説明されているようにTypeScript2からはtsdtypings等の型定義ファイル管理ツールは不要になり、npmだけで型定義ファイルが使用出来るようなったようである。

Vue.jsのTypeScript化に関しては、下記のチュートリアルや記事でざっと学習すればおおよその手順は理解出来ると思われる。

Tips

Vue.jsをTypeScript化する際のwebpack.base.conf.jsの設定

webpack.base.conf.js
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          'babel-loader',
          {
            loader: 'ts-loader',
            options: {
              appendTsSuffixTo: [/\.vue$/]
            }
          },
          {
            loader: 'tslint-loader',
            options: {
              configFile: 'tslint.json'
            }
          }
        ],
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
vue-loader-conf.js
module.exports = {
  loaders: Object.assign(
    { ts: 'ts-loader!tslint-loader' },
    utils.cssLoaders({
      sourceMap: sourceMapEnabled,
      extract: isProduction
    })
  ),

色々な方式があると思われるが、とりあえず上記のような設定を採用した。

この設定の場合、*.tsファイルに関するローダーはtslint-loader -> ts-loader -> babelの順に適用される。

ts-loaderappendTsSuffixToの部分は、*.tsファイルと*.vueファイルの依存関係を解決する際に必要なようである。ts-loader以外にbabel-loaderも使っている理由は、ts-loaderではES5までの変換は行わずにES6までの変換だけを行って、ES6からES5への変換はbabelを使用したいため。

vueLoaderConfig変数にはvue-loader-conf.jsの中身が適用されますが、ここで{ ts: 'ts-loader!tslint-loader' }を指定することで、*.vueファイル内のTypeScriptの部分にもtslint-loaderts-loaderが適用される(はず)。

本文中に記載されていない参考資料