wagtail webpack react を使ったアプリケーションの雛形を作る


wagtail で 作成中の Blog の Front 部に React.js を使いたいのですが、調べたところ、ezhome/django-webpack-loader: Transparently use webpack with django という Django plugin を使えば、構築できそうに思いました。

wagtail + django-webpack-loader + react で一通りの設定を行っている情報が見つけられなかったので、個人的な備忘録として手順をまとめます。


前提

以下の環境で作業は実施しています。

  • OS

    % sw_vers
    ProductName:    Mac OS X
    ProductVersion: 10.13.1
    BuildVersion:   17B1003
    

  • Python の Version

    % python3 -V
    Python 3.6.2
    

  • Django の Version

    % python3 -m pip list --format=columns | grep Django
    Django                            1.11.8     
    

事前環境構築として、以下を実施しています。
* Django プロジェクトを mysite という名前で作成


最終的なディレクトリ構成

インストール、設定後、Django プロジェクトは以下のディレクトリ構成になります。

├── assets // jsファイルの配置先
│   ├── js
│   └── webpack_bundles
├── home // django app
├-- db.sqlite3
├-- manage.py
├-- node_modules
├-- package-lock.json
├-- package.json
├-- requirements.txt
├-- webpack-stats.json
├-- webpack.config.js
└── mysite // django project app
手順に従うと、Django プロジェクト直下に、node_moduleをインストールする形式になります。


雛形作成の流れ

雛形作成の流れは以下の通りです。

  1. webpack、react のインストール、設定

  2. django-webpack-loader を インストール、設定

  3. 雛形ページの作成


1. webpack、react、babel のインストール、設定

webpack のインストール、設定

  • インストール
    webpackをつかってdjangoでbootstrap4をはじめる - Qiita を参考に、webpack のインストールを実施します。
    django の プロジェクトルートフォルダに移動し、以下のコマンドを実行します。
    npm init -f
    npm install --save-dev webpack webpack-bundle-tracker 
    

webpack.config.js の作成

django の プロジェクトルートフォルダにwebpack.config.js を作成します。

var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {
  context: __dirname,
  /* エントリーポイント */
  entry: './assets/js/main.js',
  output: {
      path: path.resolve('./assets/webpack_bundles/'),
      filename: "[name]-[hash].js"
  },

  plugins: [
    new BundleTracker({filename: './webpack-stats.json'})
  ]
}

エントリポイントの作成

エントリポイントは、./assets/js/main.js です。
ディレクトリと、main.js ファイルを作成します。
エントリポイントが何かがよくわからなかったので、検索したところ以下がヒットしました。
エントリポイント · JavaScriptの入門書 #jsprimer
現在(2018/01/18)の文章を引用しておきます。

エントリポイントとは、アプリケーションの中で一番最初に呼び出される部分のことです。 アプリケーションを作成するにあたり、まずはエントリポイントを用意しなければなりません。 Webアプリケーションにおいては、常にHTMLドキュメントがエントリポイントとなります。 ウェブブラウザによりHTMLドキュメントが読み込まれたあとに、HTMLドキュメント中で読み込まれたJavaScriptが実行されます。

mkdir -p assets/js
touch assets/js/main.js

js ファイルのビルド

webpack の 初期設定はこれで終了です。
js ファイルをビルドします。

./node_modules/.bin/webpack --config webpack.config.js
------------------------------------------
Hash: 315df4b2f5abf7716c38
Version: webpack 3.10.0
Time: 69ms
                       Asset     Size  Chunks             Chunk Names
main-315df4b2f5abf7716c38.js  2.47 kB       0  [emitted]  main
   [0] ./assets/js/index.js 0 bytes {0} [built]
------------------------------------------

react、babel のインストール、設定

Reactを「webpack + babel-loader」でビルドする方法 | maesblog を参考に react のインストール、設定を行います。

react の インストール

django の プロジェクトルートフォルダに移動し、以下のコマンドを実行します。

npm install --save react react-dom

Babel 関連のパッケージのインストール

babel の関連パッケージをインストールします。

npm install --save-dev babel-loader babel-core babel-preset-react babel-preset-es2015

.babelrc の作成

vi .babelrc
-----------------------------------
{
  "presets": ["react", "es2015"]
}
-----------------------------------

webpack.config.js の修正

babel、react をインストールしたので、webpack.config.js を修正して、babelの設定を追加します。

var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {
  context: __dirname,
  /* エントリーポイント */
  entry: './assets/js/main.js',
  output: {
      path: path.resolve('./assets/webpack_bundles/'),
      filename: "[name]-[hash].js"
  },
  /* ソースマップをファイル内に出力させる場合は以下を追加 */
  devtool: 'inline-source-map',
  module: {
    /* loaderの設定 */
    loaders: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/, // 除外するファイル/ディレクトリ(正規表現可)
        loader: 'babel-loader' // 使用するloader
      }
    ]
  },
  plugins: [
    new BundleTracker({filename: './webpack-stats.json'})
  ]
}

npm コマンドの登録

package.json に以下、コマンドを追加します。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "./node_modules/.bin/webpack --watch --progress",
    "build": "./node_modules/.bin/webpack --config webpack.config.js"
  },

