既存のDjangoアプリケーションにVue.jsを導入

# はじめに
実務にて、PythonのDjangoフレームワークにて構築されたWebアプリケーションに対して、後付けでVue.jsを適用したのでその流れを自分の備忘として残します。
Djangoのテンプレート機能(主なライブラリとしてjQuery)を利用して作成していたWeb画面を全てVue.js（Webpackビルド）に置き換えをしました。
Vue.js導入にあたって、Vue-Router,Vuexも導入
フロントエンドのライブラリはBootstrap-vueとjQueryを採用
※ DOM操作以外の既存コードは極力流用したいがためにajax部分も含めてjQueryを採用しました。

## 既存アプリケーションの構成
Djangoのテンプレート機能とREST frameworkが共存している構成
フロントエンドのライブラリは主にBootstrap4とjQueryを利用していました。

```text:ディレクトリ構成（※必要最低限の情報を記載しています）
:root
 └─ :django-root
     ├─ manage.py
     └─ :django-mysite
        ├─ settings.py
        ├─ urls.py
        └─ :my-app
           ├─ :static
              ├─ :css
              ├─ :images
              └─ :js
           ├─ :templates
              └─ index.html
           ├─ __init__.py
           ├─ models.py
           ├─ serializer.py
           ├─ urls.py
           └─ views.py
```

## Vue.jsのインストール ＆ プロジェクト作成
まずはvue-cliのインストール
``` $ npm install -g vue-cli ```

webpackビルド用のプロジェクトを作成
``` $ vue init webpack vue-app ```
vue-router,vuexを入れるか問われるのでYESを選択
忘れたときは以下コマンドを実行します。
``` $ npm install vue-router --save ```
``` $ npm install vuex --save ```

この時点で以下の様なディレクトリ構造になります。

```text:ディレクトリ構成（vue-app追加）
:root
 ├─ :django-root
 └─ :vue-app
```
## Webpackの設定
Webpackでビルドしたhtml,css,jsファイルをDjango側のディレクトリ（templates,static)配下に配置するため設定を変更

```javascript:vue-app/config/index.js
module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    // proxyTable: {},
    proxyTable: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        pathRewrite: { // 開発サーバーでもDjangoのAPIにアクセスできるように設定
          '^/api': 'api'
        }
      }
    },

    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    // Use Eslint Loader?
    // If true, your code will be linted during bundling and
    // linting errors and warnings will be shown in the console.
    useEslint: true,
    // If true, eslint errors and warnings will also be shown in the error overlay
    // in the browser.
    showEslintErrorsInOverlay: false,

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-module-eval-source-map',

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true,

    // デフォルトのSourceMapは相対パスを参照しているらしく、この環境ではバグになるらしい。
    cssSourceMap: false
  },

  build: {
    // Template for index.html
    // index.htmlの出力先をDjangoのtemplates配下に変更
    index: path.resolve(__dirname, '../../django-root/myapp/templates/dist/index.html'),

    // Paths
    // 静的ファイルの出力先をDjangoのstatic配下に変更
    assetsRoot: path.resolve(__dirname, '../../django-root/myapp/static'),
    assetsSubDirectory: 'build',
    assetsPublicPath: '/static/',

```
## Django側のURL設定
Django側に出力したindex.htmlを配信するためのURLを追加
DjangoのREST APIはそのまま利用するため、URLは明示的に分ける必要があります。

```python:urls.py
urlpatterns = [
    url(r'^$', schema_view),
    url(r'^admin/', admin.site.urls),
    url(r'^v1/api', include('myapp.urls')), # 既存のAPIやtemplateのレンダリング
    url(r'^v2/vue', views.top), # Vue.js root url
]

```

## Django側のViewsに追加
urls.pyに追加したのでviews.pyにもレンダリング用の設定を追加

```python:views.py
# **** ＜Vue.js テンプレート配信＞ ****
def top(request):
    """Renders the about page."""
    assert isinstance(request, HttpRequest)
    return render(request, 'dist/index.html')
```

