#
起因
近期一直在帮公司做和产品发布的流程和自动化相关的改进工作,主要是写了一些 Python + bash + Jenkinsfile 来实现 Jenkins Pipeline,效果还算不错,做产品发布比以前省心了不少。于是想着可以再做一个 Web 页面,用来展示和产品发布相关的信息,方便日常的维护工作。刚好看到网上这样一篇文章Full-stack single page application with Vue.js and Flask, 于是打算用类似的方式来实现一个SPA,顺便了解一下前端的Vue.js框架。
术语
SPA - Single Page Application
程序结构
程序的目录结构是这样的:
1 | ├── backend |
backend 目录里是一个 Python 应用负责收集产品与发布相关的信息。
www 目录里主要是一个前端程序 Python 程序提供REST API 和一个index.html静态页面,Vue.js 相关代码都在 index.html 里。
用图说话那基本上就是这样的:
技术点和总结
后台应用的 daemon 实现,直接用了开源项目 python-daemon daemon.py 的源代码。pip 里其实可以直接安装 python-daemon 库, 不过这个是另一个项目实现的,以后想用的话可以先看一下这个文档。
后台collector里用到的fabric也是 Python 里非常有用的库,建立在Invoke (subprocess command execution and command-line features) 和 Paramiko (SSH protocol implementation)之上,用起来感觉很方便,对开发 CMDB 的项目来说很有帮助。
前端的实现是 Python Flask + Vue.js, 由于 Vue.js 模板和 Flask Jinji2 在展示的时候有些预定义的分隔字符的冲突,所以在Flask app对象上需要做下面的处理:
1
2
3
def index():
return render_template('index.html')1
2
3
4
5
6
7
8
9
10
11
12
13class CustomFlask(Flask):
jinja_options = Flask.jinja_options.copy()
jinja_options.update(dict(
block_start_string='{%',
block_end_string='%}',
variable_start_string='(?',
variable_end_string='?)',
comment_start_string='{#',
comment_end_string='#}',
))
app = CustomFlask(__name__)
其实也可以通过其它方式来解决这个问题:
- 不使用 render_template(‘index.html’) 而是通过 nginx 或者 apache 返回 index.html 页面。
- 在Vue 里重定义 delimiters,参考 stackoverflow 上的这个问题
Vue.js 不愧是渐进式的 Javascript MVVM 框架, 容易上手。结合 bootstrap-vue 能够让你很方便地做出一个简单的前端页面。对于像我这种前端小白,以前只会撸 DOM 里那些 getElementById() 和 getElementByClassName() 方法的人来说真是福音。
index.html 里调用 REST API实际上用到的是 axios 框架, 想写出一个好的SPA, 个人感觉在 REST API 的返回内容的设计上也是要有技巧的。目前觉得有两点, 一是返回的JSON在层级上不要太多,否则在客户端处理起来很麻烦, 而是一次性返回尽量多的内容,避免来回调用REST API 太多次,否则很容易导致因为这样的多次调用页面在加载和刷新的时候很慢。