之前在开发的时候,一般都是使用 Flask + Jinja 来实现一个服务端渲染的网页。在开发的时候确实也非常方便,Flask 拥有着大量的扩展框架,例如 Flask-login、Flask-WTF等等,可以非常简单快速地搭建一个完善的服务(参考我之前的 友链项目)。

但在开发的过程当中,我也确实发现了许多的问题:在写前端页面的时候,大量的 Jinja 的控制代码参差在 html 模板里面,同时还要写不少的 Javascript 代码,这让模板的代码显得混乱不堪。当项目量级增大,后期的维护堪比灾难。而且 Jinja 使用的是模板继承,常常只能将其分解成为一个基模板和许多子页面模板,这也让整个项目头重脚轻(当然也可能是我掌握的还不够多 xD)。

在今年年初,也受其他同学的影响,咱尝试地去接触了 Vue 这个框架,并喜欢上了它的开发模式和设计理念。不过因为中间去学习了一段时间的机器学习算法,所以有挺长一段时间没有接触。这次也是在许多朋友的帮助下完成了这个 Hello World 的项目,真的非常感谢 ヾ(≧▽≦*)o

这次咱实现的是一个简易的购物车,源码已经放在 github 了。

技术栈:

前端:

  1. vue
  2. vuex
  3. axios

后端

  1. flask
  2. mysql
  3. JWT

下面分析一些技术细节

前后端之间的耦合

一般来说,前端都是使用 axios 来进行请求的。这里咱发现使用 axios.create 返回出来的是一个 Promise 对象,所以咱就直接将请求封装成了一个 Request.js 模块,之后在文件中引用时只要写清楚请求的路由就好啦。

requestForItems : function (config) {
        const instance = axios.create({
            baseURL: url,
            timeout: 100000
        })

        return instance(config)
    }

(这里裁切掉了添加请求头的部分)

之后直接将模块 import 到需要用的地方,然后直接调用就行了。

Request.requestForItems({
        url: "/goods",
        method: "GET"
      }).then(res => ...).catch(err => ...)
    }

这样子写有两个好处。

如果以后需要更改 api-url 之类的,直接修改 Request.js 文件就行。

如果以后 axios 过时了,那只要修改 Request.js,然后同样是返回一个 Promise 对象,就可以继续使用了。而不是需要去一个个文件进行修改。

后端全部采用 REST 风格的 API 接口。Flask 提供了一个非常实用的扩展:flask-restful,可以很简单地搭建出 REST 风格的 api。

后端的表单验证我依然是使用了 Flask-wtf。可能有人觉得前后端分离了就不需要 wtforms了,但其实它所带的验证器还是非常好用的。不过有一点就是需要手动关闭 Flask-wtf 的 csrf 验证。在 app 的设置里将 WTF_CSRF_ENABLED 的值设置为 False 即可。

登录系统与验证

由于采用了前后端分离而停用了 csrf 验证,因此也带来了隐患。于是听从了一些 dalao 的建议,采用了 JWT 来进行身份验证。同样地,flask 也提供了 flask-jwt 的扩展来实现。

用户的用户名和密码输入通过之后,后端会返回一个 JWT,之后我是将 JWT 存储在了 cookies 当中,登陆状态则存储在 vuex 当中。如果要登出,则直接将 JWT 从 cookies 当中抛弃即可。

前端打包

开发完成之后需要将前端的代码打包生成 dist。不过当我把 dist 塞到 flask 里面时发现前端对静态文件的请求全都 404 了。不过这个其实完全可以通过修改 vue.config.js 来进行解决。

module.exports = {
    publicPath:'./',
    assetsDir: 'static',
    indexPath: 'templates/index.html',
    devServer: {
        proxy: {
            '/*': {
                target: "http://localhost:5000/",
                changeOrigin: true
            }
        }
    }
}

这里我直接将其打包生成 flask 所能读取的形式,直接将 dist 塞到 flask 里面,就能正常使用了。

后记

在这里非常感谢 小透明・宸 和 かない 两位 dalao 在项目开发过程当中的帮助,万分感谢 q(≧▽≦q)

这个项目也是咱的一次技术验证,之后就要着手将这些技术放到新博客的开发当中啦!


You Are All Stardust.