你还在使用MVC吗?~开始使用Django x React开发SPA~
https://note.com/uichiyy/n/nadc6f56b01de
我们根据最新版本进行了重新制作,还添加了新的说明,使得学习更加方便!
最近,使用JS框架的网站越来越多。
尤其是React和Vue等JS框架经常被用于单页应用程序(SPA)的开发,不仅能为用户提供更好的体验,还能给开发人员带来许多优势。
目标读者
-
- Web開発経験者
-
- APIを使ったWebアプリケーションを開発したことがある人
-
- JavaScriptをそこそこ知っててPythonもそこそこ知ってる人
-
- Djangoをちょっと知っている
- MVCもしくはMTVを使った開発をしたことがある人
由于还有更详细的相关文章,所以如果对本文感到困惑或想深入学习的人,请查看这里。
还在使用MVC吗? 〜通过 React 和 Django 开始现代 Web 开发〜
本文中将使用React作为前端,Django作为后端来进行教程的进展。教程将以ToDo应用为例进行讲解。
SPA是指療養地。
SPA被称为单页面应用程序,是一种有效的提升用户体验的方法。此外,它融合了数据绑定、虚拟DOM和组件的三个特点。
数据绑定
使用原生JavaScript来修改值的话,每次都需要运行指定DOM来修改值的处理函数。
但是,使用JS框架之后,只要定义的变量被更新,界面上的值也会随之改变。
虚拟DOM
JS框架有两种类型的DOM,一种是用于在客户端浏览器中进行渲染的DOM,另一种是存在于服务器和DOM之间的虚拟DOM。
虚拟DOM的作用是获取从服务器上抓取的新的虚拟DOM与当前存在的虚拟DOM之间的差异,并将这些差异反映到DOM中。
因此,DOM的更新只会针对有差异的部分,可以加快页面的渲染速度。
成分
在JS框架中,可以将页面元素分解为称为组件的部件单元。这样一来,就可以重复使用组件,而无需编写相同的代码。
在这个教程中,由于只创建了一页网页,可能无法亲身感受到与用户体验相关的好处,但我认为可以感受到在开发方面的好处。
搭建Django环境
首先,我们将从后端开始进行。
请按照以下顺序执行这些命令。
mkdir todo-backend
cd todo-backend
python3 -m venv env
source env/bin/activate
pip install django djangorestframework django-cors-headers
django-admin startproject project .
django-admin startapp todo
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
请在环境构建好后访问127.0.0.1:8000。
应该会显示初始页面。
配置Django环境。
我们将在settings.py文件中添加插件和跨域设置。跨域设置是为了防止在浏览器中访问API时出现访问被拒绝的情况。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'todo'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
]
# 許可するオリジン
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000',
]
顺便在项目目录下的URL配置文件中设置API的路由。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('todo.urls')),
]
后端实现
我将在待办事项应用程序中进行实现。
from django.db import models
class Todo(models.Model):
name = models.CharField(max_length=64, blank=False, null=False)
checked = models.BooleanField(default=False)
def __str__(self):
return self.name
进行迁移。
python manage.py makemigrations
python manage.py migrate
from django.contrib import admin
from .models import Todo
@admin.register(Todo)
class Todo(admin.ModelAdmin):
pass
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('id', 'name', 'checked')
from rest_framework import filters, generics, viewsets
from .models import Todo
from .serializer import TodoSerializer
class ToDoViewSet(viewsets.ModelViewSet):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
filter_fields = ('name',)
from rest_framework import routers
from .views import ToDoViewSet
from django.urls import path, include
router = routers.DefaultRouter()
router.register(r'todo', ToDoViewSet)
urlpatterns = [
path('', include(router.urls)),
]
完成这些步骤后,请访问http://localhost:8000/admin,并添加一些ToDo事项。
搭建React环境
在React的环境搭建中,使用Create React App来完成。
yarn create react-app todo-frontend
cd todo-frontend
yarn start
当您访问 http://localhost:3000 并成功显示页面时,环境已经成功配置完成。
路由
由于React没有路由功能,因此需要安装一个额外的插件。
yarn add react-router-dom
请在src目录下创建一个Router.jsx文件。
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Top from '../components/Top';
const Router = () => {
return (
<BrowserRouter>
</BrowserRouter>
);
};
export default Router;
将路由加载到App.js中。
import React from 'react';
import Router from './configs/Router';
function App() {
return (
<Router />
);
}
export default App;
界面设计
请使用Material UI设计框架来设计界面。它是作为React插件提供的,所以请使用yarn add进行安装。
yarn add @material-ui/core
这是下面图片的成品图。

