Cara cepat menulis situs web atau aplikasi web dan tidak terjebak dalam kolektor

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

 #!/usr/bin/env python3 from myapp import app as application application.run(host='0.0.0.0', port=8000) 

File config / default.py untuk mengkonfigurasi aplikasi

 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 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) #  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 

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 %} 

Source: https://habr.com/ru/post/id440944/


All Articles