flask-pjax
概念
什么是Pjax?
Pjax = history.pushState + Ajax= history.pushState + Async JS + XML(xhr?)
BOM对象history被增强了一波,主要是对历史栈的操作,以前只有replace
, go
之类的,都会跳转并刷新整个页面,现在有了pushState
, replaceState
等等单纯操作历史栈的方法,只是单纯修改历史栈里的内容,没有副作用(页面不会跳转刷新)
关于history对象的更多信息请查看MDN History
Pjax有什么用?
1.最初的单页面应用(SPA)
页面刷新不仅浪费资源(很多同级页面上大部分内容都是相同的,没必要重新加载这些重复内容),还影响用户体验(loadingloadingloading…)。局部刷新能够避免loading影响用户体验,从Ajax概念一出来就有人开始这么做了,把整站做成单页面应用(Single Page App,简称SPA),Ajax请求JSON,再局部刷新呈现数据,最初的SPA存在很多缺点:
-
最大的问题:页面内容与URL不对应。
这是致命的缺点,用户看到的内容与地址栏URL不对应,意味着内容无法分享传播(分享传播太重要了),浏览器前进/后退按钮也无法按照用户预期工作。
-
其次:破坏SEO。
纯Ajax实现的SPA对SEO有极大的消极影响,由于页面上很多内容都是Ajax请求之后js控制呈现的,蜘蛛看不到这些内容,也就根本不会被索引/收录。
2.pretty AJAX URL
为了解决SEO问题,Google提出了一种很丑(虽然名字叫pretty AJAX URL。。)的方法:#!
据说Twitter用过一个月,后来用户反馈说太丑了,再后来就不用了。这种很丑的url确实能解决上述的第2个问题,但是需要搜索引擎配合,Google认可这种方式,Google的蜘蛛会把mydomain.com/index.html#!article1
和mydomain.com/index.html#!article2
当作两个不同的页面对待,服务给这种特殊URL的请求返回对应的页面即可,SEO问题没了。
但第一个问题还在,页面内容与URL仍然不对应,于是有了下面要说的Pjax。
3.Pjax
W3C提出了新的API,增强了history对历史栈的控制能力,能够直接修改地址栏URL,直到这时页面内容与URL才终于能够对应了。
每次局部刷新成功之后都调用history.pushState
同步更新地址栏的URL(维护历史栈),这些URL都对应可以直接访问的页面,每个局部刷新动作都是由a
标签触发的,js拦截默认跳转,再Ajax请求数据呈现数据,用户看到的是局部刷新和流畅的体验,蜘蛛看到的是普通的a
标签页面跳转,页面展示的内容始终与直接访问URL得到的内容一致,致命缺点也不存在了。
如果各个浏览器支持性良好的话,Pjax能够完美支持单页面应用,如果浏览器不支持新API就把它当蜘蛛好了(目前好像没有好用的兼容方案,jq插件也无能为力),至于css/js冲突、内存泄露等等都是SPA本身的问题,成熟的SPA方案应该可以避免这些问题。
为什么要用Pjax?
在不影响SEO的前提下,局部刷新 + 本地缓存能够带来前所未有的快速体验,这是Pjax的绝对优势
。
Pjax适用于非纯移动端的单页面应用,能够实现流畅的用户体验,而且封装好的Pjax组件支持一些额外的功能,比如缓存、本地存储、动画等等,使用起来也很方便。
移动页面一般不存在频繁地大篇幅页面内容更新,而且移动页面很少需要考虑SEO,直接用Ajax即可。
安装
pip install flask-pjax
配置与用法
导入flask_pjax
,创建实例化对象,并进行初始化。
from flask import Flask,render_template
from flask_pjax import PJAXapp = Flask(__name__)
pjax = PJAX()
pjax.init_app(app)@app.route('/')
def index():return "Hello World"if __name__ == "__main__":app.run()
设置基本模板
PJAX_BASE_TEMPLATE = "pjax.html"
创建base.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>基本模板</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
创建pjax.html
{% block content %} {% endblock %}
创建index.html
{% extends pjax('base.html') %}{% block content %}
<div class="container"><p style="color: white;text-align: center">Welcome to the Index page.</p>
</div>{% endblock %}
创建A.html
、B.html
、C.html
{% extends pjax('base.html') %}{% block content %}<div class="container"><p style="color: white;text-align: center">The A page.</p>
</div>{% endblock %}
...
<div class="container"><p style="color: white;text-align: center">The B page.</p>
</div>
....
...
<div class="container"><p style="color: white;text-align: center">The C page.</p>
</div>
...
创建app.py
"""Testing Flask with PJAX."""
from flask import Flask, request, render_template
from flask_pjax import PJAXapp = Flask(__name__)
app.config["PJAX_BASE_TEMPLATE"] = "pjax.html"pjax = PJAX()
pjax.init_app(app)@app.route('/')
def home():return render_template('index.html')@app.route('/A')
def a_pjax():return render_template('A.html')@app.route('/B')
def b_pjax():return render_template('B.html')@app.route('/C')
def c_pjax():return render_template('C.html')if __name__ == '__main__':app.run()
这将为PJAX请求呈现pjax.html,为非PJAX请求呈现基础。
另外指定自定义PJAX基本模板:
{% extends pjax('base.html', pjax='/base/custom_pjax_template') %}