Vue + Flask 实现单页面应用

简介: 今天使用我们一起来尝试,使用 Vue + Flask 搭建一个简单的单页面应用。

前端


环境配置

首先安装 vue

npm install vue

创建 vue 工程

# 使用 webpack 打包工具初始化一个名为 frontend 的工程
vue init webpack frontend

安装依赖

# 进入工程目录
 cd frontend
 # 安装 vue-router
 npm install vue-router --save-dev
 # 安装 element-ui
 npm i element-ui -S
 # 安装 SASS 加载器
 npm install sass-loader node-sass --save-dev
 # 安装依赖
npm install

启动工程

1npm run dev

此时,一个最简 vue 应用就完成了。

我们看一下 src 文件夹,这里就是我们写前端代码的地方了

image.png

如下文件的作用

  • assets:用于存放资源文件
  • components:用于存放 Vue 功能组件
  • views:用于存放 Vue 视图组件
  • router:用于存放 vue-router 配置
  • api:存放编写的 api 调用代码
  • config:用于存放一些公共配置,如后端 url 等
  • utils:公共方法
  • App.vue:组件模板
  • main.js:项目的入口文件

下面我们就简单实现一个登陆功能,来进一步理解下各个文件的作用。


添加代码

首先处理配置信息,在 config 文件夹中创建 url.js 文件

const devUrl = 'http://127.0.0.1:9980';
//const proUrl = 'http://apiUrl.com';
export default{
    apiUrl: devUrl,
    apiPrefix: 'api',
    gitHub: ''
}

在 api 文件夹中创建 https.js 文件

import axios from 'axios'
 import qs from 'qs'
 import Config from '../config';
 axios.defaults.timeout = 5000;
 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; 
 axios.defaults.baseURL = '';
 function buildApiUrl(url) {
  return `${Config.apiUrl}/${Config.apiPrefix}/${url}`;
}
axios.interceptors.request.use((config) => {
    if(config.method == 'post'){
        config.data = qs.stringify(config.data);
    }
    return config;
}, (error) => {
    console.log('error params')
    return Promise.reject(error);
}
);
axios.interceptors.response.use((res) => {
    if(!res.data.success) {
        return Promise.resolve(res);
    }
    return res;
}, (error) => {
    console.log('Network error')
    return Promise.reject(error);
}
);
//返回一个Promise(发送post请求)
export function fetchPost(url, params) {
    let apiUrl = buildApiUrl(url);
    return new Promise((resolve, reject) => {
        axios.post(apiUrl, params)
            .then(response => {
                resolve(response);
            }, err => {
                reject(err);
            })
            .catch((error) => {
                reject(error)
            })
    })
}
////返回一个Promise(发送get请求)
export function fetchGet(url, param) {
    let apiUrl = buildApiUrl(url);
    return new Promise((resolve, reject) => {
        axios.get(apiUrl, {params: param})
            .then(response => {
                resolve(response)
            }, err => {
                reject(err)
            })
            .catch((error) => {
                reject(error)
            })
    })
}
export default {
    fetchGet,
    fetchPost
}

这里封装了 axios 的 post 和 get 请求。

在 views 下面创建首页视图 Main.vue

<template>
     <div>
        首页
     </div>
 </template>
 <script>
     export default{
         name: "Main"
    }
</script>
<style>
</style>

在 views 下面创建登陆视图 Login.vue

<template>
    <div>
      <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
        <h3 class="login-title">欢迎登录</h3>
        <el-form-item label="账号" prop="username">
          <el-input type="text" placeholder="请输入账号" v-model="form.username"/>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input type="password" placeholder="请输入密码" v-model="form.password"/>
       </el-form-item>
       <el-form-item>
         <el-button class="login-button" type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
       </el-form-item>
     </el-form>
     <el-dialog
       title="温馨提示"
       :visible.sync="dialogVisible"
      width="30%"
       :before-close="handleClose">
       <span>请输入账号和密码</span>
       <span slot="footer" class="dialog-footer">
         <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
       </span>
     </el-dialog>
     <el-dialog
       title="温馨提示"
       :visible.sync="dialogVisible1"
       width="30%"
       :before-close="handleClose">
       <span>错误的账号或密码</span>
      <span slot="footer" class="dialog-footer">
         <el-button type="primary" @click="dialogVisible1 = false">确 定</el-button>
       </span>
     </el-dialog>
   </div>
 </template>
 <script>
 import https from '../api/https.js'
   export default {
     name: "Login",
     data() {
       return {
         form: {
           username: '',
           password: ''
         },
         // 表单验证,需要在 el-form-item 元素中增加 prop 属性
         rules: {
           username: [
             {required: true, message: '账号不可为空', trigger: 'blur'}
           ],
           password: [
             {required: true, message: '密码不可为空', trigger: 'blur'}
           ]
         },
         // 对话框显示和隐藏
         dialogVisible: false,
         dialogVisible1: false,
       }
     },
     methods: {
       onSubmit(formName) {
         // 为表单绑定验证功能
         this.$refs[formName].validate((valid) => {
              var username = this.form['username'];
              var pwd = this.form['password'];
              var login_info = {username: username, password: pwd};
           if (valid) {
               https.fetchPost('login', login_info).then((data) => {
                              console.log(data.data['code'])
                              if (data.data['code'] == 200) {
                                  this.$router.push("/home");
                              } else {
                 this.dialogVisible1 = true;
                 return false;
               }
                          })
             // 使用 vue-router 路由到指定页面,该方式称之为编程式导航
             //this.$router.push("/main");
           } else {
             this.dialogVisible = true;
             return false;
           }
         });
       },
       handleClose() {
       }
     }
   }
 </script>
 <style lang="scss" scoped>
    .login-button {
        text-align: center;
    }
  .login-box {
    border: 1px solid #DCDFE6;
    width: 350px;
    margin: 180px auto;
    padding: 35px 35px 15px 35px;
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    box-shadow: 0 0 25px #909399;
  }
  .login-title {
    text-align: center;
    margin: 0 auto 40px auto;
    color: #303133;
  }
