# [2]Django Rest API + React.js + Redux + webpack 前後端整合 ###### tags: `python` `Django` `Django Rest framework` `React.js` `redux` `webpack` > [time= 2019 11 27 ] > 原文 & 參考: > https://www.youtube.com/watch?v=GieYIzvdt2U&list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60&index=2 <br> ## Implementing React *終端機目前位置 `~/django_rest_api_react/leadmanager/`* ### setting up React and webpack 新增 Django 應用程式 frontend ```= $ python manage.py startapp frontend ``` 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──__init__.py │ │ ├──admin.py │ │ ├──apps.py │ │ ├──models.py │ │ ├──tests.py │ │ └──views.py │ │ │ ├──leadmanager │ ├──leads │ ├──db.sqlite3 │ └──manage.py │ ├──Pipfile └──Pipfile.lock ``` <br><br><br> 建立目錄結構來保存 React components: ```= $ mkdir -p ./frontend/src/components $ mkdir -p ./frontend/{static,templates}/frontend ``` 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──src │ │ │ └──components │ │ │ │ │ ├──static │ │ │ └──frontend │ │ │ │ │ ├──templates │ │ │ └──frontend │ │ │ │ │ ├──__init__.py │ │ ├──admin.py │ │ ├──apps.py │ │ ├──models.py │ │ ├──tests.py │ │ └──views.py │ │ │ ├──leadmanager │ ├──leads │ ├──db.sqlite3 │ └──manage.py │ ├──Pipfile └──Pipfile.lock ``` <br><br><br> 現在要從目前位置 `~/django_rest_api_react/leadmanager/` 移置到上一層資料夾 ```= $ cd .. ``` *終端機目前位置 `~/django_rest_api_react/`* <br><br><br> npm版本: ```= $ npm -- version ``` >*6.9.0* <br><br><br> 初始化環境: ```= $ npm init -y ``` <br><br><br> 安裝 webpack 和 webpack cli: ```= $ npm i -D webpack webpack-cli ``` >*webpack@4.41.2* >*webpack-cli@3.3.10* <br><br><br> 安裝 babel 來編譯我們的代碼: ```= $ npm i -D @babel/core babel-loader @babel/preset-env @babel/preset-react babel-plugin-transform-class-properties ``` >*babel-loader@8.0.6* >*@babel/preset-react@7.7.4* >*babel-plugin-transform-class-properties@6.24.1* >*@babel/core@7.7.4* >*@babel/preset-env@7.7.4* <br><br><br> 安裝 React 和 prop-types: ```= $ npm i react react-dom prop-types ``` >*prop-types@15.7.2* >*react@16.12.0* >*react-dom@16.12.0* <br><br><br> 配置Babel,新增一個檔案命名為 `./.babelrc` 在裡面寫入: ```bable= { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "transform-class-properties" ] } ``` 資料結構 ``` django_rest_api_react ├──leadmanager ├──node_modules ├──.babelrc ├──package-lock.json ├──package.json ├──Pipfile └──Pipfile.lock ``` <br><br><br> 配置babel-loader,新增一個檔案命名為 `./webpack.config.js` 在裡面寫入: ```javascript= module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] } }; ``` 資料結構 ``` django_rest_api_react ├──leadmanager ├──node_modules ├──.babelrc ├──package-lock.json ├──package.json ├──Pipfile ├──Pipfile.lock └──webpack.config.js ``` <br><br><br> 現在開啟 `package.json` 配置 scripts: 將 ```json= "scripts": { "test": "echo \"Error: no test specified\" && exit 1" } ``` 改成 ```json= "scripts": { "dev": "webpack --mode development ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js", "build": "webpack --mode production ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js" } ``` <br><br><br> ### the React frontend 新增一個檔案命名為 `./leadmanager/frontend/src/index.js` 在裡面寫入: ```javascript= import App from './components/App'; ``` <br><br><br> 新增一個檔案命名為 `./leadmanager/frontend/src/components/App.js` 在裡面寫入: ```javascript= import React, { Component } from 'react'; import ReactDom from 'react-dom'; export class App extends Component { render() { return <h1>React App</h1> } } ReactDom.render(<App />, document.getElementById('app')); ``` <br><br><br> 使用 Bootstrap 新增一個檔案命名為 `./leadmanager/frontend/templates/frontend/index.html` 在裡面寫入: ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="https://bootswatch.com/4/cosmo/bootstrap.min.css"> <title>Lead Manager</title> </head> <body> <div id="app"></div> {% load static %} <script src="{% static "frontend/main.js" %}"></script> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script> </body> </html> ``` `./frontend/static/frontend/main.js` 由 webpack 建置時產生 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──src │ │ │ └──components │ │ │ │ └──App.js │ │ │ │ │ │ │ └──index.js │ │ │ │ │ ├──static │ │ ├──templates │ │ │ └──frontend │ │ │ └──index.html │ │ │ │ │ ├──__init__.py │ │ ├──admin.py │ │ ├──apps.py │ │ ├──models.py │ │ ├──tests.py │ │ └──views.py │ │ │ ├──leadmanager │ ├──leads │ ├──db.sqlite3 │ └──manage.py │ ├──node_modules ├──.babelrc ├──package-lock.json ├──package.json ├──Pipfile ├──Pipfile.lock └──webpack.config.js ``` <br><br><br> 開起 `./leadmanager/leadmanager/settings.py` 加入應用程式 `frontend` 在 `INSTALLED_APPS`: ```python= INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'leads', 'frontend', # enable the frontend app ] ``` <br><br><br> creating a views,讓 django 可以導向 `index.html` 開起 `./leadmanager/frontend/views.py` 加入: ```python= from django.shortcuts import render # new add def index(request): return render(request, "frontend/index.html") ``` <br><br><br> 新增一個檔案命名為` ./leadmanager/frontend/urls.py` 在裡面寫入: ```python= from django.urls import path from . import views urlpatterns = [ path('', views.index) ] ``` 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──src │ │ ├──static │ │ ├──templates │ │ ├──__init__.py │ │ ├──admin.py │ │ ├──apps.py │ │ ├──models.py │ │ ├──tests.py │ │ ├──urls.py │ │ └──views.py │ │ │ ├──leadmanager │ ├──leads │ ├──db.sqlite3 │ └──manage.py │ ├──node_modules ├──.babelrc ├──package-lock.json ├──package.json ├──Pipfile ├──Pipfile.lock └──webpack.config.js ``` <br><br><br> 開啟 `./leadmanager/leadmanager/urls.py` 加入: ```python= from django.contrib import admin from django.urls import path, include urlpatterns = [ path('', include('frontend.urls')), # new add path('', include('leads.urls')), ] ``` <br><br><br> Run webpack: *終端機目前位置 `~/django_rest_api_react/`* ```= $ npm run dev ``` 執行完會在 `./leadmanager/frontend/static/frontend/` 新增一個 main.js 現在執行 django 查看 React 是否建置成功 ```= $ cd leadmanager $ python manage.py runserver ``` 前往 http://127.0.0.1:8000/ ![](https://i.imgur.com/WIxduMv.png) <br><br><br> creating a Header 新增一個檔案命名為 `./leadmanager/frontend/src/components/layout/Header.js` 在裡面寫入: ```javascript= import React, { Component } from 'react' export class Header extends Component { render() { return ( <nav className="navbar navbar-expand-sm navbar-light bg-light"> <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarTogglerDemo01" aria-controls="navbarTogglerDemo01" aria-expanded="false" aria-label="Toggle navigation"> <span className="navbar-toggler-icon"></span> </button> <div className="collapse navbar-collapse" id="navbarTogglerDemo01"> <a className="navbar-brand" href="#">Lead Manager</a> <ul className="navbar-nav ml-auto mt-2 mt-lg-0"> </ul> </div> </nav> ) } } export default Header ``` 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──src │ │ │ ├──components │ │ │ │ ├──layout │ │ │ │ │ └──Header.js │ │ │ │ │ │ │ │ │ └──App.js │ │ │ │ │ │ │ └──index.js │ │ │ ... ``` <br><br><br> 開啟 `./leadmanager/frontend/src/components/App.js` 引入剛剛新建的 Header.js: ```javascript= import React, { Component } from 'react'; import ReactDom from 'react-dom'; import Header from './layout/Header'; // new add export class App extends Component { render() { return ( <Header /> // modify ) } } ReactDom.render(<App />, document.getElementById('app')); ``` 開新的一個終端機(不要關掉 django server), Run webpack: *終端機目前位置 `~/django_rest_api_react/`* ```= npm run dev ``` 查看結果 ![](https://i.imgur.com/5FzpX1R.png) <br><br><br> 為了不要每次修改 JavaScript 或 React 都要執行 `npm run dev` , 在 `./package.json` 中的 `scripts` 裡加入 `--watch` : ```javascript= "scripts": { "dev": "webpack --mode development --watch ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js", "build": "webpack --mode production ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js" }, ``` 現在執行 `npm run dev` 後就會持續偵測你是否有改程式,當你存檔後會自動編譯 *(關閉按 `control + c`)* <br><br><br> 新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Form.js` 在裡面寫入: ```javascript= import React, { Component } from 'react' export class Form extends Component { render() { return ( <div> <h1>Add Leads Form</h1> </div> ) } } export default Form ``` <br><br><br> 新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Leads.js` 在裡面寫入: ```javascript= import React, { Component } from 'react' export class Leads extends Component { render() { return ( <div> <h1>Leads List</h1> </div> ) } } export default Leads ``` <br><br><br> 新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Dashboard.js` 在裡面寫入: ```javascript= import React, { Fragment } from 'react'; import Form from './Form'; import Leads from './Leads'; export default function Dashboard() { return ( <Fragment> <Form /> <Leads /> </Fragment> ) }; ``` 資料結構 ``` django_rest_api_react ├──leadmanager │ ├──frontend │ │ ├──migrations │ │ ├──src │ │ │ ├──components │ │ │ │ ├──layout │ │ │ │ ├──leads │ │ │ │ │ ├──Dashborad.js │ │ │ │ │ ├──Form.js │ │ │ │ │ └──Leads.js │ │ │ │ │ │ │ │ │ └──App.js │ │ │ │ │ │ │ └──index.js │ │ │ ... ``` <br><br><br> 開啟 `./leadmanager/frontend/src/components/App.js` 引入剛剛新建的 Dashboard.js: ```javascript= import React, { Component, Fragment } from 'react'; // modify import ReactDom from 'react-dom'; import Header from './layout/Header'; import Dashboard from './leads/Dashboard'; // new add export class App extends Component { render() { return ( <Fragment> // new add <Header /> // new add <div className="container"> <Dashboard /> </div> </Fragment> // new add ) } } ReactDom.render(<App />, document.getElementById('app')); ``` <br><br><br> reload page ![](https://i.imgur.com/pVWNhzi.png) <br><br><br> [[3]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rksvPD6nB) [[1]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/Syzvus93S)