Как быстро написать веб-сайт или веб-приложение и не увязнуть в сборщиках
Это маленькое руководство описывает создание реактивного веб-приложения используя отрисовку на стороне сервера (Server-Side Rendering, SSR). Клиентская часть являет собой полноценное Vue-приложение, в моём случае используя шаблон MVVM. Серверное приложение работает на микрофреймворке Flask, который может предоставить конечные точки подключения (endpoint) и отдать готовую HTML страницу. HTML страницы (расположены в подкаталоге myapp/templates) рендерятся шаблонизатором Jinja (устанавливается в качестве зависимости Flask).
Внимание: быстро ещё не значит, что статья предназначена для новичков.
Используемые технологии и фреймворки:
Для API используем протокол JSON-RPC www.jsonrpc.org/specification. Протокол отличается простотой, удобочитаемостью и без лишних костылей работает как на серверной, так и на клиентской стороне.
Подготовка
Установка необходимых пакетов
pip install flask flask-jsonrpc
Создаём каталог проекта и подготавливаем структуру внутри. С рекомендуемой структурой приложения можно ознакомиться здесь https://habr.com/ru/post/421887/
mkdir -p myapp/{myapp/{static/{js,css},ns_api,templates},config,data}
cd myapp
Скачиваем нужные файлы JS и CSS фреймворков
wget -O myapp/static/js/jquery-3.3.1.slim.min.js https://code.jquery.com/jquery-3.3.1.slim.min.js
wget -O myapp/static/js/popper.min.js https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js
wget -O myapp/static/js/bootstrap.min.js https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js
wget -O myapp/static/css/bootstrap.min.css https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css
wget -O myapp/static/js/vue.min.js https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js
wget -O myapp/static/js/axios.min.js https://unpkg.com/axios/dist/axios.min.js
Здесь есть зависимость jquery, но только для работы Bootstrap
Минимальное Flask приложение
Файл run.py для ручного старта и тестирования
#!/usr/bin/env python3
from myapp import app as application
application.run(host='0.0.0.0', port=8000)
Файл config/default.py для настройки приложения
import os
import sys
# Конфигурация
DEBUG = True
SQLDEBUG = False
SESSION_COOKIE_NAME = 'myapp'
SESSION_TYPE = 'filesystem'
TITLE = 'Проект'
DIR_BASE = '/'.join(os.path.dirname(os.path.abspath(__file__)).split('/')[:-1])
DIR_DATA = DIR_BASE + '/data'
# Генерировать можно утилитой pwgen
# Пример:
# pwgen -sy 64
SECRET_KEY = '''0123456789'''
# Логирование
LOG_FILE = DIR_DATA + '/myapp.log'
LONG_LOG_FORMAT = '%(asctime)s - [%(name)s.%(levelname)s] [%(threadName)s, %(module)s.%(funcName)s@%(lineno)d] %(message)s'
LOG_FILE_SIZE = 128 # Размер файла лога в МБ
Файл config/__init__.py
CONFIG = 'config.default'
Файл myapp/__init__.py
import config
import logging
from flask import Flask
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
app.config.from_object(config.CONFIG)
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
# Логирование
handler = RotatingFileHandler(app.config['LOG_FILE'],
maxBytes=app.config['LOG_FILE_SIZE']*1024*1024,
backupCount=1)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(app.config['LONG_LOG_FORMAT'])
handler.setFormatter(formatter)
app.logger.addHandler(handler)
# API
from . import ns_api
from . import views
Файл myapp/ns_api/__init__.py
from flask_jsonrpc import JSONRPC
from .. import app
jsonrpc = JSONRPC(app, '/api')
from . import logic
Файл myapp/views.py
from myapp import app
from flask import render_template
from . import forms, models
@app.route('/')
def index():
pagedata = {}
pagedata['title'] = app.config['TITLE']
pagedata['data'] = {
"A": True,
"B": False,
"result": False
}
body = render_template('index.html', pagedata=pagedata)
return body
Файл myapp/ns_api/logic.py
import operator
from . import jsonrpc
@jsonrpc.method('logic.and(A=bool, B=bool)')
def logic_and(A, B):
"""
Логическое И
"""
return operator.and_(A, B)
@jsonrpc.method('logic.not(A=bool)')
def logic_not(A):
"""
Логическое НЕ
"""
return operator.not_(A)
@jsonrpc.method('logic.or(A=bool, B=bool)')
def logic_or(A, B):
"""
Логическое ИЛИ
"""
return operator.or_(A, B)
@jsonrpc.method('logic.xor(A=bool, B=bool)')
def logic_xor(A, B):
"""
Логическое ИСКЛЮЧАЮЩЕЕ ИЛИ
"""
return operator.xor(A, B)
Устанавливаем права на запуск
chmod +x run.py
Клиентская сторона пользовательского интерфейса (фронтенд, front-end)
Файл myapp/templates/header.html
{{ pagedata['title'] }}
Файл myapp/templates/skeleton.html
{% include 'header.html' %}
{% block content %}
{% endblock %}
{% block script %}
{% endblock %}
Файл myapp/templates/index.html
{% extends "skeleton.html" %}
{% block content %}
Микросервисная архитектура
http://127.0.0.1:8000/api/browse
API
curl -i -X POST \
-H "Content-Type: application/json; indent=4" \
-d '{
"jsonrpc": "2.0",
"method": "logic.and",
"params": {
"A": true,
"B": true
},
"id": "1"
}' http://127.0.0.1:8000/api
Логические
- logic.and (A, B)
- logic.not (A)
- logic.or (A, B)
- logic.xor (A, B)
API
Истина
Ложь
И
Истина
Ложь
=
Истина
Ложь
{% endblock %}
{% block script %}
{% endblock %}