Одностраничные приложения загружают только одну главную страницу, а затем загружают другие фрагменты страницы через AJAX без обновления. На первый взгляд есть только один файл HTML, так называемая отдельная страница. С точки зрения разработки, интерфейс и сервер разделены, интерфейс ориентирован на шаблоны рендеринга, а серверу нужно только предоставить API, поэтому нет необходимости устанавливать шаблоны самостоятельно. По сути, страница и общие файлы JS и CSS загружаются только один раз, что может снизить нагрузку на сервер и сэкономить определенную пропускную способность сети. Кроме того, поскольку нет необходимости каждый раз загружать страницы и совместно используемые статические файлы, скорость отклика также в определенной степени повышается, а взаимодействие с пользователем улучшается. Есть, конечно, и некоторые минусы, например SEO-оптимизация неудобна, но соответствующие решения есть. В целом преимущества использования одностраничных приложений намного перевешивают недостатки, поэтому все больше и больше людей используют одностраничные приложения.
Есть много способов создать одностраничное приложение, здесь мы выбираем реализацию Flask + Vue. В этой статье в качестве основной линии используется демонстрация CRUD Demo, а для ее описания добавлены необходимые технические моменты. Это может включать некоторые концепции, с которыми вы не знакомы или не знакомы, но это не имеет значения, я дам вам соответствующие справочные статьи, чтобы помочь вам понять. Конечно, Даниэль может их игнорировать :). Прочитав эту статью, я считаю, что вы также можете создать свое собственное одностраничное приложение.
1 передняя часть
Здесь мы будем использовать фреймворк Vue.Если вы еще не прикасались к нему, рекомендуется прочитать раздел «Основы» официальной документации. Вы также можете сначала посмотреть прямо вниз.Демо использует некоторые основные вещи, и вы должны быть в состоянии понять это, взглянув на него примерно. Даже если вы пока не понимаете этого, попрактиковавшись на примере, вы должны получить больше информации из документации.
Для более удобного создания проекта на основе Vue мы можем использовать скаффолдинг Vue Cli. При создании проекта с помощью строительных лесов это поможет нам выполнить некоторую настройку, сэкономив нам время на ручную настройку. Новые партнеры будут использовать его для создания проектов на ранней стадии, а о некоторых более глубоких вещах они узнают позже.
Установить леса
$ npm install -g @vue/cli
Здесь мы устанавливаем последнюю версию 3.
Существует множество библиотек компонентов пользовательского интерфейса, основанных на Vue, таких как iView, Element, Vuetify и т. д. Есть много людей, которые используют iView и Element в Китае, но относительно мало людей используют Vuetify, Я не знаю, потому ли это, что людям не нравится его стиль Material Design, или из-за нехватки его китайских документов. Но лично мне нравится стиль Vuetify, поэтому я буду использовать эту библиотеку компонентов для создания интерфейсных страниц.
Если вы еще не использовали эту библиотеку компонентов, вы также можете получить общее представление об использовании Vuetify, следуя пошаговым инструкциям в этой статье. Если вы чувствуете, что в этом процессе слишком много вопросов, вы можете посмотреть этот видеоурок на YouTube.
https://dwz.cn/lxMHF4bY
Не ищите везде одинаковые ресурсы, прочитав серию видео и добавив официальные документы, освоить общеупотребительные пункты в принципе не составит труда.
Тем не менее, все же рекомендуется сначала реализовать Демо по этой статье, а потом изучить его, я думаю, это будет эффективнее.
Создайте новый каталог spa-demo, а затем переключитесь в этот каталог, чтобы создать новый интерфейсный клиент проекта.
$ vue create client
При создании проекта вам будет предложено вручную выбрать некоторые конфигурации, вот мои настройки на тот момент
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)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) N
После завершения установки мы переключаемся в каталог клиента и выполните команду
$ npm run serve
После выполнения вышеуказанной команды будет вывод, подобный этому
...
App running at:
- Local: http://localhost:8080/
- Network: http://172.20.10.3:8080/
...
Доступ в браузере
http://localhost:8080/
Если вы видите страницу с текстом ниже
Welcome to Your Vue.js App
Установка проекта прошла успешно.
Установить Vuetify
$ vue add vuetify
Вам также будет предложено выбрать некоторые конфигурации, здесь я выбираю Default
? Choose a preset: Default (recommended)
После завершения установки перезагрузите сервер
$ npm run serve
После выполнения заходим в браузер
http://localhost:8080/
Вы увидите некоторые изменения в содержании страницы, там такая строчка текста
Welcome to Vuetify
Это показывает, что Vuetify успешно установлен.
Посмотрите на структуру каталогов в это время
spa-demo
└── client
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── node_module
│ └── ...
├── public
│ ├── favicon.ico
│ └── index.html
└── src
├── App.vue
├── assets
│ ├── logo.png
│ └── logo.svg
├── components
│ └── HelloWorld.vue
├── main.js
├── plugins
│ └── vuetify.js
├── router.js
└── views
├── About.vue
└── Home.vue
упрощатьspa-demo/client/src/App.vue
, измените его на
<template>
<v-app>
<v-content>
<router-view></router-view>
</v-content>
</v-app>
</template>
<script>
export default {
name: 'App',
data () {
return {
//
}
}
}
</script>
Исправлятьspa-demo/client/src/views/Home.vue
, поместите таблицу данных на страницу
<template>
<div class="home">
<v-container class="my-5">
<!-- 对话框 -->
<!-- 表格 -->
<v-data-table
:headers="headers"
:items="books"
hide-actions
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.name }}</td>
<td>{{ props.item.category }}</td>
<td class="layout px-0">
<v-icon small class="ml-4" @click="editItem(props.item)">
edit
</v-icon>
<v-icon small @click="deleteItem(props.item)">
delete
</v-icon>
</td>
</template>
<template slot="no-data">
<v-alert :value="true" color="info" outline>
无数据
</v-alert>
</template>
</v-data-table>
</v-container>
</div>
</template>
<script>
export default {
data: () => ({
headers: [
{ text: '书名', value: 'name', sortable: false, align: 'left'},
{ text: '分类', value: 'category', sortable: false },
{ text: '操作', value: 'name', sortable: false }
],
books: [],
}),
created () {
this.books = [
{ name: '生死疲劳', category: '文学' },
{ name: '国家宝藏', category: '人文社科' },
{ name: '人类简史', category: '科技' },
]
},
}
</script>
Мы использовали заголовки данных и книги для управления заголовком и данными таблицы и заполнили книги некоторыми временными данными при ее создании.
На этой странице используется таблица данных. Вам не нужно запоминать соответствующий код. В документации Vuetify есть много примеров поиска таблицы данных. Прочитав несколько, вы узнаете, как ее использовать. Новичкам может быть трудно понять слот-область (scope slot), см. официальную документацию Vue для этого контента.
- "Основы компонентов" в разделе "Основы"
- «Регистрация компонента», «Реквизит», «Пользовательские события», «Слоты» в разделе «Погружение в компоненты».
Просто остановитесь и прочитайте, и вы поймете, это не сложно, я не буду повторяться здесь.
Точно так же и здесь можно проследить за тыквой и сначала нарисовать совок, можно временно проигнорировать какие-то непонятные места, а потом попробовать разобраться после практики.
Открытым
http://localhost:8080/
Страница, которую вы видите, похожа на эту
Просто список книг.
Теперь нам нужно сделать всплывающий диалог для добавления книг. мы в<!-- 对话框 -->
Добавьте следующий код в местоположение
<v-toolbar flat class="white">
<v-toolbar-title>图书列表</v-toolbar-title>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="600px">
<v-btn slot="activator" class="primary" dark>新增</v-btn>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-alert :value="Boolean(errMsg)" color="error" icon="warning" outline>
{{ errMsg }}
</v-alert>
<v-container grid-list-md>
<v-layout>
<v-flex xs12 sm6 md4>
<v-text-field label="书名" v-model="editedItem.name"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field label="分类" v-model="editedItem.category"></v-text-field>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat @click="close">取消</v-btn>
<v-btn color="blue darken-1" flat @click="save">保存</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
соответственно, в<script></script>
добавить немного JS между ними
export default {
data: () => ({
dialog: false, // 是否展示对话框
errMsg: '', // 是否有错误信息
editedIndex: -1, // 当前在对话框中编辑的图书在列表中的序号
editedItem: { // 当前在对话框中编辑的图书内容
id: 0,
name: '',
category: ''
},
defaultItem: { // 默认的图书内容,用于初始化新增对话框内容
id: 0,
name: '',
category: ''
}
}),
computed: {
formTitle () {
return this.editedIndex === -1 ? '新增' : '编辑'
}
},
watch: {
dialog (val) {
if (!val) {
this.close()
this.clearErrMsg()
}
}
},
methods: {
clearErrMsg () {
this.errMsg = ''
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
}
}
}
Чтобы сделать статью более лаконичной, я опустил предыдущие фрагменты, когда размещал код.При написании вы можете добавить приведенный выше код в соответствующее место в зависимости от местоположения.
Мы использовали панель инструментов и диалоговое окно, чтобы добавить в таблицу элементы, связанные с диалогами. Точно так же нам не нужно запоминать код. Если вы не знаете, как его написать, просто обратитесь к документации.
Диалоговое окно данных указывает, отображается ли текущее диалоговое окно, errMsg управляет отображением сообщения об ошибке, а диалоговое окно прослушивателя закрывает диалоговое окно и очищает errMsg, когда оно изменяется на false. Вычисляемое свойство formTitle используется для управления заголовком диалогового окна. Затем добавляются два элемента формы для заполнения названия книги и категории.
Когда мы нажимаем «Добавить», страница выглядит так
На самом деле, на данный момент наша страница интерфейса почти в порядке, после чего следует реализация дополнений, удалений и изменений. Сначала мы реализуем это в одностороннем порядке во внешнем интерфейсе, а затем интегрируем с бэкэндом. Таким образом, демонстрационная версия интерфейса будет более полной.
Реализовать метод сохранения, добавить методы сохранения
save() {
if (this.editedIndex > -1) { // 编辑
Object.assign(this.books[this.editedIndex], this.editedItem)
} else { // 新增
this.books.push(this.editedItem)
}
this.close()
}
При редактировании для отображения попапа нам нужно добавить метод editItem
editItem (item) {
this.editedIndex = this.books.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
}
Способ сохранения такой же, как и при его добавлении.
Реализовать метод удаления deleteItem
deleteItem (item) {
const index = this.books.indexOf(item)
confirm('确认删除?') && this.books.splice(index, 1)
}
На этом фронтенд-проект подошёл к концу.
2 задний конец
На серверной части нам нужно только предоставить интерфейс для добавления, удаления, изменения и проверки использования внешнего интерфейса. RESTful API — это относительно зрелый набор теорий проектирования интернет-приложений, и на его основе я также реализую соответствующий рабочий интерфейс книги.
Учитывая, что есть партнеры, которые не знакомы с RESTful API, я перечислил несколько статей, которые я изучил ранее, для справки.
-
«Понимание архитектуры RESTful»
https://dwz.cn/eXu0p6pv
-
Руководство по дизайну RESTful API
https://dwz.cn/8v4B0twY
-
Лучшие практики RESTful API
https://dwz.cn/2aSnI8fF
-
Зная вопрос «Как объяснить REST простым языком, а RESTful? 》
https://dwz.cn/bVxrSsf4
Прочитав соответствующую информацию выше, вы должны иметь определенное представление об этой теории дизайна.
Кроме того, вам пока не нужно досконально разбираться в RESTful API, просто поймите его, как показано ниже.
Это использование URL для поиска ресурсов и использование HTTP для описания операций.
Это ответ, который я видел на вышеупомянутый вопрос Zhihu, автор @Ivony. Написано лаконично, но со смыслом.
После того, как я попрактиковался один раз, я оглянусь на некоторые теоретические вещи, и я буду более впечатлен.
Сначала перечислите интерфейсы, которые нам нужно реализовать.
серийный номер | метод | URL | описывать |
---|---|---|---|
1 | GET | http://domain/api/v1/books | Получить все книги |
2 | GET | http://domain/api/v1/books/123 | Получить книги с первичным ключом 123 |
3 | POST | http://domain/api/v1/books | Добавить книги |
4 | PUT | http://domain/api/v1/books/123 | Книга обновлений с первичным ключом 123 |
5 | DELETE | http://domain/api/v1/books/123 | удалить книгу с первичным ключом 123 |
Мы можем напрямую использовать Flask для реализации вышеописанного интерфейса, но когда ресурсов много, мы будем писать много повторяющихся фрагментов при написании кода, что нарушает принцип DRY (Don’t Repeat Yourself), и поддерживать его более проблематично. позже, поэтому мы используем реализацию расширения Flask -RESTful.
Кроме того, в этом разделе основное внимание уделяется реализации интерфейса, и для простоты мы храним данные непосредственно в словаре и не задействуем операции, связанные с базой данных.
Создайте новый каталог сервера в каталоге spa-demo и переключитесь в этот каталог, чтобы инициализировать среду Python.
$ pipenv --python 3.6.0
В настоящее время Pipenv является официально рекомендуемой виртуальной средой и инструментом управления пакетами.Я уже писал статью «Быстрый старт Pipenv», и вы можете прочитать ее, если не прикасались к ней.
Установить колбу
$ pipenv install flask
Установить Flask-RESTful
$ pipenv install flask-restful
новыйspa-demo/server/app.py
# coding=utf-8
from flask import Flask, request
from flask_restful import Api, Resource, reqparse, abort
app = Flask(__name__)
api = Api(app)
books = [{'id': 1, 'name': 'book1', 'category': 'cat1'},
{'id': 2, 'name': 'book2', 'category': 'cat2'},
{'id': 3, 'name': 'book3', 'category': 'cat3'}]
# 公共方法区
class BookApi(Resource):
def get(self, book_id):
pass
def put(self, book_id):
pass
def delete(self, book_id):
pass
class BookListApi(Resource):
def get(self):
return books
def post(self):
pass
api.add_resource(BookApi, '/api/v1/books/<int:book_id>', endpoint='book')
api.add_resource(BookListApi, '/api/v1/books', endpoint='books')
if __name__ == '__main__':
app.run(debug=True)
Выше приведена стандартная структура кода, интегрирующая Flask-RESTful, похожие примеры можно увидеть в официальной документации Flask-RESTful. Для каждого ресурса мы можем реализовать интерфейс с аналогичной структурой. Методы get, put и delete в классе BookApi соответствуют интерфейсам 2, 4 и 5, а методы get и post в классе BookListApi соответствуют интерфейсам 1 и 3. Затем идет путь регистрации. Увидев это, у некоторых партнеров могут возникнуть вопросы, зачем нужно определять два класса для одного и того же ресурса? На самом деле удобно прописывать маршрут с первичным ключом для ресурса и без него.
На данный момент структура проекта
spa-demo
├── client
│ └── ...
└── server
├── Pipfile
├── Pipfile.lock
└── app.py
переключить наspa-demo/server
каталог, запуститьapp.py
$ pipenv run python app.py
Затем проверьте, доступен ли интерфейс получения всех книг. Поскольку это тест API, не рекомендуется использовать браузер напрямую, ведь иногда неудобно конструировать параметры и просматривать информацию HTTP. Рекомендуется использовать Postman. Конечно, для простых тестов можно использовать команду curl напрямую.
Интерфейс запроса 1 для получения всей информации о книге
$ curl -i http://127.0.0.1:5000/api/v1/books
получил ответ
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 249
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:21:56 GMT
[
{
"id": 1,
"name": "book1",
"category": "cat1"
},
{
"id": 2,
"name": "book2",
"category": "cat2"
},
{
"id": 3,
"name": "book3",
"category": "cat3"
}
]
Все книги успешно получены, что указывает на то, что интерфейс 1 в порядке.
Затем реализуйте интерфейс 2, чтобы получить книгу с указанным идентификатором. Поскольку операция получения книг на основе идентификатора и выдача 404, когда книга не существует, будет часто использоваться, здесь в «области общедоступных методов» упомянуты два метода.
def get_by_id(book_id):
book = [v for v in books if v['id'] == book_id]
return book[0] if book else None
def get_or_abort(book_id):
book = get_by_id(book_id)
if not book:
abort(404, message=f'Book {book_id} not found')
return book
Затем реализуйте метод get в BookApi.
def get(self, book_id):
book = get_or_abort(book_id)
return book
Возьмите книгу с ID 1 для проверки
$ curl -i http://127.0.0.1:5000/api/v1/books/1
результат
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 61
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:31:48 GMT
{
"id": 1,
"name": "book1",
"category": "cat1"
}
Возьмите книгу с ID 5 и протестируйте ее.
$ curl -i http://127.0.0.1:5000/api/v1/books/5
результат
HTTP/1.0 404 NOT FOUND
Content-Type: application/json
Content-Length: 149
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:32:47 GMT
{
"message": "Book 5 not found. You have requested this URI [/api/v1/books/5] but did you mean /api/v1/books/<int:book_id> or /api/v1/books ?"
}
Если идентификатор равен 1, информация о книге получена успешно; если идентификатор равен 5, будет возвращен ответ 404, поскольку книга не существует. Результаты теста соответствуют ожиданиям, указывая на то, что интерфейс в порядке.
Реализуйте интерфейс 3 и добавьте новые книги. При добавлении новых книг мы должны проверять, соответствуют ли параметры требованиям. Flask-RESTFul предоставляет нам более элегантную реализацию, которая не требует от нас использования жестко закодированной формы множественных суждений if для проверки правильности параметров.
Поскольку название книги и категория не могут быть пустыми, нам нужно настроить правила, мы можем добавить метод в «область общедоступных методов».
def not_empty_str(s):
s = str(s)
if not s:
raise ValueError("Must not be empty string")
return s
Переопределить метод инициализации BookListApi
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('name', type=not_empty_str, required=True, location='json')
self.reqparse.add_argument('category', type=not_empty_str, required=True, location='json')
super(BookListApi, self).__init__()
Затем реализуйте метод post
def post(self):
args = self.reqparse.parse_args()
book = {
'id': books[-1]['id'] + 1 if books else 1,
'name': args['name'],
'category': args['category'],
}
books.append(book)
return book, 201
В методе сначала проверьте правильность параметров, затем возьмите идентификатор последней книги плюс 1 и сохраните его как идентификатор новой книги, и, наконец, верните добавленную информацию о книге и код состояния 201 (указывающий, что она была созданный).
Проверить правильность проверки параметра
$ curl -i \
-H "Content-Type: application/json" \
-X POST \
-d '{"name":"","category":""}' \
http://127.0.0.1:5000/api/v1/books
результат
HTTP/1.0 400 BAD REQUEST
Content-Type: application/json
Content-Length: 70
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:46:18 GMT
{
"message": {
"name": "Must not be empty string"
}
}
Возвращается ошибка 400, указывающая, что проверка параметра действительна.
Проверьте, доступен ли новый интерфейс
$ curl -i \
-H "Content-Type: application/json" \
-X POST \
-d '{"name":"t_name","category":"t_cat"}' \
http://127.0.0.1:5000/api/v1/books
результат
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 63
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:53:54 GMT
{
"id": 4,
"name": "t_name",
"category": "t_cat"
}
Указывает, что создание прошло успешно. Подтверждаем получением книжного интерфейса указанного ID
$ curl -i http://127.0.0.1:5000/api/v1/books/4
результат
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 63
Server: Werkzeug/0.14.1 Python/3.6.0
Date: Thu, 13 Dec 2018 15:54:18 GMT
{
"id": 4,
"name": "t_name",
"category": "t_cat"
}
Если приобретение прошло успешно, значит, создание действительно удалось, а значит, и с интерфейсом 3 все в порядке.
Реализация интерфейсов 4 и 5 аналогична вышеописанной, а код выложен здесь, и подробно его объяснять не буду.
Как и в случае с BookListApi, сначала перепишите метод инициализации BookApi.
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('name', type=not_empty_str, required=True, location='json')
self.reqparse.add_argument('category', type=not_empty_str, required=True, location='json')
super(BookApi, self).__init__()
Затем реализуйте методы put и delete
def put(self, book_id):
book = get_or_abort(book_id)
args = self.reqparse.parse_args()
for k, v in args.items():
book[k] = v
return book, 201
def delete(self, book_id):
book = get_or_abort(book_id)
del book
return '', 204
На этом бэкэнд-проект в основном завершен.
Конечно, это неполный вариант, например, нет аутентификации для API, это можно сделать с помощью Flask-HTTPAuth или других методов. Из-за нехватки места я не буду расширять описание здесь, если вам интересно, вы можете прочитать документацию этого расширения или исследовать и реализовать его самостоятельно.
3 Интеграция
У отдельного фронтенда или бэкенда есть прототип, а до интеграции всего один шаг.
Фронтенду нужно запросить данные, здесь мы используем axios, переключаемся наspa-demo/client
Установить в каталог
$ npm install axios --save
Исправлятьspa-demo/client/src/views/Home.vue
,существуетscript
Ввести аксиомы между тегами и инициализировать адрес API
import axios from 'axios'
const booksApi = 'http://localhost:5000/api/v1/books'
export default {
...
}
Измените логику хука, созданного для получения данных из бэкенда.
created () {
axios.get(booksApi)
.then(response => {
this.books = response.data
})
.catch(error => {
console.log(error)
})
}
После запуска внешнего проекта проверьте домашнюю страницу и обнаружите, что данных нет. Заглянув в инструменты разработчика, мы найдем такую ошибку
Access to XMLHttpRequest at 'http://localhost:5000/api/v1/books' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
То есть текущий проект не поддерживает CORS (Cross-Origin Resource Sharing, то есть доступ к ресурсам между источниками). Мы можем реализовать это в виде добавления прокси на интерфейсе или через Flask-CORS на сервере. Здесь я использую последний.
переключить наspa-demo/server
каталог, установите Flask-CORS
$ pipenv install flask-cors
Исправлятьspa-demo/server/app.py
, ввести CORS в голову
from flask_cors import CORS
в коде
app = Flask(__name__)
и
api = Api(app)
добавить линию между
CORS(app, resources={r"/api/*": {"origins": "*"}})
Затем повторно запустите app.py, обновите домашнюю страницу, мы увидим, что в списке есть данные, указывающие на то, что проблема CORS была успешно решена.
существуетspa-demo/client/src/views/Home.vue
, измените метод сохранения и добавьте вспомогательный метод setErrMsg
setErrMsg (errResponse) {
let errResMsg = errResponse.data.message
if (typeof errResMsg === 'string') {
this.errMsg = errResMsg
} else {
let errMsgs = []
let k
for (k in errResMsg) {
errMsgs.push('' + k + ' ' + errResMsg[k])
}
this.errMsg = errMsgs.join(',')
}
},
save() {
if (this.editedIndex > -1) { // 编辑
axios.put(booksApi + '/' + this.editedItem.id, this.editedItem)
.then(response => {
Object.assign(this.books[this.editedIndex], response.data)
this.close()
}).catch(error => {
this.setErrMsg(error.response)
console.log(error)
})
} else { // 新增
axios.post(booksApi, this.editedItem)
.then(response => {
this.books.push(response.data)
this.close()
}).catch(error => {
this.setErrMsg(error.response)
console.log(error)
})
}
}
На этом этапе книга добавлена и сохранена.
Изменить метод deleteItem
deleteItem (item) {
const index = this.books.indexOf(item)
confirm('确认删除?') && axios.delete(booksApi + '/' + this.books[0].id)
.then(response => {
this.books.splice(index, 1)
}).catch(error => {
this.setErrMsg(error.response)
console.log(error)
})
}
На этом этапе также выполняется метод удаления.
На этом интеграция завершена, и завершена демонстрация CRUD, основанная на разделении интерфейса и сервера Vue + Flask.
Прочитав эту статью, вы можете выполнить шаги, чтобы реализовать это самостоятельно. У новых партнеров могут возникнуть сомнения в некоторых местах в процессе чтения, и я также предоставил некоторую информацию, о которой я могу подумать, вы можете попробовать ее. Если вы не можете предоставить их все, вам нужно решить это самостоятельно в Baidu/Google.Тем не менее, я все же рекомендую не пытаться понять каждый пункт очень четко, сначала понять ключевые моменты, постараться их осознать, и когда вы оглянетесь на соответствующую информацию, вы почувствуете себя более эмоционально.
Полный код можно посмотреть на GitHub
https://github.com/kevinbai-cn/spa-demo
4 Ссылка
- «Полнофункциональное одностраничное приложение с Vue.js и Flask»
https://bit.ly/2C9kSiG
- «Разработка одностраничного приложения с помощью Flask и Vue.js»
https://bit.ly/2ElaXrB
- Проверка документов
https://bit.ly/2QupMzx
- «Разработка RESTful API с помощью Python и Flask»
https://bit.ly/2vqq3Y1
- «Разработка RESTful API с использованием Flask-RESTful»
https://bit.ly/2nGDNtL
Эта статья была впервые опубликована на паблике «Little Backend».