实现API
首先,我們將開始實現API。
由於把它放在單個組件中會降低可讀性,所以我們將把API處理分開到不同的文件中實現。
要實現的API處理包括獲取待辦事項清單、創建待辦事項、勾選待辦事項和刪除待辦事項這四個部分。
请创建一个名为src/common/api的文件夹,并在其中创建一个名为todo.js的文件。
const originUrl = 'http://127.0.0.1:8000';
const getTodoList = (() => {
const url = new URL('/api/todo/', originUrl);
return new Promise( (resolve, reject) => {
fetch(url.href)
.then( res => res.json() )
.then( json => resolve(json) )
.catch( () => reject([]) );
});
});
export default getTodoList;
export const postCreateTodo = (name) => {
const url = new URL('/api/todo/', originUrl);
return new Promise( resolve => {
fetch(url.href, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: name
})
})
.then( res => res.json() )
.then( data => resolve(data) );
});
};
export const patchCheckTodo = ((id, check) => {
const url = new URL(`/api/todo/${id}/`, originUrl);
fetch(url.href, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
checked: check
})
});
});
export const deleteTodo = ((id) => {
const url = new URL(`/api/todo/${id}/`, originUrl);
fetch(url.href, { method: 'DELETE' });
});
组件的实现 de
接下来,我们将实现组件。
import React, { useEffect, useState } from 'react';
import Button from '@material-ui/core/Button';
import Box from '@material-ui/core/Box';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Container from '@material-ui/core/Container';
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import getToDoList, { postCreateTodo, patchCheckTodo, deleteTodo } from '../../common/api/todo';
const useStyles = makeStyles(theme => ({
todoTextField: {
marginRight: theme.spacing(1)
}
}));
const Top = () => {
const classes = useStyles();
const [todoList, setTodoList] = useState([]);
const [todo, setTodo] = useState('');
useEffect(() => {
(async () => {
const list = await getToDoList();
setTodoList(list);
})();
}, []);
const handleCreate = async () => {
if ( todo === '' || todoList.some( value => todo === value.name ) ) return;
const createTodoResponse = await postCreateTodo(todo);
setTodoList(todoList.concat(createTodoResponse));
};
const handleSetTodo = (e) => {
setTodo(e.target.value);
};
const handleCheck = (e) => {
const todoId = e.target.value;
const checked = e.target.checked;
const list = todoList.map( (value, index) => {
if (value.id.toString() === todoId) {
todoList[index].checked = checked;
}
return todoList[index];
});
setTodoList(list)
patchCheckTodo(todoId, checked);
}
const handleDelete = (e) => {
const todoId = e.currentTarget.dataset.id;
const list = todoList.filter( value => value['id'].toString() !== todoId);
setTodoList(list);
deleteTodo(todoId);
};
return (
<Container maxWidth="xs">
<Box display="flex" justifyContent="space-between" mt={4} mb={4}>
<TextField className={classes.todoTextField} label="やること" variant="outlined" size="small" onChange={handleSetTodo} />
<Button variant="contained" color="primary" onClick={handleCreate}>作成</Button>
</Box>
<FormGroup>
{todoList.map((todo, index) => {
return (
<Box key={index} display="flex" justifyContent="space-between" mb={1}>
<FormControlLabel
control={
<Checkbox
checked={todo.checked}
onChange={handleCheck}
value={todo.id}
color="primary"
/>
}
label={todo.name}
/>
<Button variant="contained" color="secondary" data-id={todo.id} onClick={handleDelete}>削除</Button>
</Box>
)
})}
</FormGroup>
</Container>
)
};
export default Top;
最后
我已经制作了一个ToDo应用程序,但仅凭本文的内容仍无法实际使用,因此我打算介绍一下后续将要托管的部分。
如果有错别字或错误,请与我们联系。
由于我们将源代码上传到GitHub上,请随意使用如有需要。
前端 (Front-end)
https://github.com/uichi/todo-frontend
后端
https://github.com/uichi/todo-backend
请提供以下内容的中文本地语言解释:”参考”
React 公式:用于构建用户界面的 JavaScript 库
Material UI 公式:一个在 React 基础上构建的开源 UI 组件库
Django 公式:一个高级的 Python Web 框架,用于快速开发安全可靠的网站
Django REST framework 公式:专为 Django 开发的强大且灵活的 Web API 框架