Я очень привык к Django, питаю симпатию к Flask, время от времени играю с aiohttp. Короче говоря - я люблю использовать python для бэкенда.

И как всякий современный веб-разработчик, я увлеченно наблюдаю за той вакханалией, что творится в мире javascript. Конечно же, я тоже хочу в ней принять участие. Это желание стало особо острым после того, как пришлось по работе создать несколько проектов на Laravel (да простят меня питонщики), с интегрированным из коробки Vue.js. Ощущения были непередаваемые.

Но статей "Как перестать рефлексировать и наконец начать использовать современный javascript вместе с Flask" я нашел немного. О чем это я - да их почти нет! В итоге, пришлось протаптывать дорожку самому. И вот, когда поставленная цель была достигнута и был написан полноценный работающий проект, я решил написать эту статью, чтобы поделится своим опытом. Я покажу как организовал разработку Flask + Webpack + Vue.js на деле.

Предполагается, что читатель достаточно хорошо владеет разработкой под python и сможет настроить себе виртуальное окружение. Также, я уверен, что у вас уже установлен npm, а если нет, то можно ознакомиться со статьей.

Работать будем в 4 этапа:

  • Создадим простое Flask приложение.

  • Установим vue-cli и сгенерируем основу для vue приложения.

  • Настроим webpack для удобной работы с flask.

  • Соединим фронт с бэком с помощью ajax запросов.

Пишем бэкенд

Ставим себе Flask:

pip install Flask

создаем файл app.py со следующим кодом:

from flask import Flask, jsonify, send_from_directory

app = Flask(__name__, static_folder='static/dist')


@app.route('/')
def index():
    # тут просто пробрасываем файлик, без всякого препроцессинга
    return app.send_static_file("index.html")

@app.route('/dist/<path:path>')
def static_dist(path):
    # тут пробрасываем статику
    return send_from_directory("static/dist", path)


@app.route('/api/languages')
def languages():
    # а это простой метод, который будет возвращать список языков программирования
    # который мы потом с помощью vue.js будем отображать
    return jsonify({
        'languages': [
            'assembly',
            'c#',
            'c',
            'c++',
            'go',
            'java',
            'javascript',
            'object c',
            'pascal',
            'perl',
            'php',
            'python',
            'R',
            'ruby',
            'SQL',
            'swift',
            'visual basic',
        ]
    })

if __name__ == '__main__':
    app.run()

Можно проверить работоспособность сервера, запустив его:

python app.py

и убедившись, что 127.0.0.1:5000/api/languages возвращает список языков в виде json объекта. Пока нет смысла обращаться к 127.0.0.1:5000/, так как этот url предполагает наличие файла index.html в папке static. А у нас пока такого файла нет.

Интегрируем vue.js в проект

Создаем папку static и переходим в нее:

mkdir static
cd static

Далее, устанавливаем vue-cli, желательно глобально (подробнее можно почитать тут):

npm install -g vue-cli

теперь инициализируем проект (я использую webpack-simple - шаблон инициализаци vue.js, потому что его очень удобно кастомизировать, в отличие от шаблона webpack, который заточен под nodejs разработку):

vue init webpack-simple .

отвечаем на вопросы, например, так:

Alt Text

и запускаем установку пакетов:

npm install

Ждем установки всех зависимостей. Когда все установится, открываем файл package.json и смотрим содержимое. Нас интересует группа scripts, которая определяет пользовательские команды, вызываемые через:

npm run action_name

По умолчанию нам предлагается две команды:

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
}
  • dev - запускает встроенный nodejs сервер для разработки с отслеживанием изменений в коде и автоматической перезагрузкой кода.

  • build - предназначена для сборки приложения для продакшена, то есть все, что можно будет минимизировать - будет минимизиованно, а вместо версии vue (для разработчиков), будет использованна "ужатая" версия.

Запустим сервер для разработки фронта:

npm run dev

Так как стоит флаг --open, то должен будет открыться браузер по адресу 127.0.0.1:8080/

Vue main page

А теперь в отдельном терминале запустим наш бэк:

python app.py

Попробуем открыть список языков 127.0.0.1:8080/api/languages/. Как и ожидалось, никакого списка языков нам не пришло. Нас все время выбрасывает на страницу с Vue приветствием. Как вы наверное догадались это потому что бэк у нас на одном порту, а сервер для фронта на другом. Чтобы решить эту проблему мы настроим прокси у фронт-сервера.

Открываем файлик webpack.config.js, находим строчки

devServer: {
  historyApiFallback: true,
  noInfo: true,
  overlay: true,
},

и добавляем настройки прокси сервера, чтобы все запросы начинающиеся на api обрабатывались flask-сервером, которые у нас на 5000 порту:

devServer: {
  historyApiFallback: true,
  noInfo: true,
  overlay: true,
  proxy: {
    "/api": "http://localhost:5000"
  }
},

Теперь перезапустим сервер для фронт-разработки:

npm run dev

Если открыть http://localhost:8080/ то окажемся на странице vue приветствия, а если открыть http://localhost:8080/api/languages то увидем список языков в json формате.

Красота! Теперь можно убрать страницу по умолчанию и что-нибудь стянуть с сервера.

Соединяем фронт с бэком.

В vue из коробки не встроен пакет для работы с ajax запросами. Поэтому, нужно установить какой-нибудь (я предпочитаю axios):

npm install -S axios

Флаг -S я добавил, чтобы модуль зафиксировался в качестве зависимости проекта в package.json.

Теперь нам нужно очистить файл static/src/App.vue от лишнего кода:

<template>
  <div id="app">

  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
    }
  }
}
</script>

<style>

</style>

добавить переменную для хранения списка языков:

...
data () {
    return {
        languages: []
    }
}
...

и отредактировать шаблон:

<template>
    <div id="app">
        <ul>
            <li v-for="l in languages">{{ l }}</li>
        </ul>
    </div>
</template>

Если перезагрузить страницу, то мы ничего не увидим. Это потому, что список языков пустой. Воспользуемся библиотекой axios, чтобы получить список языков:

<script>
    import axios from 'axios'

    export default {
        name: 'app',
        data() {
            return {
                    languages: []
            }
        },
        created() {
            axios.get("/api/languages").then(r => {
                   this.languages = r.data.languages
            })
        }
    }
</script>

Так как мы используем фронт-сервер, он автоматически обновит код по мере его изменения, и сразу запросит список языков с сервера. Получим такую картинку:

Подведем итоги

Интегрировать vue с любым приложением, будь то flask, django или любой другой фреймворк, очень просто. Главный секрет заключается в настройки прокси для фронт-сервера и разработке при параллельной работе двух команд:

python app.py

и:

npm run dev

Одна отслеживает бэк, другая - фронт. Вот так все просто.