[webpack] webpack HMR(Hot Hodule Replacement) 應用於 django 搭配 react-hot-loader

前陣子剛換工作時,剛好部門內使用的後端為 django,前端為 react,然後使用 webpack 做編譯、打包及壓縮等佈署的動作。

但在開發前端 react 時必須要重複以下流程,更改程式碼 => 儲存 => 重新整理

想說既然有用 webpack ,就試著利用 webpack 所提供的 HMR(Hot Module Replacement) 來做 hot reload,開發會更快一點。

以下是當初設定的步驟~


dajngo 設定部分:

  1. 安裝 django-webpack-loader
    pip install django-webpack-loader
  2.  設定 settings.py
    import sys
    import os
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'assets/bundles'),
        os.path.join(BASE_DIR, 'assets/static),
    )
    
    WEBPACK_LOADER = {
        'DEFAULT': {
            'BUNDLE_DIR_NAME': '',
            'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
        }
    }
    
    INSTALLED_APPS = (
        ...
        'webpack_loader',
    )
    
    

    讓 django 預設會抓取 assets/bundlesasset/static 這裡個位置的靜態檔案, webpack BUNDLE_DIR_NAME 設為 '',讓 webpack-loader 也至以上兩個路徑抓取靜態檔。

  3. 在 django 使用的 tempalte 中引用 wepack-loader module
    {% load render_bundle from webpack_loader %}<!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Example</title>
    </head>
    
    <body>
    
    <div id="react-app"></div>
    
    {% render_bundle 'main' 'js' %}
    </body>
    </html>
    
    

    上面這段( render_bundle ‘main’ )會幫你注入 script 標籤 (main.js)

 

webpack 設定部分:

  1. 安裝 webpack-dev-server 與 react-hot-loader
    npm install --save-dev webpack-dev-server react-hot-loader
  2. 設定  webpack.config.js
    const path = require('path');
    const webpack = require('webpack');
    const BundleTracker = require('webpack-bundle-tracker');
    
    module.exports = {
      context: __dirname,
      entry: {
        main: ['react-hot-loader/patch',
          'webpack-dev-server/client?http://localhost:3000',
          'webpack/hot/only-dev-server',
          './assets/src/main.js']
      },
      output: {
        path: path.resolve('./assets/bundles/'),
        filename: '[name].js',
        publicPath: 'http://localhost:3000/assets/bundles/',
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new BundleTracker({filename: './webpack-stats.json'})
      ],
      module: {
        loaders: [
          {
            test: /\.js$/,
            loaders: ['react-hot-loader/webpack', 'babel'],
            include: path.resolve(__dirname, "assets/src/")
          },
          {
            test: /\.js$/,
            loaders: ['babel'],
            include: [
              path.resolve(__dirname, "assets/src/")
          }
      }
    }
    

    在上面這段 config,我們將 webpack 的進入點增加 localhost:3000 (為了 HMR 的即時更新畫面),並且設定 django-webpack-loader 的 public path 為 localshot:3000/assets/bundles,這會影響在 webpack_loader template 塞入 script 的 src。

    ps. webpack-bundle-tracker 會產出 webpakc-stats.json,提供 Django 的 webpack_loader 打包後的檔案資訊,以便正確塞對應的標籤.

  3. 新增 webpack-dev-server 的設定檔(server.js)
    var webpack = require('webpack');
    var WebpackDevServer = require('webpack-dev-server');
    var config = require('./webpack.config');
    
    new WebpackDevServer(webpack(config), {
        publicPath: config.output.publicPath,
        hot: true,
        headers: { "Access-Control-Allow-Origin": "*" },
        inline: true,
        historyApiFallback: true,
        stats: 'normal'
    }).listen(3000, '0.0.0.0', function (err) {
    if (err) {
        console.log(err);
    }
    
    console.log('Listening at 0.0.0.0:3000');
    });
    

    上面這個設定檔,設定了一些 HMR 需要的設定,如: CORS…等

 

react 的設定部分,這邊舉有用 react-reduxreact-router 的範例(若無使用以上兩者,可以直接把 AppContainer 包住 root component 即可:

  1. 將 react-hot-loader 套至所有 component
    'use strict';
    import { Provider } from "react-redux";
    import { render } from "react-dom";
    import React from 'react';
    import { Router, Route, browserHistory } from 'react-router';
    import configStore from "./redux/store/config";
    import { AppContainer } from 'react-hot-loader';
    
    const store = configStore();
    const history = syncHistoryWithStore(browserHistory, store);
    const renderApp = () => {
        render(
            <AppContainer>
                <Provider store={store}>
                    <Router history={history}>
                        <Route path="/">
                            .....
                        </Route>
                    <Router>
                </Provider>
            </AppContainer>,
            document.getElementById('root')
        );
    };
    
    if (module.hot) {
        module.hot.accept();
    }
    
    renderApp();
    

    在 react-redux 的 Provider 上面包一層 react-hot-loader 的 AppContainer,並且加上 module.hot.accept();

 

設定到這步就完成了~ 我們就可以執行 node server.js 用 webpack  watch 程式碼改變並即時更新至瀏覽器。

 

參考資料:

http://owaislone.org/blog/webpack-plus-reactjs-and-django/

https://github.com/patrikholcak/hot-loader-demo

Leave a Reply