In diesem kleinen Tutorial wird beschrieben, wie Sie mit Server-Side Rendering (SSR) eine reaktive Webanwendung erstellen. Der Client-Teil ist eine vollwertige Vue-Anwendung, in meinem Fall unter Verwendung der MVVM-Vorlage. Die Serveranwendung wird auf dem Flask-Mikroframework ausgeführt, das Endpunkte bereitstellen und vorgefertigte HTML-Seiten rendern kann. HTML-Seiten (im Unterverzeichnis myapp / templates) werden von der Jinja-Template-Engine (als Flask-Abhängigkeit installiert) gerendert.
Hinweis: Schnell bedeutet nicht, dass der Artikel für Anfänger gedacht ist.
Verwendete Technologien und Frameworks:
Für die API verwenden wir das JSON-RPC-Protokoll
www.jsonrpc.org/specification . Das Protokoll ist einfach, leicht zu lesen und funktioniert ohne unnötige Krücken sowohl auf Server- als auch auf Clientseite.
Vorbereitung
Installieren Sie die erforderlichen Pakete
pip install flask flask-jsonrpc
Wir erstellen den Projektkatalog und bereiten die Struktur im Inneren vor. Die empfohlene Anwendungsstruktur finden Sie hier
https://habr.com/en/post/421887/ mkdir -p myapp/{myapp/{static/{js,css},ns_api,templates},config,data} cd myapp
Laden Sie die erforderlichen JS- und CSS-Framework-Dateien herunter
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
Hier besteht eine JQuery-Abhängigkeit, aber nur, damit Bootstrap funktioniert
Minimale Kolbenanwendung
Run.py-Datei zum manuellen Starten und Testen
Datei Config / default.py zum Konfigurieren der Anwendung
import os import sys
Datei Config / __ init__.py
CONFIG = 'config.default'
Datei 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)
Datei myapp / ns_api / __ init__.py
from flask_jsonrpc import JSONRPC from .. import app jsonrpc = JSONRPC(app, '/api') from . import logic
Datei myapp / views.py
from myapp import app from flask import render_template @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
Datei myapp / ns_api / logo.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)
Startrechte festlegen
chmod +x run.py
Client-Seite der Benutzeroberfläche (Front-End, Front-End)
Datei myapp / templates / header.html
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css" /> <script src="/static/js/vue.min.js"></script> <script src="/static/js/axios.min.js"></script> <title>{{ pagedata['title'] }}</title> </head>
Datei myapp / templates / skeleton.html
<!DOCTYPE html> <html lang="ru"> {% include 'header.html' %} <body> <section id="app"> <div class="container-fluid"> {% block content %} {% endblock %} </div> </section> {% block script %} <script type="text/javascript"> var app = new Vue({ el: '#app', data: { }, methods: { } }) </script> {% endblock %} </body> </html>
Datei myapp / templates / index.html
{% extends "skeleton.html" %} {% block content %} <h1> </h1> <a href="http://127.0.0.1:8000/api/browse">http://127.0.0.1:8000/api/browse</a> <h2>API</h2> <pre>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 </pre> <h3></h3> <ul> <li>logic.and(A, B)</li> <li>logic.not(A)</li> <li>logic.or(A, B)</li> <li>logic.xor(A, B)</li> </ul> <h3>API</h3> <div class="btn-group"> <div class="btn btn-outline-success" v-if="A" v-on:click="changeA"></div> <div class="btn btn-outline-danger" v-else v-on:click="changeA"></div> <div class="btn btn-outline-secondary disabled"></div> <div class="btn btn-outline-success" v-if="B" v-on:click="changeB"></div> <div class="btn btn-outline-danger" v-else v-on:click="changeB"></div> <div class="btn btn-outline-secondary disabled">=</div> <div class="btn btn-success disabled" v-if="result"></div> <div class="btn btn-danger disabled" v-else></div> </div> {% endblock %} {% block script %} <script type="text/javascript"> var app = new Vue({ el: '#app', data: {{ pagedata['data']|tojson|safe }}, methods: { changeA: function() { var vm = this; vm.A = !vm.A; vm.update(); }, changeB: function() { var vm = this; vm.B = !vm.B; vm.update(); }, update: function() { var vm = this; axios.post( '/api', { "jsonrpc": "2.0", "method": 'logic.and', "params": { "A": vm.A, "B": vm.B }, "id": 1 } ).then( function(response) { if ('result' in response.data) { vm.result = response.data['result']; } } ); } } }) </script> {% endblock %}