Introduction

Le monde du développent des sites web est tellement vaste que chaque jour nous, les développeurs, sommes amenés à utiliser/apprendre des nouvelles technologies. La tendance à la mode, aujourd’hui, c’est du faire des SPA (Single Page Application) en utilisant, généralement, React, Angular et VueJS (je note aussi Svelte) frameworks. Cependant, comme étant un développeur Python à la base, j’ai cherché sur net un tutoriel qui étale comment on devra procéder pour créer un site web en utilisant Flask et React.

Hélas, tout ce que j’ai trouvé ce sont soi des tutoriels qui utilisent deux serveurs web l’un pour Flask et l’autre pour React et que dans le passage en PROD on devra simuler le meme environnement, ou bien des tutoriels qui fonctionnent pas.

Pour moi, ce genre d’approche n’est pas la bonne, à vous de voir si ça vous convient ou pas. Mais personnellement je ne veux pas gérer deux environnements différents et surtout ne pas gérer deux serveurs web différents. C’est pour cela je me suis dit qu’il est temps que j’écris ce tutoriel pour vous montrer une autre approche d’utilisation de ces frameworks pour faire du SPA et les intégrer avec une app Flask et servir le tour dans un seul environnement et surtout avec un seul serveur web.

Installation

Pour commencer, on va créer un virtalenv pour notre app en Flask:

$> mkdir flask_react
$> cd flask_react
$> virtualenv venv
$> source venv/bin/activate
$> pip install flask
(venv)$>

Ensuite on doit installer NodeJS, NPM (>=v8) React app et Webpack:

(venv)$> npm init react-app frontend

Ensuite on va installer Webpack et Webpack-cli:

(venv)$> cd frontend
(venv)$> npm install --save-dev webpack
(venv)$> npm install --save-dev webpack-cli
(venv)$> npm i @babel/core babel-loader @babel/preset-env @babel/preset-react babel-plugin-transform-class-properties --save-dev

Maintenant, nous devons modifier le fichier packages.json comme suit:

 "scripts": {
    "dev": "webpack --mode development ./src/index.js --output ./compiled/bundles.js",
    "build": "webpack --mode production ./src/index.js --output ./compiled/bundles.js",
    "watch": "webpack --watch --mode development ./src/index.js --output ./compiled/bundles.js"
  },

Et ajouter un fichier .babelrc avec ce contenu:

{
    "presets": [
        "@babel/preset-env", "@babel/preset-react"
    ],
    "plugins": [
        "transform-class-properties"
    ]
}

Ensuite installer Webpack live reload plugin, CSS loader et SVG loader pour React:

(venv)$> npm install --save-dev webpack-livereload-plugin
(venv)$> npm install --save-dev css-loader
(venv)$> npm install svg-inline-loader react-inlinesvg --save-dev

Finalement, on doit ajouter un fichier nommé webpack.config.js et on ajoute dedans:

var LiveReloadPlugin = require('webpack-livereload-plugin');

module.exports = {
    plugins: [
        new LiveReloadPlugin()
    ],
    watchOptions: {
        ignored: /node_modules/
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader"
          }
        },
        {
           test: /\.css$/i,
           use: ['style-loader', 'css-loader'],
        },
        {
            test: /\.svg$/,
            loader: 'svg-inline-loader'
        }
      ]
    }
  };

Ajouter le bundle.js à notre Flask app

À ce niveau on a configuré React et Webpack, ce qu’il nous reste à faire c’est de configurer Flask pour qu’il puisse lire le fichier JS compilé et l’ajouter comme un fichier statique à index.html. Pour le faire, on va modifier un peu le fichier public/index.html comme suit:


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="{{ url_for('static', filename='public/favicon.ico') }}" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="{{ url_for('static', filename='public/logo192.png') }}" />
    <link rel="manifest" href="{{ url_for('static', filename='public/manifest.json') }}" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script src="{{ url_for('static', filename='compiled/bundles.js') }}?v={{ range(50)|random }}"></script>
  </body>
</html>

PS: Vous pouvez avoir du mal à faire le bon rendu des images SVG, c’est pourquoi je vous conseil de modifier le fichier src/index.js et utiliser le tag SVG via le paquet svg-inline-loader comme suit:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import SVG from 'react-inlinesvg';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <SVG src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

Creattion d’une vue avec Flask:

app.py

from flask import Flask, render_template
from flask.views import MethodView


application = Flask(
    __name__,
    template_folder='frontend/',
    static_folder='frontend/'
)


class IndexView(MethodView):
    template_name = 'public/index.html'

    def get(self):
        return render_template(self.template_name)


application.add_url_rule('/', view_func=IndexView.as_view('index'))

wsgi.py

from app import application


if __name__ == '__main__':
    DEBUG = True
    PORT = 5000
    HOST = '0.0.0.0'
    application.run(host=HOST, port=PORT, debug=DEBUG)

Tout exécuter!

Pour exécuter ce tutoriel vous avez 3 modes avec NPM et 2 modes avec Flask. Dans, le fichier packages.json on a spécifié 3 modes qui sont:

Et au niveay de Flask, on a deux modes selon la valeur de la variable DEBUG.

Ceci dit, en mode de développent pur on pourra lancer 2 terminales: Une terminale qui lance le mode watch de l’app en React et l’autre lance le serveur de développent de Flask en mode DEBUG.

En gros:

# Première terminale
(venv)$> flask wsgi.py
# Deuxième terminale
(venv)$> cd frontend
(venv)$> npm run watch

Par contre en mode de Production, ce qu’on a à faire c’est juste lancer:

(venv)$> npm build

Et ensuite servir notre application Flask avec Gunicorn ou autre en se servant du fichier React/JavaScript compilé frontend/compiled/bundles.js.