Flask-Vue 前后端分离基础
使用 Flask + Vue 实现一个应用,简单来说主要是以下几个步骤:
- Vue.js 实现前端页面 & 通过 axios 库请求后台接口获取数据后重新渲染页面;
- Flask & Flask-CORS 提供接口 & 实现跨域服务。
- 打包 Vue.js 文件 & 部署到服务器,通过 index 页面进行访问。
如果需要最终可以在公网访问最终打包好的 Vue 前端界面,则执行以下操作:
- 在服务器实现文件 get_msg.py 文件中配置 app.run(host=’your_ip_address’);
- 配置 Vue axios请求接口的 base_url 为 your_ip_address;
- 通过 npm run build 打包得到最终 dist 文件并部署到服务器中(部署可通过 python -m http.server 进行简单部署,然后通过 http://your_ip_address:8000 端口访问最终的页面)。
Vue 前端实现简单页面
<template>
<div>
<span>{{ serverResponse }} </span>
<button @click="getData">GET DATA</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "my-first-vue",
data: function() {
return {
serverResponse: 'resp'
}
},
methods: {
getData() {
var that = this;
// 对应 Python 提供的接口,这里的地址填写下面服务器运行的地址,本地则为127.0.0.1,外网则为 your_ip_address
const path = 'http://127.0.0.1:5000/getMsg';
axios.get(path).then(function (response) {
// 这里服务器返回的 response 为一个 json object,可通过如下方法需要转成 json 字符串
// 可以直接通过 response.data 取key-value
// 坑一:这里不能直接使用 this 指针,不然找不到对象
var msg = response.data.msg;
// 坑二:这里直接按类型解析,若再通过 JSON.stringify(msg) 转,会得到带双引号的字串
that.serverResponse = msg;
alert('Success ' + response.status + ', ' + response.data + ', ' + msg);
}).catch(function (error) {
alert('Error ' + error);
})
}
}
}
}
</script>
项目简记
项目目录结构
- back_flask/app.py 代码
from flask import Flask, jsonify, request
from flask_cors import CORS
app = Flask(__name__,
template_folder="../front_vue/dist",
static_folder="../front_vue/dist/static")
app.config.from_object(__name__)
CORS(app, resources={r'/*': {'origins': '*'}})
RESOURCES = [
{
'sn': 'SSNI-1003',
'teacher': '楚留香',
'learnt': True
},
{
'sn': 'SSNI-100045',
'teacher': '令狐冲',
'learnt': False
},
{
'sn': 'HHNI-10037',
'teacher': '王语嫣',
'learnt': True
},
{
'sn': 'TUNF-4565',
'teacher': '霍青桐',
'learnt': False
}
]
@app.route('/resources', methods=['GET', 'POST'])
def all_res():
response_object = {'status': 'success'}
if request.method == 'POST':
post_data = request.get_json()
sn = post_data.get('sn')
for r in RESOURCES:
if r['sn'] == sn:
response_object = {'status': 'failed'}
response_object['message'] = '资源编号已存在, 添加失败!'
break
else:
RESOURCES.append({
'sn': sn,
'teacher': post_data.get('teacher'),
'learnt': post_data.get('learnt')
})
response_object['message'] = '资源添加成功!'
else:
response_object['resources'] = RESOURCES
return jsonify(response_object)
@app.route('/resources/<sn>', methods=['PUT', 'DELETE'])
def single_res(sn):
response_object = {'status': 'success'}
if request.method == 'PUT':
post_data = request.get_json()
if remove_res(sn):
response_object['message'] = '资源更新成功!'
else:
response_object['message'] = '新加资源成功!'
RESOURCES.append({
'sn': sn,
'teacher': post_data.get('teacher'),
'learnt': post_data.get('learnt')
})
elif request.method == 'DELETE':
if remove_res(sn):
response_object['message'] = '删除资源成功!'
response_object['status'] = 'success'
else:
response_object['message'] = '该资源不存在!'
response_object['status'] = 'failed'
return jsonify(response_object)
def remove_res(sn):
for r in RESOURCES:
if r['sn'] == sn:
RESOURCES.remove(r)
return True
return False
@app.route('/open', methods=['GET'])
def open_door():
return jsonify(u'芝麻开门!')
@app.route('/ping-tong', methods=['GET'])
def ping_pong():
return jsonify(u'Hello World Flask_Vue 前后端分离!') # (jsonify返回一个json格式的数据)
if __name__ == '__main__':
app.config['JSON_AS_ASCII'] = False
app.run(debug=True)
- front_vue/src/components/Alert.vue
<template>
<div>
<b-alert :variant="variant" show>{{ message }}</b-alert>
<br>
</div>
</template>
<script>
export default {
props: ['message', 'variant']
}
</script>
- front_vue/src/components/Door.vue
<template>
<div class="container">
<button type="button" class="btn btn-primary">{{ msg }}</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Door',
data () {
return {
msg: ''
}
},
methods: {
getMessage () {
const path = `http://localhost:5000/open`
axios.get(path)
.then((res) => {
this.msg = res.data
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
})
}
},
created () {
this.getMessage()
}
}
</script>
- front_vue/src/components/HelloWorld.vue
<template>
<div class="container">
<button type="button" class="btn btn-primary">{{ msg }}</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Hello World FlaskVue!'
}
},
methods: {
getMessage () {
const path = `http://localhost:5000/ping-tong`
axios.get(path)
.then((res) => {
this.msg = res.data
})
.catch((error) => {
// eslint-disable-next-line
console.error(error)
})
}
},
created () {
this.getMessage()
}
}
</script>
- front_vue/src/components/Resources.vue
<template>
<div class="container bg-dark">
<div class="row">
<div class="col-sm-10">
<h1><img src="../assets/logo.png" alt="学习资源"></h1>
<hr><br><br>
<alert :message="message" :variant="alertvariant" v-if="showMessage"></alert>
<button type="button" class="btn btn-info btn-sm" v-b-modal.res-modal>添加资源</button>
<br><br>
<table class="table table-hover text-white">
<thead>
<tr>
<th scope="col">编号</th>
<th scope="col">老师</th>
<th scope="col">已学习</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(r, index) in resources" :key="index">
<td>{{ r.sn }}</td>
<td>{{ r.teacher }}</td>
<td>
<span v-if="r.learnt">是</span>
<span v-else>否</span>
</td>
<td>
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-warning btn-sm"
v-b-modal.res-update-modal
@click="editRes(r)">
修改
</button>
<button
type="button"
class="btn btn-danger btn-sm"
@click="onDeleteRes(r)">
删除
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<b-modal ref="addResModal"
id="res-modal"
title="添加新资源"
hide-footer>
<b-form @submit="onSubmit" @reset="onReset" class="w-100">
<b-form-group id="form-sn-group"
label="编号:"
label-for="form-sn-input">
<b-form-input id="form-sn-input"
type="text"
v-model="addResForm.sn"
required
placeholder="输入编号">
</b-form-input>
</b-form-group>
<b-form-group id="form-teacher-group"
label="老师:"
label-for="form-teacher-input">
<b-form-input id="form-teacher-input"
type="text"
v-model="addResForm.teacher"
required
placeholder="输入老师姓名">
</b-form-input>
</b-form-group>
<b-form-group id="form-learnt-group">
<b-form-checkbox-group v-model="addResForm.learnt" id="form-checks">
<b-form-checkbox value="true">已学习?</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
<b-button type="submit" variant="primary">提交</b-button>
<b-button type="reset" variant="danger">重置</b-button>
</b-form>
</b-modal>
<b-modal ref="editResModal"
id="res-update-modal"
title="Update"
hide-footer>
<b-form @submit="onSubmitUpdate" @reset="onResetUpdate" class="w-100">
<b-form-group id="form-sn-edit-group"
label="编号:"
label-for="form-sn-edit-input">
<b-form-input id="form-sn-edit-input"
type="text"
v-model="editForm.sn"
required
readonly>
</b-form-input>
</b-form-group>
<b-form-group id="form-teacher-edit-group"
label="老师:"
label-for="form-teacher-edit-input">
<b-form-input id="form-teacher-edit-input"
type="text"
v-model="editForm.teacher"
required
placeholder="输入老师姓名">
</b-form-input>
</b-form-group>
<b-form-group id="form-learnt-edit-group">
<b-form-checkbox-group v-model="editForm.learnt" id="form-checks">
<b-form-checkbox value="true">已学习?</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
<b-button-group>
<b-button type="submit" variant="primary">更新</b-button>
<b-button type="reset" variant="danger">取消</b-button>
</b-button-group>
</b-form>
</b-modal>
</div>
</template>
<style>
html,
body {
margin: 0;
padding: 0;
background-color:#343a40;
}
</style>
<script>
import axios from 'axios'
import Alert from './Alert.vue'
export default {
data () {
return {
resources: [],
addResForm: {
sn: '',
teacher: '',
learnt: []
},
editForm: {
sn: '',
teacher: '',
learnt: []
},
message: '',
alertvariant: '',
showMessage: false
}
},
components: {
alert: Alert
},
methods: {
getResources () {
const path = `http://localhost:5000/resources`
axios.get(path)
.then((res) => {
this.resources = res.data.resources
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
})
},
addRes (payload) {
const path = `http://localhost:5000/resources`
axios.post(path, payload)
.then((res) => {
this.message = res.data.message
var success = res.data.status
if (success === 'failed') {
this.alertvariant = 'danger'
} else if (success === 'success') {
this.alertvariant = 'success'
} else {
this.alertvariant = 'info'
}
this.showMessage = true
this.getResources()
})
.catch((error) => {
// eslint-disable-next-line
console.log(error)
this.getResources()
})
},
editRes (resource) {
this.editForm = resource
},
removeRes (sn) {
const path = `http://localhost:5000/resources/${sn}`
axios.delete(path)
.then((res) => {
this.message = res.data.message
var success = res.data.status
if (success === 'failed') {
this.alertvariant = 'danger'
} else if (success === 'success') {
this.alertvariant = 'success'
} else {
this.alertvariant = 'info'
}
this.showMessage = true
this.getResources()
})
.catch((error) => {
// eslint-disable-next-line
console.error(error)
this.getResources()
})
},
onDeleteRes (r) {
this.removeRes(r.sn)
},
initForm () {
this.addResForm.sn = ''
this.addResForm.teacher = ''
this.addResForm.learnt = []
this.editForm.sn = ''
this.editForm.teacher = ''
this.editForm.learnt = []
},
onSubmit (evt) {
evt.preventDefault()
this.$refs.addResModal.hide()
let learnt = false
if (this.addResForm.learnt[0]) learnt = true
const payload = {
sn: this.addResForm.sn,
teacher: this.addResForm.teacher,
learnt
}
this.addRes(payload)
this.initForm()
},
onSubmitUpdate (evt) {
evt.preventDefault()
this.$refs.editResModal.hide()
let learnt = false
if (this.editForm.learnt[0]) learnt = true
const payload = {
sn: this.editForm.sn,
teacher: this.editForm.teacher,
learnt
}
this.updateRes(payload, this.editForm.sn)
},
updateRes (payload, sn) {
const path = `http://localhost:5000/resources/${sn}`
axios.put(path, payload)
.then((res) => {
this.message = res.data.message
this.alertvariant = 'success'
this.showMessage = true
this.getResources()
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
this.getResources()
})
},
onReset (evt) {
evt.preventDefault()
this.$refs.addResModal.hide()
this.initForm()
},
onResetUpdate (evt) {
evt.preventDefault()
this.$refs.editResModal.hide()
this.initForm()
this.getResources()
}
},
created () {
this.getResources()
}
}
</script>
- front_vue/src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Resources from '../components/Resources.vue'
import Door from '../components/Door.vue'
import HelloWorld from '../components/HelloWorld.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Resources',
component: Resources
},
{
path: '/ping-pong',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/open',
name: 'Door',
component: Door
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
- front_vue/src/App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
- front_vue/src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'bootstrap/dist/css/bootstrap.css'
import BootstrapVue from 'bootstrap-vue'
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
Vue.prototype.axios = axios
Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.use(BootstrapVue)
/* eslint-disable no-new */
new Vue({
router,
render: h => h(App)
}).$mount('#app')
- front_vue/src/views/About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
- front_vue/src/views/Home.vue
<template>
<div class="home">
<img alt="Vue logo" src="../src/assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
components: {
HelloWorld
}
}
</script>
本文来自博客园,作者:愺様,转载请注明原文链接:https://www.cnblogs.com/wyh0923/p/15854427.html