Я люблю Vue и Vite - какой замечательный опыт разработки. Но я люблю Python за его Tornado, SQLAlchemy и многое другое. Итак, вот верхушка айсберга - это демонстрирует взаимосвязь между вопиющим злоупотреблением инструментами и плохой инженерией - где линтинг, форматирование, тестирование, документация и все остальное? Если меня спросят, я могу набрать pylint, axblack, pytest, sphinx, но отдыхать не буду; рпкс для меня.
Для начала создадим Makefile - его шаги просты, если у вас нет Make:
- создать виртуальную среду под названием venv
- активировать и установить nodeenv и tornado
- создать виртуальную среду узла под названием nenv
Makefile
setup: if which python3 && [ ! -d venv ] ; then python3 -m venv venv ; fi source venv/bin/activate \ && python -m pip install -q -U pip \ && pip install nodeenv tornado \ && if [ ! -d nenv ] ; then nodeenv nenv; fi
Затем вызовите функцию настройки вашего Makefile:
make setup
Создайте пакет Python с именем chat
и добавьте к нему модули с именами main.py
и websocket.py
. В пакет добавьте каталог с именем static
, в котором будут размещаться наши статические веб-файлы.
В bash вы можете запустить:
mkdir chat touch chat/__init__.py touch chat/main.py touch chat/websocket.py mkdir chat/static
Main будет содержать нашу tornado
конфигурацию и websocket
определять наш обработчик веб-сокетов. Tornado создает экземпляры классов RequestHandler для обработки каждого запроса. Мы будем использовать два обработчика: Websocket и StaticFileHandler.
websocket.py
""" our websocket handler """ import logging from tornado.websocket import WebSocketHandler log = logging.getLogger(__name__) class Websocket(WebSocketHandler): """ a websocket handler that broadcasts to all clients """ clients = [] def check_origin(self, origin): """ in development allow ws from anywhere """ if self.settings.get('debug', False): return True return super().check_origin(origin) def open(self, *args, **kwargs): """ we connected """ log.info('WebSocket opened') self.clients.append(self) def on_close(self): """ we're done """ if self in self.clients: self.clients.remove(self) log.info('WebSocket closed') def on_message(self, message): """ we've said something, tell everyone """ for client in self.clients: client.write_message(message)
Здесь вы найдете документацию по торнадо для веб-сокетов.
Наша специализация добавляет свойство класса clients
, к которому каждый запрос добавляет и удаляет из себя. Это позволяет нам вести трансляцию при получении сообщения. Мы проверяем настройку отладки, чтобы узнать, разрешаем ли мы запросы из других источников. Мы собираемся получить доступ к этому веб-сокету с сервера Vite через порт 3000. Без метода check_origin
торнадо вернет ошибку 403.
main.py
""" our entry point """ import logging import tornado.ioloop from tornado.web import Application from tornado.options import define, options, parse_command_line from .websocket import Websocket log = logging.getLogger(__name__) define('debug', type=bool, default=False, help='auto reload') define('port', type=int, default=8080, help='port to listen on') def make_app(): """ make an application """ return Application( [ (r'/ws', Websocket), ( r'/(.*)', tornado.web.StaticFileHandler, {'path': 'chat/static', 'default_filename': 'index.html'}, ), ], debug=options.debug, ) def main(): """ parse command line, make and start """ tornado.options.parse_command_line() app = make_app() app.listen(options.port) log.info('listening on port: %s', options.port) if options.debug: log.warning('running in debug mode') tornado.ioloop.IOLoop.current().start() if __name__ == '__main__': main()
Tornado предоставляет парсер аргументов, и с его помощью мы определяем две опции: порт и отладка. Затем мы создаем приложение, определяя маршруты и настройки. Маршруты определяют путь, параметры обработчика и инициализации. У нашего Websocket нет опций, но StaticFileHandler должен знать путь к файлам и имя файла по умолчанию, если имя файла не присутствует в запросе. Используя нашу опцию отладки, мы можем активировать горячую перезагрузку торнадо, и мы сделаем это в командной строке, указав --debug=true
.
Tornado - это асинхронный фреймворк, поэтому мы запускаем его ioloop. Теперь он написан поверх пакета asyncio стандартной библиотеки с несколькими отличными дополнениями.
Итак, теперь у нас есть приложение торнадо, которое мы можем запустить из командной строки, активировав нашу виртуальную среду и вызвав наш основной модуль:
. venv/bin/activate
python -m chat.main --debug=true
Vite
Сначала нам нужно активировать наш nodeenv, а затем вызвать Vite для создания приложения.
. nenv/bin/activate
npm init @vitejs/app client
Вам будет предложено установить Vite и спросить, какое приложение вы хотите. Мы выберем обычный Vue. Затем он проинструктирует вас перейти в каталог клиента и запустить npm install
и npm run dev
. Сделай так. Теперь на порту 3000 вы должны найти свое приложение Vite Vue3. Добавим клиент Websocket:
websocket.js
import { reactive } from 'vue' export default { install: (app, options) => { const ws = new WebSocket("ws://localhost:8080/ws"); ws.transcript = reactive([]) ws.onmessage = function (evt) { ws.transcript.push(evt.data) }; app.config.globalProperties.$ws = ws } }
Мы создаем WebSocket обратно в наше приложение торнадо. У нас также есть атрибут стенограммы reactive
. Мы устанавливаем себя как плагин, предоставляя глобальный доступ к $ ws в любом компоненте vue. Каждое сообщение websocket будет добавлено в наш атрибут transcript. Хотя здесь это не видно, у Websocket есть метод send
, который принимает строковый параметр сообщения. Мы будем использовать это в методе HelloWorld.vue say
.
Затем в _17 _...
main.js
import { createApp } from 'vue' import App from './App.vue' import ws from './websocket.js' const app = createApp(App) app.use(ws) app.mount('#app')
Мы добавляем наш плагин в приложение с app.use(ws)
. В остальном это так, как было в шаблоне проекта.
Затем мы меняем components/HelloWorld.vue
, чтобы использовать наш плагин:
HelloWorld.vue
<template> <div class="page"> <div class="transcript"> <div class="line" v-for="line in $ws.transcript"> {{ line }} </div> </div> <form @submit.prevent="say"> <input v-model="name" placeholder="your name" size="10"> <input v-model="something" placeholder="say something" size="40"> <input type="submit" value="Say"> </form> </div> </template>
Наш шаблон представляет собой простую колонку с расшифровкой и записью. Наша стенограмма - это итерация реактивного атрибута $ ws.transcript. Наша запись - это ряд полей ввода для имени, чего-то и кнопки отправки. В нашей форме есть обработчик событий отправки, который предотвращает поведение по умолчанию - например, он не делает запрос и не перезагружает страницу.
<script> export default { data() { return { name: "anon", something: "" } }, methods: { say() { this.$ws.send(`${this.name} said: ${this.something}`) this.something = "" } } } </script>
Да, я знаю, что не использовал композицию api. Это все еще читается, и по мере того, как я привыкаю к api композиции, я перейду к ней. Это выходит из моих пальцев само по себе ... У нас есть два реактива и метод. Метод вызывается событием формы, мы форматируем строку и отправляем ее через веб-сокет. Затем мы сбрасываем ввод something.
<style scoped> .page { display: flex; flex-direction: column; } .form { display: flex; flex-direction: row; } .transcript { display: flex; flex-direction: column; } </style>
CSS предоставляет столбцы и строки с использованием flex
.
Кроме того, я заменил логотип изображением, чтобы мои ошибки или небрежность не ассоциировались с Vite, Vue или Tornado.
И вот результат:
Вы должны увидеть это на:
http://localhost:3000
Затем мы создадим наше приложение и получим к нему доступ через порт 8080. Во-первых, нам нужно отредактировать client/package.json
, чтобы сообщить Vite, куда поместить созданные файлы.
"build": "vite build --outDir=../chat/static/ --emptyOutDir",
Затем запустите команду сборки в новом терминале:
. nenv/bin/activate cd client npm run build
Теперь вы должны указать в браузере:
http://localhost:8080
Торнадо обслуживает и клиента, и веб-узел. Уточняйте размер клиента! Для меня это 59к! (размер статического каталога).
Если вы все еще используете Vite на порту 3000, используя две страницы браузера, вы можете общаться между Vite и Tornado, разработчиком и создателем!
Помещение в контейнер докеров при многоступенчатой сборке приведет к получению образа размером 48 МБ!
Это только начало. Отсюда мы находим контейнеры докеров, доступ к базе данных через SQLAlchemy, Redis для поддержки многопроцессного взаимодействия, удаленные вызовы процедур для предоставления представлений, кеширование для поиска и многое другое, что стало возможным благодаря простому запуску тестируемых функций ioloop.
Проект доступен по адресу: https://github.com/blueshed/chat
Следующая статья доступна по адресу: Python Realtime Chat Engineered.
Больше контента на plainenglish.io