Tutorial kecil ini menjelaskan cara membuat aplikasi web reaktif menggunakan Server-Side Rendering (SSR). Bagian klien adalah aplikasi Vue lengkap, dalam kasus saya menggunakan template MVVM. Aplikasi server berjalan pada mikroframework Flask, yang dapat memberikan titik akhir dan membuat halaman HTML yang sudah jadi. Halaman HTML (terletak di subdirektori myapp / templates) diberikan oleh mesin template Jinja (diinstal sebagai ketergantungan Flask).
Catatan: cepat tidak berarti bahwa artikel tersebut ditujukan untuk pemula.
Teknologi dan kerangka kerja yang digunakan:
Untuk API, kami menggunakan protokol JSON-RPC
www.jsonrpc.org/specification . Protokolnya sederhana, mudah dibaca, dan tanpa kruk yang tidak perlu, ia berfungsi di sisi server dan klien.
Persiapan
Instal paket yang diperlukan
pip install flask flask-jsonrpc
Kami membuat katalog proyek dan menyiapkan struktur di dalamnya. Struktur aplikasi yang direkomendasikan dapat ditemukan di sini
https://habr.com/en/post/421887/ mkdir -p myapp/{myapp/{static/{js,css},ns_api,templates},config,data} cd myapp
Unduh file kerangka kerja JS dan CSS yang diperlukan
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
Ada ketergantungan jquery di sini, tetapi hanya agar Bootstrap berfungsi
Aplikasi Labu Minimal
File run.py untuk memulai dan menguji secara manual
File config / default.py untuk mengkonfigurasi aplikasi
import os import sys
Config / __ init__.py file
CONFIG = 'config.default'
File 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)
File myapp / ns_api / __ init__.py
from flask_jsonrpc import JSONRPC from .. import app jsonrpc = JSONRPC(app, '/api') from . import logic
File 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
File 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)
Tetapkan hak peluncuran
chmod +x run.py
Sisi klien dari antarmuka pengguna (front-end, front-end)
File 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>
File 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>
File 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 %}