## ライブラリの導入
- jQuery: ajax通信、既存コード流用時に必要になる。
- Bootstrap-vue: UIはBootstrapで統一する。
- fontawesome/vue: Bootstrap-vueにはアイコンがないため導入
- vue-loading-spinner: ローディングアイコンを自作するのが面倒だったため導入

以下コマンドを実行
```$ npm install jquery --save```
```$ npm i bootstrap-vue --save```
```$ npm i --save @fortawesome/fontawesome-svg-core \```
```$ npm i --save @fortawesome/fontawesome-svg-core```
```$ npm i --save @fortawesome/free-solid-svg-icons```
```$ npm i --save @fortawesome/vue-fontawesome```

main.jsでライブラリをimport

```javascript:main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import App from './App'
import router from './router'
import store from '@/store/index'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faTrash, faSearch, faDownload } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

library.add(faTrash)
library.add(faSearch)
library.add(faDownload)

Vue.component('font-awesome-icon', FontAwesomeIcon)

Vue.use(BootstrapVue)
Vue.config.productionTip = false

/* eslint-disable */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

```
jQueryは使用する各vueファイルにて読み込みとしました。

```javascript:hoge.vue
<script>
import JQuery from 'jquery'
let $ = JQuery
```
入れた後で``` import $ from 'jquery’```と書けばよかったと気づきました。
## vue-app配下のディレクトリ構成
以下が完成形です。

```text:vue-app(解説しないところは割愛しています)
:vue-app
 ├─ :build
 ├─ :config
 ├─ :node_modules
 ├─ :src                          # vue-appで使用するメインのソースの格納場所
 |   ├─ :components                # 単一ファイルコンポーネントのソース格納場所
 |   |   ├─ :mixin                  # MIXINとして作成したコンポーネントの格納場所
 |   |   |   └─ API.vue              # API通信周りの関数を定義したMIXIN
 |   |   ├─ :pages                  # 各画面のコンポーネントの格納場所
 |   |   |   └─ TopPage.vue          # TOP画面のコンポーネント
 |   |   └─ :partial                # 画面部品としてのコンポーネントの格納場所
 |   |       ├─ LoadingIcon.vue      # ローディングアイコンのコンポーネント
 |   |       └─ PageHeader.vue       # ヘッダーのコンポーネント
 |   ├─ :lib                       # 純粋なJSファイルの格納場所
 |   |   └─ utility.js              # 汎用的に使用できる関数を集めたソース
 |   ├─ :router                    # vue-router関連のソース格納場所
 |   |   └─ index.js                # vue-routerのルーティング定義
 |   ├─ :store                     # vuex関連のソース格納場所
 |   |   ├─ :modules                # モジュール分割した状態管理ソースの格納場所
 |   |   |   └─ User.js              # Userの状態管理
 |   |   └─ index.js                # vuexの共通ソース
 |   ├─ App.vue                    # Vueインスタンスから一番最初に呼ばれるコンポーネント
 |   └─ main.js                    # Vueインスタンスの生成、ライブラリの読み込みなど 
 └─ :static # ビルドしない静的ファイル
     └─ favicon.ico
```
## 終わりに
フロントエンド開発は未経験でしたが、
先人の知恵（記事）を参考にしながら試行錯誤した結果、比較的見通しの良い構成になったと思います。

## 参考にしたサイト
- [Django + Vue.js な開発環境を作ろう](https://qiita.com/kawaMk4/items/89b18c608dc7dd2b946b)
- [Django + Vue.js(webpack使用)でView機能はVueのみを使う場合の環境設定について](https://teratail.com/questions/129132)
- [大規模Vue.jsアプリを開発するときのディレクトリ構成考えた](https://qiita.com/wonohe/items/529321475d68b9191036)
- [Bootstrap Vue](https://bootstrap-vue.js.org/)
- [jQuery(npm)](https://www.npmjs.com/package/jquery)
- [Font Awesome Vue](https://fontawesome.com/how-to-use/on-the-web/using-with/vuejs)
- [vue-loading-spinner(npm)](https://www.npmjs.com/package/vue-loading-spinner)