DjangoでVue.js(webpack)を扱うための備忘録

# 前書き

この記事はPythonをバックエンド（Djangoをフレームワークとして使用）、Node.jsでWebpackを用いてVue.jsをフロントエンドとして使用できるようにするための備忘録記事です。

node.jsの知識が浅いので、使い方を間違っていることもあります。ご了承ください。

また、この記事は`pipenv`や`yarn`を使用しています。導入していない場合は導入してからこの記事を読むことをお勧めします。

# パッケージのインストール

Djangoのstartprojectで自動作成されたフォルダを基にします。

## Python

```bash
pip install django
django-admin startproject django_vuejs
cd django_vuejs
pipenv --python 3
pipenv install django
pipenv install django-webpack-loader==0.7.0
```

## node.js

```bash
yarn init -yp # package.jsonを作る（対話なし）
yarn add --dev @babel/core @babel/preset-env
yarn add --dev babel-loader css-loader sass-loader style-loader vue-loader vue-template-compiler
yarn add --dev webpack webpack-cli
yarn add --dev webpack-bundle-tracker@0.4.3
yarn add vue vuex vue-router
yarn add --dev clean-webpack-plugin # これは任意ですが、これを入れておくとビルドしたjsファイルがかさばらなくなるので導入推奨。
```

気をつけてほしいのは`webpack-bundle-tracker`のバージョン指定です。バージョンが新しすぎてPython側の`django-webpack-loader`が上手く動作しなかったため、バージョン指定しています[^1]。念の為Python側の`django-webpack-loader`のバージョンも指定してます。

# 設定

## webpack

```javascript:webpack.config.js
var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var VueLoaderPlugin = require('vue-loader/lib/plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin'); // clean-webpack-pluginを導入した場合は追記

module.exports = {
  context: __dirname,
  mode: process.env.NODE_ENV,
  entry: {
    main: './src-front/main.js'
  },

  output: {
    filename: "[name]-[hash].js",
    path: path.resolve('./static/build/')
  },

  plugins: [
    new BundleTracker({
      path: '.',
      filename: 'webpack-stats.json'
    }),
    // clean-webpack-pluginを導入した場合は追記(ここから)
    new CleanWebpackPlugin({
      verbose: true
    }),
    // (ここまで)
    new VueLoaderPlugin()
  ],

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              url: false,
              sourceMap: true
            }
          }
        ]
      }
    ]
  },

  resolve: {
    extensions: ['.js', '.vue'],
    modules: [
      'node_modules'
    ],
    alias: {
      'vue': path.resolve('./node_modules/vue/dist/vue.js')
    }
  }
}

```

## Django

```python:django_vuejs/settings.py
# (省略)
INSTALLED_APPS = [
    # ...
    # 以下の文を追記
    'webpack_loader',
]

# 追記
WEBPACK_LOADER = {
   'DEFAULT': {
       'CACHE': not DEBUG,
       'BUNDLE_DIR_NAME': 'build/',
       'TIMEOUT': None,
       'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
       'IGNORE': [r'.+\.hot-update.js', r'.+\.map']
   }
}

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

```

## package.json

`package.json`内に以下の内容を追記します。
`--progress`オプションはwebpackの進捗状況を可視化できるオプションのため、不要であれば付ける必要はありません。

```json:package.json
  "scripts": {
    "dev": "webpack --mode development --progress",
    "build": "webpack --mode production --progress",
    "watch": "webpack --watch -d --progress"
  },
```

## メインページ

これがフロントエンドがベースとなるファイルです。ここにwebpackでビルドしたCSSやjavascriptのリンクが埋め込まれます。

```html:template/index.html
{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>django_vuejs</title>
        {% render_bundle 'main' 'css' %}
    </head>
    <body>
        <noscript>
            <p>Your browser is disabled JavaScript. To use this web application, please enable JavaScript.</p>
        </noscript>
        <div id="app"></div>
        {% render_bundle 'main' 'js' %}
    </body>
</head>
```

## Django URL

※今回は1ファイルにまとめていますが、適宜場所を移してください。

```python:django_vuejs
from django.contrib import admin
from django.urls import path
from django.shortcuts import render

def index(request):
    return render(request, "index.html")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index, name="index"),
]
```

## フロントエンド

```javascript:src-front/main.js
import Vue from 'vue'

import App from './App'
import router from './router.js'
import store from './store'

Vue.config.productionTip = false

const app = new Vue({
    el: '#app',
    router,
    store,
    template: "<App/>",
    components: { App }
})
```

```vue:src-front/App.vue
<template>
  <div>
      <p>Hello World!</p>
      <router-view></router-view>
  </div>
</template>

<script>
export default {
    name: 'App'
}
</script>

<style>

</style>
```

`./router.js`や`./store`はVue.jsのコマンドで自動生成されるものともろ一緒なので、申し訳ないのですが割愛します。

## gitignore (gitで管理する場合は設定)

```plaintext:.gitignore
# 追記
/static/build/
/webpack-stats.json
```

# ビルド

本番用でビルドする場合は`yarn run build`、
開発用としてビルドする場合は`yarn run dev`または`yarn run watch`(フロントエンド系のソースを集中的に弄る場合は推奨)
のコマンドを実行します。

ビルドが完了したら`python manage.py runserver`でDjangoの開発サーバーを起動し、`http://localhost:8000/`にアクセスすると、表示されるはずです。

# 参考文献

- https://note.com/shimakaze_soft/n/nf2128d113fbe

[^1]: 動作しない問題はすでに`django-webpack-loader`の[Issue](https://github.com/owais/django-webpack-loader/issues/227)で上がっています。これを見るまでわかりませんでした・・・。