說實話此次的項目對個人提高並不大,不管是前端、後端我過去都有所經驗,對於團隊協做、版本控制、自動化CI/CD也有所瞭解。因此本篇博客我不會將主要的分析放在先後端的實現上,更多的着眼於部署等方面。javascript
附項目體驗地址。php
咱們完成的是典型的先後端分離項目,首先要作的就是落實先後端交互格式,咱們採用了JSON
這種輕量的數據交換格式,具體格式約定以下:前端
{ "status": "success", "data": interface{} }
{ "status": "error", "err_msg": "error message" }
接下來咱們要作的就是先後端的分別實現,前端的實現咱們放到代碼複用部分講,這裏咱們看看後端。後端咱們選擇Python
做爲主要的開發工具,咱們先來看APP的實例獲取過程:java
from flask import Flask from flask_cors import CORS from app.config import FlaskConfig from app.controllers import register_routers from app.models import connect_db def new_flask_app() -> Flask: app = Flask(__name__) CORS(app, supports_credentials=True) # 添加配置文件 app.config.from_object(FlaskConfig) # 註冊路由 register_routers(app) # 連接數據庫 connect_db(app) return app
代碼中的註釋都比較詳細,不想講不少。你們注意一下Python中的類型註解這一個特性這樣能夠得到很是好的代碼提示等IDE支持。python
接下來,咱們分別看一個Model和Controller的例子:react
from app.models import db from app.models import session_commit class User(db.Model): id = db.Column(db.Integer, autoincrement=True, primary_key=True) username = db.Column(db.String, nullable=True) password = db.Column(db.String, nullable=True) def __init__(self, username, password): self.username = username self.password = password def __str__(self): return "User(username={})".format(self.username) @classmethod def check_password(cls, username: str): return cls.query.filter_by(username=username).first() @classmethod def change_password(cls, username: str, password: str): user = cls.query.filter_by(username=username).first() user.password = password return session_commit() def new_user(self): db.session.add(self) return session_commit()
注意其中類方法的使用便可,其他的就是簡單的Python的ORM的使用nginx
from flask import Blueprint, request, session from app.models.user import User import app.utils.return_warp as warp login_out_page = Blueprint('login_out', __name__, url_prefix='/log') @login_out_page.route('/in', methods=['POST']) def login(): username = request.form.get('username') password = request.form.get('password') # 校驗必須參數 if username is None or password is None: return warp.fail_warp('params error') user = User.check_password(username=username) if user.password == password: session.clear() session['user'] = username return warp.success_warp('login success') else: return warp.fail_warp('user error') @login_out_page.route('/out', methods=['GET']) def logout(): session.clear() return warp.success_warp('logout success')
其他的內容便再也不贅述,具體的代碼實現也沒有什麼難懂的地方,也沒什麼講解的必要。docker
本次項目咱們主要複用的是前端部分的代碼,並在其基礎上進行了優化。數據庫
能夠說本次結對編程,個人隊友負責前端部分,由於複用的是我我的項目時的前端代碼,因此咱們採用的是React
這樣的技術。個人隊友對於如今前端工程化的方法有了很大的理解,對於前端工做流的使用也初步進行了入門,對於流行的MVVM模型有了深刻的理解。編程
接下來舉一個React的Context的例子說明是怎麼進行代碼複用的:
// 我的項目代碼 import React, {createContext, useState} from 'react'; export const TypeContext = createContext(null); export const TypeProvider = props => { let [userType, setUserType] = useState({ name: '張三1', type: 1 }); return ( <TypeContext.Provider value={{userType, setUserType}}> {props.children} </TypeContext.Provider> ) }; export const TypeConsumer = TypeContext.Consumer; //結對項目 import React, {createContext, useState} from 'react'; export const UserContext = createContext(null); export const TypeProvider = props => { const [user, setUser] = useState(''); const [errorMessage, setErrorMessage] = useState(''); return ( <UserContext.Provider value={{user, setUser, errorMessage, setErrorMessage}}> {props.children} </UserContext.Provider> ) }; export const TypeConsumer = UserContext.Consumer;
能夠看到二者的代碼基本上是一致的,只是咱們所須要共享的數據不太同樣,因此對外提供了不一樣的Provide。
有興趣的能夠了解一下React Hooks的原理和使用
Docker是一個開放源代碼軟件項目,讓應用程序部署在軟件貨櫃下的工做能夠自動化進行,藉此在Linux操做系統上,提供一個額外的軟件抽象層,以及操做系統層虛擬化的自動管理機制。 Docker利用Linux核心中的資源分離機制,例如cgroups,以及Linux核心名字空間,來建立獨立的容器。
咱們的後端項目即採用Docker進行部署,具體的命令等你們能夠查看Docker官網,下面給出Dockerfile:
# 基礎鏡像 FROM python:3.7 WORKDIR /app ADD . /app RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple CMD ["gunicorn", "main:app", "-c", "gunicorn.conf.py"]
Nginx是異步框架的網頁服務器,也能夠用做反向代理、負載平衡器和HTTP緩存。該軟件由伊戈爾·賽索耶夫建立並於2004年首次公開發布。 2011年成立同名公司以提供支持。2019年3月11日,Nginx公司被F5 Networks以6.7億美圓收購。 Nginx是免費的開源軟件,根據類BSD許可證的條款發佈。
咱們的服務器只在44三、80兩個端口運行,其他的部署經過Nginx反代進行:
#PROXY-START/pair location /pair { expires 12h; if ($request_uri ~* "(php|jsp|cgi|asp|aspx)") { expires 0; } proxy_pass http://內網IP:6000/; proxy_set_header Host localhost; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; add_header X-Cache $upstream_cache_status; add_header Cache-Control no-store; proxy_cache cache_one; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 301 302 1m; } #PROXY-END/pair
注意其中由於使用了Docker不能夠使用localhost,必須使用內網IP,記得做爲API服務器應該設置不開啓瀏覽器緩存。