Visualforce上で動くTypeScript + Vue.jsをVisual Studio Codeで開発する

# <font color="red">※ 記事の内容を更新しました(下記参照)</font>
- [Visualforce with Vue.js(ES5)](https://www.kenichi-odo.com/articles/2017_12_08_visualforce-with-vue)
- [Salesforce DX with LDS + TypeScript + Vue.js](https://www.kenichi-odo.com/articles/2018_01_06_sfdx-with-lds-and-typescript-and-vue)



# 事前準備
 - [Homebrew](https://brew.sh/index_ja.html)
 - [MavensMate-Desktop](https://github.com/joeferraro/MavensMate-Desktop/releases/tag/v0.0.11-beta.6)
    - Visual Studio Codeのmavensmate拡張機能を動かすためにバージョンがv0.0.11-beta.6のものを使用する必要があります
 - [nodebrew](https://github.com/hokaccha/nodebrew)
    - npmコマンド用です

## Visual Studio Codeのインストール
```bash
brew cask update
brew cask install visual-studio-code
```

### 拡張機能をインストール
サイドバーで拡張機能を開き、下記を検索・インストールします。

 - mavensmate
    - Force.com開発用拡張機能
 - vetur
    - Vueファイルの構文チェック・シンタックスハイライト・ドキュメントのフォーマットを行うための拡張機能

# ビルド環境作成
適当な場所にフォルダを作成し、フォルダ内に下記ファイルを作成します。

## package.json
パッケージ管理ファイルです。
scriptsのwebpackの引数には最終的にアップロードする静的リソース名を設定します(今回はVueTest)。

```json:package.json
{
  "devDependencies": {
    "typescript": "^2.2.2",
    "vue": "^2.2.6",
    "vue-loader": "^11.3.4",
    "vue-template-compiler": "^2.2.6",
    "vue-ts-loader": "0.0.3",
    "vue-typescript-import-dts": "^3.0.1",
    "webpack": "^2.3.3",
    "webpack-sfdc-deploy-plugin": "^1.1.9"
  },
  "scripts": {
    "build-VueTest": "webpack --env.resource_name=VueTest",
    "watch-VueTest": "webpack -w --env.resource_name=VueTest"
  }
}
```

## webpack.config.js
ビルド設定ファイルです。
package.jsonのscriptsで設定したresource_nameと一致するフォルダをビルドして、そのresource_nameでForce.com環境へ静的リソースとしてアップロードします。

```js:webpack.config.js
var Webpack = require('webpack')
var SfdcDeployPlugin = require('webpack-sfdc-deploy-plugin')

module.exports = function (env_) {
  return {
    entry: './src/' + env_.resource_name + '/index.ts',
    output: {
      filename: './build/' + env_.resource_name + '/bundle.js'
    },
    devtool: 'source-map',
    module: {
      loaders: [
        { test: /\.ts$/, loader: 'vue-ts-loader' },
        { test: /\.vue$/, loader: 'vue-loader' }
      ]
    },
    resolve: { alias: { vue: 'vue/dist/vue.js' } },
    plugins: [
      new Webpack.LoaderOptionsPlugin({
        options: {
          resolve: { extensions: ['.ts', '.vue'] },
          vue: {
            loaders: { js: 'vue-ts-loader' }
          }
        }
      }),
      new SfdcDeployPlugin({
        credentialsPath: __dirname + '/sfdc-org-config.js',
        filesFolderPath: './build/' + env_.resource_name,
        staticResourceName: env_.resource_name
      })
    ]
  }
}
```

### ビルド前プロジェクト構成イメージ
<img width="200" alt="0.png" src="https://qiita-image-store.s3.amazonaws.com/0/110768/18a01b45-d153-d05c-99f3-78307f5e1b29.png">

### ビルド後プロジェクト構成イメージ
<img width="200" alt="1.png" src="https://qiita-image-store.s3.amazonaws.com/0/110768/d75ec8cb-1c91-4a5d-6ff5-c17c8eb1b3a2.png">

## sfdc-org-config.js
Force.comアップロード時のログイン情報記述ファイルです。
Force.comのログイン情報を記述します(`webpack-sfdc-deploy-plugin`が**<font color="red">Sandbox環境のログインに対応していない</font>**ので方法は後述)。

```js:sfdc-org-config.js
module.exports = {
  username: 'user@test.com',
  password: 'password',
  token: ''
}
```

## tsconfig.json
TypeScriptの設定ファイルです。
vue-typescript-import-dtsは`import from`で.vueファイルをインポートできるようにします。

```json:tsconfig.json
{
  "compilerOptions": {
    "module": "es2015",
    "moduleResolution": "node",
    "types": [
      "vue-typescript-import-dts"
    ],
    "allowSyntheticDefaultImports": true
  }
}
```

## パッケージインストール
作成したフォルダまでターミナルで移動し、`npm i`を実行してpackage.jsonで定義したパッケージをインストールします。

# ソース作成
## TypeScript + Vue
srcフォルダを作成、その配下にpackage.jsonのscriptsに設定した静的リソース名(`VueTest`)のフォルダを作成し、下記ソースを作成します。

### index.ts
```ts:index.ts
import Vue from 'vue'

import App from './app.vue'


new Vue({
  el: '#app',
  render: _ => _(App)
})
```

### app.vue
```html:app.vue
<template>
  <div>
    {{text}}
    <child-component>
    </child-component>
  </div>
</template>

<script>
import ChildComponent from './child-component.vue'

export default {
  data() {
    return { text: 'foobar' }
  },
  components: {
    'child-component': ChildComponent
  }
}
</script>
```

### child-component.vue
```html:child-component.vue
<template>
  <input type="button"
         :value="button_name"
         @click="showAlert" />
</template>

<script>
export default {
  data() {
    return { button_name: 'foo' }
  },
  methods: {
    showAlert(): void {
      alert('bar')
    }
  }
}
</script>
```

### アップロード
フォルダルートで`npm run build-VueTest`を実行することで、静的リソースがアップロードされます。
<img width="490" alt="3.png" src="https://qiita-image-store.s3.amazonaws.com/0/110768/9eccab1a-a7ef-8f41-3b33-5a668ead8407.png">


## Visualforce
 - `command + shift + P` → `mnp`とタイピング → `MavensMate: New Project`を選択し、Force.com環境のソースを取得する
    - 取得後、`Open in Vscode`でVisual Studio Codeでプロジェクトフォルダを開く
 - `command + shift + P` → `mnvp`とタイピング → `MavensMate: New Visualforce Page`を選択し、**VueTest.page**を作成する

### VueTest.page
静的リソースのVueTestの中には、ZIP圧縮された中に**bundle.js**が含まれているのでそれをインポートします。

```html:VueTest.page
<apex:page
  sidebar="false"
  showHeader="false"
  standardStylesheets="false"
  applyBodyTag="false"
  applyHtmlTag="false"
  docType="html-5.0">

<html>

  <head>
    <meta charset="UTF-8" />
  </head>

  <body style="margin: 0;">
    <div id="app"></div>
    <script src="{!URLFOR($Resource.VueTest, 'bundle.js')}"></script>
  </body>

</html>

</apex:page>
```

# 実行結果
<img width="490" alt="4.png" src="https://qiita-image-store.s3.amazonaws.com/0/110768/8bd05afb-8835-a98d-2fde-75eb2e01bd1c.png">

# まとめ
 - 上記サンプル + Sandbox環境ログインに対応したものを[GitHub](https://github.com/kenichi-odo/typescript-vue-visualforce/releases/tag/1.1)に上げました
    - sfdc-org-config.jsにログインURLを設定できる`url`プロパティを追加
    - webpack.config.jsに静的リソースのキャッシュコントロールを設定する`isPublic`プロパティを追加
        - 上記修正は[プルリクエスト](https://github.com/henko-okdev/webpack-sfdc-deploy-plugin/pulls)済み
 - 使用する際ルートフォルダで`npm i`でパッケージインストールしてから使用してください
 - 補足ですが、`npm run watch-VueTest`でファイル監視状態になるので毎回`npm run watch-VueTest`でビルドしなくでもファイル保存だけでビルド → アップロードが走ります

# 参考文献
 - [TypeScript のサポート - Vue.js](https://jp.vuejs.org/v2/guide/typescript.html)
 - [Vue2 + TypeScript2 -- an introductory guide](https://herringtondarkholme.github.io/2016/10/03/vue2-ts2/)