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
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>client</title> </head> <body> <div id="app"></div> <!-- встроенные файлы вводятся автоматически --> </body> </html><p>Кроме того, у нас есть элемент</p>
22
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>client</title> </head> <body> <div id="app"></div> <!-- встроенные файлы вводятся автоматически --> </body> </html><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
<template> <div> <p>{{ msg }}</p> </div> </template> <script> export default { name: 'Ping', data() { return { msg: 'Hello!', }; }, }; </script><p>Также следует обновить файл client/src/router/index.js:</p>
30
<template> <div> <p>{{ msg }}</p> </div> </template> <script> export default { name: 'Ping', data() { return { msg: 'Hello!', }; }, }; </script><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
<template> <div id="app"> <router-view/> </div> </template><p>После всего этого, в браузере должно отобразиться "Hello!"</p>
32
<template> <div id="app"> <router-view/> </div> </template><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
<script> import axios from 'axios'; export default { name: 'Ping', data() { return { msg: '', }; }, methods: { getMessage() { const path = 'http://localhost:5000/ping'; axios.get(path) .then((res) => { this.msg = res.data; }) .catch((error) => { // eslint-выключение следующей строки console.error(error); }); }, }, created() { this.getMessage(); }, }; </script><p>Что же, давайте запустим Flask-приложение в новом окне. Теперь по адресу http://localhost:8080 отобразится "pong!".</p>
35
<script> import axios from 'axios'; export default { name: 'Ping', data() { return { msg: '', }; }, methods: { getMessage() { const path = 'http://localhost:5000/ping'; axios.get(path) .then((res) => { this.msg = res.data; }) .catch((error) => { // eslint-выключение следующей строки console.error(error); }); }, }, created() { this.getMessage(); }, }; </script><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: '<App/>', });<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: '<App/>', });<p>Обновляем раздел style в client/src/App.vue:</p>
41
<style> #app { margin-top: 60px } </style><p>Теперь нужно убедиться, что Bootstrap подключён корректно, для чего используем Container и Button в компоненте Ping:</p>
41
<style> #app { margin-top: 60px } </style><p>Теперь нужно убедиться, что Bootstrap подключён корректно, для чего используем Container и Button в компоненте Ping:</p>
42
<template> <div class="container"> <button type="button" class="btn btn-primary">{{ msg }}</button> </div> </template><p>Запускаем dev-сервер:</p>
42
<template> <div class="container"> <button type="button" class="btn btn-primary">{{ msg }}</button> </div> </template><p>Запускаем dev-сервер:</p>
43
<p>Ожидаемый результат:</p>
43
<p>Ожидаемый результат:</p>
44
<p>Теперь добавляем в новый файл Books.vue компонент Books:</p>
44
<p>Теперь добавляем в новый файл Books.vue компонент Books:</p>
45
<template> <div class="container"> <p>books</p> </div> </template><p>И обновляем роутер:</p>
45
<template> <div class="container"> <p>books</p> </div> </template><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
<template> <div class="container"> <div class="row"> <div class="col-sm-10"> <h1>Books</h1> <hr><br><br> <button type="button" class="btn btn-success btn-sm">Add Book</button> <br><br> <table class="table table-hover"> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Read?</th> <th></th> </tr> </thead> <tbody> <tr> <td>foo</td> <td>bar</td> <td>foobar</td> <td> <button type="button" class="btn btn-warning btn-sm">Update</button> <button type="button" class="btn btn-danger btn-sm">Delete</button> </td> </tr> </tbody> </table> </div> </div> </div> </template><p>Ожидаемый результат:</p>
49
<template> <div class="container"> <div class="row"> <div class="col-sm-10"> <h1>Books</h1> <hr><br><br> <button type="button" class="btn btn-success btn-sm">Add Book</button> <br><br> <table class="table table-hover"> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Read?</th> <th></th> </tr> </thead> <tbody> <tr> <td>foo</td> <td>bar</td> <td>foobar</td> <td> <button type="button" class="btn btn-warning btn-sm">Update</button> <button type="button" class="btn btn-danger btn-sm">Delete</button> </td> </tr> </tbody> </table> </div> </div> </div> </template><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
<template> <div class="container"> <div class="row"> <div class="col-sm-10"> <h1>Books</h1> <hr><br><br> <button type="button" class="btn btn-success btn-sm">Add Book</button> <br><br> <table class="table table-hover"> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Read?</th> <th></th> </tr> </thead> <tbody> <tr v-for="(book, index) in books" :key="index"> <td>{{ book.title }}</td> <td>{{ book.author }}</td> <td> <span v-if="book.read">Yes</span> <span v-else>No</span> </td> <td> <button type="button" class="btn btn-warning btn-sm">Update</button> <button type="button" class="btn btn-danger btn-sm">Delete</button> </td> </tr> </tbody> </table> </div> </div> </div> </template> <script> import axios from 'axios'; export default { data() { return { books: [], }; }, methods: { getBooks() { const path = 'http://localhost:5000/books'; axios.get(path) .then((res) => { this.books = res.data.books; }) .catch((error) => { // eslint-отключение следующей строки console.error(error); }); }, }, created() { this.getBooks(); }, }; </script><p>После того, как компонент инициализирован, вызываем метод getBooks() посредством Lifecycle Hook.</p>
60
<template> <div class="container"> <div class="row"> <div class="col-sm-10"> <h1>Books</h1> <hr><br><br> <button type="button" class="btn btn-success btn-sm">Add Book</button> <br><br> <table class="table table-hover"> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Read?</th> <th></th> </tr> </thead> <tbody> <tr v-for="(book, index) in books" :key="index"> <td>{{ book.title }}</td> <td>{{ book.author }}</td> <td> <span v-if="book.read">Yes</span> <span v-else>No</span> </td> <td> <button type="button" class="btn btn-warning btn-sm">Update</button> <button type="button" class="btn btn-danger btn-sm">Delete</button> </td> </tr> </tbody> </table> </div> </div> </div> </template> <script> import axios from 'axios'; export default { data() { return { books: [], }; }, methods: { getBooks() { const path = 'http://localhost:5000/books'; axios.get(path) .then((res) => { this.books = res.data.books; }) .catch((error) => { // eslint-отключение следующей строки console.error(error); }); }, }, created() { this.getBooks(); }, }; </script><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: '<App/>', });<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: '<App/>', });<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