</style>

修改 router 下面路由函数 index.js

import Vue from 'vue'
 import Router from 'vue-router'
 import HelloWorld from '@/components/HelloWorld'
 import Login from '@/views/Login'
 import Main from '@/views/Main'
 Vue.use(Router)
 export default new Router({
  routes: [
   {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
        {
            // Main 页面
            path: '/main',
            name: 'Main',
            component: Main
        },
        {
            // 登陆页面
            path: '/login',
            name: 'Login',
            component: Login
        },
  ]
})

修改 App.vue 文件

<template>
   <div id="app">
     <!--<img src="./assets/logo.png">-->
     <router-view/>
   </div>
 </template>
 <script>
 export default {
  name: 'App'
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

最后在配置入口文件 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 Vue from 'vue'
 import App from './App'
 import router from './router'
 import VueRouter from 'vue-router'
 // 导入 elementUI
 import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 导入 axios
import axios from 'axios'
import QS from 'qs'
Vue.prototype.$axios = axios
Vue.prototype.qs = QS
Vue.config.productionTip = false
Vue.use(VueRouter)
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  //components: { App },
  //template: '<App/>'
    render: h => h(App)
})

下面我们启动我们的前端程序

npm run dev

如果看到类似的

Your application is running here: http://localhost:8080

说明我们的前端代码构建成功。

现在我们在浏览器中打开上面的地址,就可以得到页面如下:

image.png


后端


后端代码,我准备用 flask + flask_restful 来搭建

class LoginView(Resource):
     def post(self):
         try:
             username = request.get_json()['username']
             pwd = request.get_json()['password']
             user = User.query.filter_by(username=username).first()
             if user is not None and user.verify_password(pwd):
                 login_user(user)
                 return {'code': 200, 'message': 'you are login now!'}
            else:
                return {'code': 403, 'message': 'wrong account or password'}
        except:
            raise
api_login.add_resource(LoginView, '/login')

这里仅仅给出了最核心的 api 代码,还是非常简单的。更多的关于 flask_restful,可以查看其官网。

至此,一个简单的前后端分离的单页面应用就完成了。

看完本文,你可以按着步骤自己实现下。刚接触的伙伴在看的过程中在某些地方可能有疑惑,其实我也研究了好久,也有好多存疑的地方。不过,我还是建议不要妄求每个点都了解的特别清楚,先明白关键点,试着实现一下,回头去看相关资料的时候,也更有感触一些。

相关文章
|
7月前
|
JavaScript 前端开发 搜索推荐
vue -- 单页面应用和多页面应用区别及优缺点
vue -- 单页面应用和多页面应用区别及优缺点
172 0
|
7月前
|
JavaScript NoSQL 前端开发
Vue + Flask 小知识(七)
Vue + Flask 小知识(七)
|
1月前
|
JavaScript UED
"Vue实战技巧大揭秘:一招解决路由跳转页面不回顶部难题,让你的单页面应用用户体验飙升!"
【10月更文挑战第23天】在Vue单页面应用中,点击路由跳转时,默认情况下页面不会自动滚动到顶部,这可能影响用户体验。本文通过一个新闻网站的案例,介绍了如何使用Vue-router的全局前置守卫和`scrollBehavior`方法,实现路由跳转时页面自动滚动到顶部的功能,提升用户浏览体验。
68 0
|
4月前
|
JavaScript 前端开发 UED
揭秘Vue.js高效开发:Vue Router如何让单页面应用路由管理变得如此简单?
【8月更文挑战第30天】随着Web应用复杂性的增加,单页面应用(SPA)因出色的用户体验和高效的页面加载性能而备受青睐。Vue.js凭借简洁的语法和灵活的组件系统成为构建SPA的热门选择,其官方路由管理器Vue Router则简化了路由管理。本文通过实战示例介绍如何利用Vue Router实现高效的SPA路由管理,包括命名路由、动态路由及其核心优势。
41 0
|
4月前
|
JavaScript Docker 容器
【Azure 应用服务】在Azure上部署一套VUE框架的单页面应用,有什么可以参考的文档呢?
【Azure 应用服务】在Azure上部署一套VUE框架的单页面应用,有什么可以参考的文档呢?
|
6月前
|
缓存 JavaScript 前端开发
Vue.js框架在构建单页面应用中的最佳实践
Vue.js最佳实践包括组件化(如单一职责组件)、使用Vuex管理状态、axios处理异步请求、Vue Router进行路由、优化性能(keep-alive及懒加载)和选择UI库配合模块化样式。通过这些方法,开发者能构建高效、可维护的SPA。【6月更文挑战第25天】
180 1
|
7月前
|
JavaScript 前端开发 开发者
vue单页面应用?
vue单页面应用?
35 3
|
7月前
|
JavaScript
在vue中,如何实现SPA单页面应用的路由管理?
在vue中,如何实现SPA单页面应用的路由管理?
69 2
|
7月前
|
JavaScript 前端开发 API
Vue + Flask 实现单页面应用
Vue + Flask 实现单页面应用
|
7月前
|
JavaScript 前端开发 Python
Vue + Flask 小知识(六)
Vue + Flask 小知识(六)