Posted at

既存の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を利用していました。


ディレクトリ構成(※必要最低限の情報を記載しています)

: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

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


ディレクトリ構成(vue-app追加)

:root

├─ :django-root
└─ :vue-app


Webpackの設定

Webpackでビルドしたhtml,css,jsファイルをDjango側のディレクトリ(templates,static)配下に配置するため設定を変更


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は明示的に分ける必要があります。


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にもレンダリング用の設定を追加


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


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ファイルにて読み込みとしました。


hoge.vue

<script>

import JQuery from 'jquery'
let $ = JQuery

入れた後でimport $ from 'jquery’と書けばよかったと気づきました。


vue-app配下のディレクトリ構成

以下が完成形です。


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


終わりに

フロントエンド開発は未経験でしたが、

先人の知恵(記事)を参考にしながら試行錯誤した結果、比較的見通しの良い構成になったと思います。


参考にしたサイト