HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>В этой статье мы настроим базовое CRUD-приложении, используя<strong>Vue</strong>и<strong>Flask</strong>. Сначала создадим новое Vue-приложение с помощью Vue CLI, а потом приступим к выполнению CRUD-операций посредством RESTful API на бэкенде под управлением Flask и Python.</p>
1 <p>В этой статье мы настроим базовое CRUD-приложении, используя<strong>Vue</strong>и<strong>Flask</strong>. Сначала создадим новое Vue-приложение с помощью Vue CLI, а потом приступим к выполнению CRUD-операций посредством RESTful API на бэкенде под управлением Flask и Python.</p>
2 <p>Ожидаемый результат:</p>
2 <p>Ожидаемый результат:</p>
3 <p>Что нам потребуется: • Vue v2.5.2; • Vue CLI v2.9.3; • Python v3.6.5; • npm v6.1.0; • Node v10.3.0; • Flask v1.0.2.</p>
3 <p>Что нам потребуется: • Vue v2.5.2; • Vue CLI v2.9.3; • Python v3.6.5; • npm v6.1.0; • Node v10.3.0; • Flask v1.0.2.</p>
4 <h2>Настраиваем Flask</h2>
4 <h2>Настраиваем Flask</h2>
5 <p>Начнём работу с создания новой директории нашего проекта:</p>
5 <p>Начнём работу с создания новой директории нашего проекта:</p>
6 $ mkdir flask-vue-crud $ cd flask-vue-crud<p>Во-первых, в директории flask-vue-crud нужно создать новый каталог и назвать его server. Далее следует создать и активировать виртуальную среду разработки:</p>
6 $ mkdir flask-vue-crud $ cd flask-vue-crud<p>Во-первых, в директории flask-vue-crud нужно создать новый каталог и назвать его server. Далее следует создать и активировать виртуальную среду разработки:</p>
7 $ python3.6 -m venv env $ source env/bin/activate<p>Обратите внимание, что команды могут быть другими, если у вас другая среда разработки.</p>
7 $ python3.6 -m venv env $ source env/bin/activate<p>Обратите внимание, что команды могут быть другими, если у вас другая среда разработки.</p>
8 <p>Теперь установим Flask и Flask-CORS:</p>
8 <p>Теперь установим Flask и Flask-CORS:</p>
9 (env)$ pip install Flask==1.0.2 Flask-Cors==3.0.4<p>Добавляем файл app.py в каталог, который вы только что создали:</p>
9 (env)$ pip install Flask==1.0.2 Flask-Cors==3.0.4<p>Добавляем файл app.py в каталог, который вы только что создали:</p>
10 from flask import Flask, jsonify from flask_cors import CORS # configuration DEBUG = True # instantiate the app app = Flask(__name__) app.config.from_object(__name__) # enable CORS CORS(app) # sanity check route @app.route('/ping', methods=['GET']) def ping_pong(): return jsonify('pong!') if __name__ == '__main__': app.run()<p>Flask-CORS потребуется для отправки cross-origin-запросов, следовательно, надо включить общий доступ к ресурсам (CORS).</p>
10 from flask import Flask, jsonify from flask_cors import CORS # configuration DEBUG = True # instantiate the app app = Flask(__name__) app.config.from_object(__name__) # enable CORS CORS(app) # sanity check route @app.route('/ping', methods=['GET']) def ping_pong(): return jsonify('pong!') if __name__ == '__main__': app.run()<p>Flask-CORS потребуется для отправки cross-origin-запросов, следовательно, надо включить общий доступ к ресурсам (CORS).</p>
11 <p>Запускаем наше приложение:</p>
11 <p>Запускаем наше приложение:</p>
12 <p>Чтобы выполнить проверку, вводим в строку браузера http://localhost:5000/ping. Ожидаемый результат - "Pong!".</p>
12 <p>Чтобы выполнить проверку, вводим в строку браузера http://localhost:5000/ping. Ожидаемый результат - "Pong!".</p>
13 <p>Чтобы завершить работу сервера, нажимаем Ctrl + C. После этого можно перейти к фронтенду и настройкам Vue.</p>
13 <p>Чтобы завершить работу сервера, нажимаем Ctrl + C. После этого можно перейти к фронтенду и настройкам Vue.</p>
14 <h2>Настраиваем Vue</h2>
14 <h2>Настраиваем Vue</h2>
15 <p>Чтобы создать индивидуальный темплейт проекта, мы будем задействовать мощный интерфейс Vue CLI. Устанавливаем его глобально:</p>
15 <p>Чтобы создать индивидуальный темплейт проекта, мы будем задействовать мощный интерфейс Vue CLI. Устанавливаем его глобально:</p>
16 $ npm install -g vue-cli@2.9.3<p>Теперь в каталоге flask-vue-crud выполняем команду, которая нужна для инициализации нового проекта Vue с конфигурацией webpack и под именем client :</p>
16 $ npm install -g vue-cli@2.9.3<p>Теперь в каталоге flask-vue-crud выполняем команду, которая нужна для инициализации нового проекта Vue с конфигурацией webpack и под именем client :</p>
17 $ vue init webpack client<p>Что касается<strong>webpack</strong>, то это инструмент для сборки и пакетный модуль, применяемый для создания, минимизации и объединения JS-файлов и прочих клиентских ресурсов.</p>
17 $ vue init webpack client<p>Что касается<strong>webpack</strong>, то это инструмент для сборки и пакетный модуль, применяемый для создания, минимизации и объединения JS-файлов и прочих клиентских ресурсов.</p>
18 <p>При создании нового Vue-проекта вам потребуется ответить на несколько вопросов: 1. Установить vue-router? - Да. 2. Использовать для линтинга кода ESLint? - Да. 3. Выберите пресет ESLint - Airbnb. 4. Настраивать юнит-тесты? - Нет. 5. Настраивать тесты e2e с Nightwatch? - Нет. 6. Запускать установку npm после создания проекта? - Да, применять NPM.</p>
18 <p>При создании нового Vue-проекта вам потребуется ответить на несколько вопросов: 1. Установить vue-router? - Да. 2. Использовать для линтинга кода ESLint? - Да. 3. Выберите пресет ESLint - Airbnb. 4. Настраивать юнит-тесты? - Нет. 5. Настраивать тесты e2e с Nightwatch? - Нет. 6. Запускать установку npm после создания проекта? - Да, применять NPM.</p>
19 <p>Vue-сборка в нашем случае - Runtime + Compiler.</p>
19 <p>Vue-сборка в нашем случае - Runtime + Compiler.</p>
20 Vue CLI v3.7.0 ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, Linter ? Use history mode for router? Yes ? Pick a linter / formatter config: Airbnb ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? (y/N) No<p>Если посмотреть на сгенерированную структуру проекта, то может показаться, что она очень большая. Однако по факту вам придётся иметь дело лишь с папками и файлами в каталоге /src вместе с файлом index.html.</p>
20 Vue CLI v3.7.0 ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, Linter ? Use history mode for router? Yes ? Pick a linter / formatter config: Airbnb ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? (y/N) No<p>Если посмотреть на сгенерированную структуру проекта, то может показаться, что она очень большая. Однако по факту вам придётся иметь дело лишь с папками и файлами в каталоге /src вместе с файлом index.html.</p>
21 <p>Что касается файла index.html, то он является отправной точкой нашего Vue-приложения.</p>
21 <p>Что касается файла index.html, то он является отправной точкой нашего Vue-приложения.</p>
22 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;meta name="viewport" content="width=device-width,initial-scale=1.0"&gt; &lt;title&gt;client&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="app"&gt;&lt;/div&gt; &lt;!-- встроенные файлы вводятся автоматически --&gt; &lt;/body&gt; &lt;/html&gt;<p>Кроме того, у нас есть элемент</p>
22 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;meta name="viewport" content="width=device-width,initial-scale=1.0"&gt; &lt;title&gt;client&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="app"&gt;&lt;/div&gt; &lt;!-- встроенные файлы вводятся автоматически --&gt; &lt;/body&gt; &lt;/html&gt;<p>Кроме того, у нас есть элемент</p>
23 <p>с идентификатором app. Это не что иное, как контейнер, который Vue станет применять для присоединения сгенерированных CSS и HTML во время создания пользовательского интерфейса.</p>
23 <p>с идентификатором app. Это не что иное, как контейнер, который Vue станет применять для присоединения сгенерированных CSS и HTML во время создания пользовательского интерфейса.</p>
24 <p>Давайте посмотрим, на каталоги и файлы внутри src:</p>
24 <p>Давайте посмотрим, на каталоги и файлы внутри src:</p>
25 ├── App.vue ├── assets │ └──── logo.png ├── components │ └──── HelloWorld.vue ├── main.js ├── router └─── index.js<p>Что тут что: -<strong>main.js</strong>- точка входа в наше приложение, осуществляет загрузку и инициализацию Vue совместно с корневым компонентом; -<strong>App.vue</strong>- корневой компонент, из него рендерятся прочие компоненты (отправная точка); -<strong>assets</strong>- место для хранения статических ассетов типа шрифтов и изображений; -<strong>components</strong>- место для хранения UI-компонентов; -<strong>router</strong>- место для определения URL-адресов и сопоставления их с компонентами.</p>
25 ├── App.vue ├── assets │ └──── logo.png ├── components │ └──── HelloWorld.vue ├── main.js ├── router └─── index.js<p>Что тут что: -<strong>main.js</strong>- точка входа в наше приложение, осуществляет загрузку и инициализацию Vue совместно с корневым компонентом; -<strong>App.vue</strong>- корневой компонент, из него рендерятся прочие компоненты (отправная точка); -<strong>assets</strong>- место для хранения статических ассетов типа шрифтов и изображений; -<strong>components</strong>- место для хранения UI-компонентов; -<strong>router</strong>- место для определения URL-адресов и сопоставления их с компонентами.</p>
26 <p>Файл client/src/components/HelloWorld.vue является компонентом Single File, разбитым на 3 различных подраздела: • template: для компонентного HTML; • script: тут компонентная логика реализована посредством JS; • style: для CSS-стилей.</p>
26 <p>Файл client/src/components/HelloWorld.vue является компонентом Single File, разбитым на 3 различных подраздела: • template: для компонентного HTML; • script: тут компонентная логика реализована посредством JS; • style: для CSS-стилей.</p>
27 <p>Запускаем dev-сервер:</p>
27 <p>Запускаем dev-сервер:</p>
28 $ cd client $ npm run dev<p>Перейдя по адресу http://localhost:8080, увидим:</p>
28 $ cd client $ npm run dev<p>Перейдя по адресу http://localhost:8080, увидим:</p>
29 <p>Теперь добавим в папку client/src/components новый компонент с именем Ping.vue :</p>
29 <p>Теперь добавим в папку client/src/components новый компонент с именем Ping.vue :</p>
30 &lt;template&gt; &lt;div&gt; &lt;p&gt;{{ msg }}&lt;/p&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'Ping', data() { return { msg: 'Hello!', }; }, }; &lt;/script&gt;<p>Также следует обновить файл client/src/router/index.js:</p>
30 &lt;template&gt; &lt;div&gt; &lt;p&gt;{{ msg }}&lt;/p&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'Ping', data() { return { msg: 'Hello!', }; }, }; &lt;/script&gt;<p>Также следует обновить файл client/src/router/index.js:</p>
31 import Vue from 'vue'; import Router from 'vue-router'; import Ping from '@/components/Ping'; Vue.use(Router); export default new Router({ routes: [ { path: '/', name: 'Ping', component: Ping, }, ], });<p>В client/src/App.vue удаляем изображение из темплейта:</p>
31 import Vue from 'vue'; import Router from 'vue-router'; import Ping from '@/components/Ping'; Vue.use(Router); export default new Router({ routes: [ { path: '/', name: 'Ping', component: Ping, }, ], });<p>В client/src/App.vue удаляем изображение из темплейта:</p>
32 &lt;template&gt; &lt;div id="app"&gt; &lt;router-view/&gt; &lt;/div&gt; &lt;/template&gt;<p>После всего этого, в браузере должно отобразиться "Hello!"</p>
32 &lt;template&gt; &lt;div id="app"&gt; &lt;router-view/&gt; &lt;/div&gt; &lt;/template&gt;<p>После всего этого, в браузере должно отобразиться "Hello!"</p>
33 <p>Для соединения клиентского Vue-приложения с бэкендом на Flask, подойдёт библиотека axios. Установим её:</p>
33 <p>Для соединения клиентского Vue-приложения с бэкендом на Flask, подойдёт библиотека axios. Установим её:</p>
34 $ npm install axios@0.18.0 -save<p>Теперь следует обновить раздел script компонента в Ping.vue:</p>
34 $ npm install axios@0.18.0 -save<p>Теперь следует обновить раздел script компонента в Ping.vue:</p>
35 &lt;script&gt; import axios from 'axios'; export default { name: 'Ping', data() { return { msg: '', }; }, methods: { getMessage() { const path = 'http://localhost:5000/ping'; axios.get(path) .then((res) =&gt; { this.msg = res.data; }) .catch((error) =&gt; { // eslint-выключение следующей строки console.error(error); }); }, }, created() { this.getMessage(); }, }; &lt;/script&gt;<p>Что же, давайте запустим Flask-приложение в новом окне. Теперь по адресу http://localhost:8080 отобразится "pong!".</p>
35 &lt;script&gt; import axios from 'axios'; export default { name: 'Ping', data() { return { msg: '', }; }, methods: { getMessage() { const path = 'http://localhost:5000/ping'; axios.get(path) .then((res) =&gt; { this.msg = res.data; }) .catch((error) =&gt; { // eslint-выключение следующей строки console.error(error); }); }, }, created() { this.getMessage(); }, }; &lt;/script&gt;<p>Что же, давайте запустим Flask-приложение в новом окне. Теперь по адресу http://localhost:8080 отобразится "pong!".</p>
36 <h2>Настраиваем Bootstrap</h2>
36 <h2>Настраиваем Bootstrap</h2>
37 <p>Чтобы быстро настраивать стиль нашего приложения, добавляем Bootstrap:</p>
37 <p>Чтобы быстро настраивать стиль нашего приложения, добавляем Bootstrap:</p>
38 $ npm install bootstrap@4.1.1 --save<p>Предупреждения для jquery и popper.js игнорируем - они нам не нужны.</p>
38 $ npm install bootstrap@4.1.1 --save<p>Предупреждения для jquery и popper.js игнорируем - они нам не нужны.</p>
39 <p>Стили Bootstrap импортируем в client/src/main.js:</p>
39 <p>Стили Bootstrap импортируем в client/src/main.js:</p>
40 import 'bootstrap/dist/css/bootstrap.css'; import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; /* eslint-отключение no-new */ new Vue({ el: '#app', router, components: { App }, template: '&lt;App/&gt;', });<p>Обновляем раздел style в client/src/App.vue:</p>
40 import 'bootstrap/dist/css/bootstrap.css'; import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; /* eslint-отключение no-new */ new Vue({ el: '#app', router, components: { App }, template: '&lt;App/&gt;', });<p>Обновляем раздел style в client/src/App.vue:</p>
41 &lt;style&gt; #app { margin-top: 60px } &lt;/style&gt;<p>Теперь нужно убедиться, что Bootstrap подключён корректно, для чего используем Container и Button в компоненте Ping:</p>
41 &lt;style&gt; #app { margin-top: 60px } &lt;/style&gt;<p>Теперь нужно убедиться, что Bootstrap подключён корректно, для чего используем Container и Button в компоненте Ping:</p>
42 &lt;template&gt; &lt;div class="container"&gt; &lt;button type="button" class="btn btn-primary"&gt;{{ msg }}&lt;/button&gt; &lt;/div&gt; &lt;/template&gt;<p>Запускаем dev-сервер:</p>
42 &lt;template&gt; &lt;div class="container"&gt; &lt;button type="button" class="btn btn-primary"&gt;{{ msg }}&lt;/button&gt; &lt;/div&gt; &lt;/template&gt;<p>Запускаем dev-сервер:</p>
43 <p>Ожидаемый результат:</p>
43 <p>Ожидаемый результат:</p>
44 <p>Теперь добавляем в новый файл Books.vue компонент Books:</p>
44 <p>Теперь добавляем в новый файл Books.vue компонент Books:</p>
45 &lt;template&gt; &lt;div class="container"&gt; &lt;p&gt;books&lt;/p&gt; &lt;/div&gt; &lt;/template&gt;<p>И обновляем роутер:</p>
45 &lt;template&gt; &lt;div class="container"&gt; &lt;p&gt;books&lt;/p&gt; &lt;/div&gt; &lt;/template&gt;<p>И обновляем роутер:</p>
46 import Vue from 'vue'; import Router from 'vue-router'; import Ping from '@/components/Ping'; import Books from '@/components/Books'; Vue.use(Router); export default new Router({ routes: [ { path: '/', name: 'Books', component: Books, }, { path: '/ping', name: 'Ping', component: Ping, }, ], mode: 'hash', });<p>Как обычно, тестируем: 1. http://localhost:8080. 2. http://localhost:8080/#/ping.</p>
46 import Vue from 'vue'; import Router from 'vue-router'; import Ping from '@/components/Ping'; import Books from '@/components/Books'; Vue.use(Router); export default new Router({ routes: [ { path: '/', name: 'Books', component: Books, }, { path: '/ping', name: 'Ping', component: Ping, }, ], mode: 'hash', });<p>Как обычно, тестируем: 1. http://localhost:8080. 2. http://localhost:8080/#/ping.</p>
47 <p>Чтобы избавиться от хеша в URL, меняем mode на history, дабы использовать для навигации history API-браузера:</p>
47 <p>Чтобы избавиться от хеша в URL, меняем mode на history, дабы использовать для навигации history API-браузера:</p>
48 export default new Router({ routes: [ { path: '/', name: 'Books', component: Books, }, { path: '/ping', name: 'Ping', component: Ping, }, ], mode: 'history', });<p>Добавляем таблицу в Bootstrap-стиле в компонент Books:</p>
48 export default new Router({ routes: [ { path: '/', name: 'Books', component: Books, }, { path: '/ping', name: 'Ping', component: Ping, }, ], mode: 'history', });<p>Добавляем таблицу в Bootstrap-стиле в компонент Books:</p>
49 &lt;template&gt; &lt;div class="container"&gt; &lt;div class="row"&gt; &lt;div class="col-sm-10"&gt; &lt;h1&gt;Books&lt;/h1&gt; &lt;hr&gt;&lt;br&gt;&lt;br&gt; &lt;button type="button" class="btn btn-success btn-sm"&gt;Add Book&lt;/button&gt; &lt;br&gt;&lt;br&gt; &lt;table class="table table-hover"&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope="col"&gt;Title&lt;/th&gt; &lt;th scope="col"&gt;Author&lt;/th&gt; &lt;th scope="col"&gt;Read?&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;foo&lt;/td&gt; &lt;td&gt;bar&lt;/td&gt; &lt;td&gt;foobar&lt;/td&gt; &lt;td&gt; &lt;button type="button" class="btn btn-warning btn-sm"&gt;Update&lt;/button&gt; &lt;button type="button" class="btn btn-danger btn-sm"&gt;Delete&lt;/button&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt;<p>Ожидаемый результат:</p>
49 &lt;template&gt; &lt;div class="container"&gt; &lt;div class="row"&gt; &lt;div class="col-sm-10"&gt; &lt;h1&gt;Books&lt;/h1&gt; &lt;hr&gt;&lt;br&gt;&lt;br&gt; &lt;button type="button" class="btn btn-success btn-sm"&gt;Add Book&lt;/button&gt; &lt;br&gt;&lt;br&gt; &lt;table class="table table-hover"&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope="col"&gt;Title&lt;/th&gt; &lt;th scope="col"&gt;Author&lt;/th&gt; &lt;th scope="col"&gt;Read?&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;foo&lt;/td&gt; &lt;td&gt;bar&lt;/td&gt; &lt;td&gt;foobar&lt;/td&gt; &lt;td&gt; &lt;button type="button" class="btn btn-warning btn-sm"&gt;Update&lt;/button&gt; &lt;button type="button" class="btn btn-danger btn-sm"&gt;Delete&lt;/button&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt;<p>Ожидаемый результат:</p>
50 <p>Итак, тепреь можно создавать функциональность CRUD-приложения.</p>
50 <p>Итак, тепреь можно создавать функциональность CRUD-приложения.</p>
51 <h2>Что предстоит создавать?</h2>
51 <h2>Что предстоит создавать?</h2>
52 <p>Наша задача - разработка бэкенда RESTful API, работающего на Python и Flask. API должен следовать принципам RESTful-разработки, применяя основные HTTP-команды: POST, PUT, GET, DELETE.</p>
52 <p>Наша задача - разработка бэкенда RESTful API, работающего на Python и Flask. API должен следовать принципам RESTful-разработки, применяя основные HTTP-команды: POST, PUT, GET, DELETE.</p>
53 <h2>GET-маршрут</h2>
53 <h2>GET-маршрут</h2>
54 <h4>Сервер</h4>
54 <h4>Сервер</h4>
55 <p>Начнём с добавления списка книг в server/app.py:</p>
55 <p>Начнём с добавления списка книг в server/app.py:</p>
56 BOOKS = [ { 'title': 'On the Road', 'author': 'Jack Kerouac', 'read': True }, { 'title': 'Harry Potter and the Philosopher\'s Stone', 'author': 'J. K. Rowling', 'read': False }, { 'title': 'Green Eggs and Ham', 'author': 'Dr. Seuss', 'read': True } ]<p>Потом добавляем обработчик маршрута:</p>
56 BOOKS = [ { 'title': 'On the Road', 'author': 'Jack Kerouac', 'read': True }, { 'title': 'Harry Potter and the Philosopher\'s Stone', 'author': 'J. K. Rowling', 'read': False }, { 'title': 'Green Eggs and Ham', 'author': 'Dr. Seuss', 'read': True } ]<p>Потом добавляем обработчик маршрута:</p>
57 @app.route('/books', methods=['GET']) def all_books(): return jsonify({ 'status': 'success', 'books': BOOKS })<p>Запускаем приложение, проверяем маршрут по адресу http://localhost:5000/books.</p>
57 @app.route('/books', methods=['GET']) def all_books(): return jsonify({ 'status': 'success', 'books': BOOKS })<p>Запускаем приложение, проверяем маршрут по адресу http://localhost:5000/books.</p>
58 <h4>Клиент</h4>
58 <h4>Клиент</h4>
59 <p>Обновляем компонент:</p>
59 <p>Обновляем компонент:</p>
60 &lt;template&gt; &lt;div class="container"&gt; &lt;div class="row"&gt; &lt;div class="col-sm-10"&gt; &lt;h1&gt;Books&lt;/h1&gt; &lt;hr&gt;&lt;br&gt;&lt;br&gt; &lt;button type="button" class="btn btn-success btn-sm"&gt;Add Book&lt;/button&gt; &lt;br&gt;&lt;br&gt; &lt;table class="table table-hover"&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope="col"&gt;Title&lt;/th&gt; &lt;th scope="col"&gt;Author&lt;/th&gt; &lt;th scope="col"&gt;Read?&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr v-for="(book, index) in books" :key="index"&gt; &lt;td&gt;{{ book.title }}&lt;/td&gt; &lt;td&gt;{{ book.author }}&lt;/td&gt; &lt;td&gt; &lt;span v-if="book.read"&gt;Yes&lt;/span&gt; &lt;span v-else&gt;No&lt;/span&gt; &lt;/td&gt; &lt;td&gt; &lt;button type="button" class="btn btn-warning btn-sm"&gt;Update&lt;/button&gt; &lt;button type="button" class="btn btn-danger btn-sm"&gt;Delete&lt;/button&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import axios from 'axios'; export default { data() { return { books: [], }; }, methods: { getBooks() { const path = 'http://localhost:5000/books'; axios.get(path) .then((res) =&gt; { this.books = res.data.books; }) .catch((error) =&gt; { // eslint-отключение следующей строки console.error(error); }); }, }, created() { this.getBooks(); }, }; &lt;/script&gt;<p>После того, как компонент инициализирован, вызываем метод getBooks() посредством Lifecycle Hook.</p>
60 &lt;template&gt; &lt;div class="container"&gt; &lt;div class="row"&gt; &lt;div class="col-sm-10"&gt; &lt;h1&gt;Books&lt;/h1&gt; &lt;hr&gt;&lt;br&gt;&lt;br&gt; &lt;button type="button" class="btn btn-success btn-sm"&gt;Add Book&lt;/button&gt; &lt;br&gt;&lt;br&gt; &lt;table class="table table-hover"&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope="col"&gt;Title&lt;/th&gt; &lt;th scope="col"&gt;Author&lt;/th&gt; &lt;th scope="col"&gt;Read?&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr v-for="(book, index) in books" :key="index"&gt; &lt;td&gt;{{ book.title }}&lt;/td&gt; &lt;td&gt;{{ book.author }}&lt;/td&gt; &lt;td&gt; &lt;span v-if="book.read"&gt;Yes&lt;/span&gt; &lt;span v-else&gt;No&lt;/span&gt; &lt;/td&gt; &lt;td&gt; &lt;button type="button" class="btn btn-warning btn-sm"&gt;Update&lt;/button&gt; &lt;button type="button" class="btn btn-danger btn-sm"&gt;Delete&lt;/button&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import axios from 'axios'; export default { data() { return { books: [], }; }, methods: { getBooks() { const path = 'http://localhost:5000/books'; axios.get(path) .then((res) =&gt; { this.books = res.data.books; }) .catch((error) =&gt; { // eslint-отключение следующей строки console.error(error); }); }, }, created() { this.getBooks(); }, }; &lt;/script&gt;<p>После того, как компонент инициализирован, вызываем метод getBooks() посредством Lifecycle Hook.</p>
61 <p>Для просмотра книг в темплейте используется директива v-for, создающая на каждой итерации новую строку таблицы. При этом значение индекса применяется в виде ключа (key). Потом применяется директива v-if, обеспечивающая отображение Yes либо No (читал ли пользователь книгу).</p>
61 <p>Для просмотра книг в темплейте используется директива v-for, создающая на каждой итерации новую строку таблицы. При этом значение индекса применяется в виде ключа (key). Потом применяется директива v-if, обеспечивающая отображение Yes либо No (читал ли пользователь книгу).</p>
62 <h2>Bootstrap Vue</h2>
62 <h2>Bootstrap Vue</h2>
63 <p>Далее используем компонент Modal, необходимый для добавления новых книг. Пригодится библиотека Bootstrap Vue, предоставляющая нам набор Vue-компонентов, стилизованных посредством CSS и HTML на основе Bootstrap.</p>
63 <p>Далее используем компонент Modal, необходимый для добавления новых книг. Пригодится библиотека Bootstrap Vue, предоставляющая нам набор Vue-компонентов, стилизованных посредством CSS и HTML на основе Bootstrap.</p>
64 <p>Почему мы выбрали Bootstrap Vue? Потому что Modal Bootstrap использует jQuery, а нам желательно избегать применения в одном проекте jQuery и Vue, ведь последний задействует для обновления DOM-структуры Virtual Dom. В результате, если вы используете для манипуляций с DOM jQuery, Vue об этом не узнает.</p>
64 <p>Почему мы выбрали Bootstrap Vue? Потому что Modal Bootstrap использует jQuery, а нам желательно избегать применения в одном проекте jQuery и Vue, ведь последний задействует для обновления DOM-структуры Virtual Dom. В результате, если вы используете для манипуляций с DOM jQuery, Vue об этом не узнает.</p>
65 <p>Выполняем установку:</p>
65 <p>Выполняем установку:</p>
66 $ npm install bootstrap-vue@2.0.0-rc.11 -save<p>Подключаем Bootstrap Vue в файле client/src/main.js:</p>
66 $ npm install bootstrap-vue@2.0.0-rc.11 -save<p>Подключаем Bootstrap Vue в файле client/src/main.js:</p>
67 import 'bootstrap/dist/css/bootstrap.css'; import BootstrapVue from 'bootstrap-vue'; import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; Vue.use(BootstrapVue); /* eslint-отключение no-new */ new Vue({ el: '#app', router, components: { App }, template: '&lt;App/&gt;', });<p>Это ещё далеко не всё, продолжение смотрите в следующей части нашей статьи.</p>
67 import 'bootstrap/dist/css/bootstrap.css'; import BootstrapVue from 'bootstrap-vue'; import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; Vue.use(BootstrapVue); /* eslint-отключение no-new */ new Vue({ el: '#app', router, components: { App }, template: '&lt;App/&gt;', });<p>Это ещё далеко не всё, продолжение смотрите в следующей части нашей статьи.</p>
68 <p><em>Источник - "<a>Developing a Single Page App with Flask and Vue.js</a>".</em></p>
68 <p><em>Источник - "<a>Developing a Single Page App with Flask and Vue.js</a>".</em></p>
69  
69