上記コマンドの追加後は、django の プロジェクトルートフォルダ で 以下のコマンドが実行できるようになります。

npm run watch
npm run build
# これは`package.json` 追加後、デフォルトで定義されているコマンドになります。
npm run test

以上で、react、babel のインストールは完了です。


2. django-webpack-loader を インストール、設定

続いて、django と webpack の 連携用の plugin ezhome/django-webpack-loader: Transparently use webpack with django をインストールします。

django-webpack-loader のインストール

pip でインストールします。1

python3 -m pip install django-webpack-loader
-------------------------------------------
Collecting django-webpack-loader
  Downloading django_webpack_loader-0.5.0-py2.py3-none-any.whl
Installing collected packages: django-webpack-loader
Successfully installed django-webpack-loader-0.5.0
-------------------------------------------

STATICFILES_DIRS の作成 と設定

django の デフォルトの静的コンテンツディレクトリは、static で、 STATICFILES_DIRS には以下の通り定義されているかと思います。

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

これを、README.md の内容に合わせて、assets に変更します。

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

settings.py に WEBPACK_LOADER を追加する

以下も、README.md の記載の通りです。
settings.py に WEBPACK_LOADER を追加します。

WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': not DEBUG,
        'BUNDLE_DIR_NAME': 'webpack_bundles/', # must end with slash
        # PROJECT_DIRからここは変えているかもしれません。   
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
        'POLL_INTERVAL': 0.1,
        'TIMEOUT': None,
        'IGNORE': ['.+\.hot-update.js', '.+\.map']
    }
}

INSTALL_APPS に webpack_loader を追加する

settings.py の INSTALL_APPS に webpack_loader を追加します。

INSTALLED_APPS = [
    ...
    'webpack_loader',
]   

動作確認用のため、template に js 読み込み記述を追加

テンプレートファイルに以下の記述を追加します。

{% load render_bundle from webpack_loader %}
{# 以下は、body タグの直前に #}
{% render_bundle 'main' %}

サーバー起動して、動作確認

サーバーを起動して、動作確認します。

python3 manage.py runserver    
サーバーを起動、http://127.0.0.1:8000 にアクセスすると、{% render_bundle 'main' %} が、<script type="text/javascript" src="/static/webpack_bundles/main-315df4b2f5abf7716c38.js" ></script> に変換され、ページが表示できました。


3. 雛形ページの作成

react の 雛形ページを作成します。
mzabriskie/react-example: Simple React example app を参考に作成しました。
必要なファイルは、App.jsx と、main.js になります。
wagtail helloword を出力する。アプリケーションになります。
* assets/js/App.jsx

import React from 'react';

class App extends React.Component {
  render() {
    return (
      <div>wagtail, helloworld</div>
    );
  }
}
export default App;

  • assets/js/main.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    import App from './App.jsx';
    
    ReactDOM.render(<App />, document.getElementById('app'));
    

発生したエラー

インストール、動作確認時に発生したエラーを記載します。

Module parse failed: Unexpected token (6:6)

React の サンプルアプリケーションを追加した後、webpack のビルド時に以下のエラーが出力されました。

ERROR in ./assets/js/App.jsx
Module parse failed: Unexpected token (6:6)
You may need an appropriate loader to handle this file type.
以下 issue に記載がありました。
JSX is not transform by babel loader from node_modules/my_module · Issue #377 · babel/babel-loader
コンパイルする js の拡張子として、jsx の記載がないとエラーになるようです。
webpack.config.js に、jsx の記述を追加したところうまく動きました。
  module: {
    /* loaderの設定 */
    loaders: [
      {
        test: /\.(js|jsx)$/, // jsxも対象にする
        exclude: /node_modules/, // 除外するファイル/ディレクトリ正規表現可loader: 'babel-loader' // 使用するloader
      }
    ]
  },

Target container is not a DOM element.

画面表示時に以下のエラーが発生しました。

Target container is not a DOM element.
これは、よく見るエラーで、react js を含む、webpack が生成した js をフッタ部に記載したところ上手く動作しました。

OSError: Error reading /Users/xxxxx/xxxx/webpack-stats.json.

Django の ローカルサーバーを起動して、動作確認したところ以下のエラーが発生しました。

OSError: Error reading /Users/xxxxx/xxxx/webpack-stats.json. Are you sure webpack has generated the file and the path is correct?
ドキュメントに、以下のような記載があります。

One of the core principles of django-webpack-loader is to not manage webpack itself in order to give you the flexibility to run webpack the way you want. If you are new to webpack, check one of the examples, read my detailed blog post or check webpack docs.

django-webpack-loader は webpack 自体の管理は行わないので、別途ビルドが必要な旨が記載されています。
webpack の ビルド後にサーバ起動したところ発生しなくなりました。


補足

django * react の 連携 は 謎が多くあり、なんとなく検索して、ハマりながら作業を進めました。
ひとしきり作業してから、以下の記事を見つけました。
Create React App and Django
どちらかというとこの構成が一般的な気がしました。
次回構築するときは、この記事を参考に進めようかなと思います。

以上です。


  1. README.md には、npm install --save-dev webpack-bundle-tracker の記載がありますが、インストール先の調整が必要に思いましたので、とばしています。 

コメント