关于HTML5 history API
参考资料:http://diveintohtml5.info/history.html
参考资料为英文,内容较为简单,周末会做简单翻译,方便英语吃力的童鞋。
H5之前,一个URL对应一个页面,无论以何种方式修改地址栏地址,都会导致页面完全刷新,无论跳转页面之间是否有关联。简单来说,H5的history API提供接口,可以使用javascript修改地址栏路径,而不刷新页面。
使用场景
简化说明:未设置静态缓存。
假设后端数据输出模式为:获取数据—读取模板—渲染模板。不同页面之间共享模板,只是数据内容不同。在没有H5的新接口之前,不同页面之间的跳转,则相同的脚本,CSS文件会重新加载,不仅加剧服务器压力,在慢速网络下,用户体验也会变差。在有H5接口之后,不同路径跳转,可以拦截超链接默认行为,通过脚本控制地址栏,通过Ajax加载数据,在前端完成数据呈现,就能避免不必要的资源加载时间。
Demo浅析
- 数据源
/static/articles/index.json
;/static/articles/node.json
;/static/articles/javascript.json
json
// index.json { "title": "Frontend develop language", "content": "FE encounter lots of trouble" } // node.json { "title": "Node theme develop", "content": "whether believe or not, node changes frontend develop way" } // javascript.json { "title": "Javascript theme develop", "content": "maybe javascript just tools, it enhance programmer skill" }
- 模板
/views/article.jade
(临时看的jade,所以简单为上)
jade
doctype html html head title Shuffle History script(src="/libs/superagent.js") script(src="/js/initialize.js") body h3#title | #{title} p#content | #{content} p a(href="/") Index entrance p a(href="/node/" id="node") Node entrance p a(href="/javascript/" id="javascript") Javascript entrance
总计三个页面,共享同一个jade模板。数据源对应关系为/
———- index.json
/node/
— node.json
/javascript
— javascript.json
若直接访问/
,/node/
, javascript
页面,express
读取对应数据源,渲染模板,然后直接输出html
,代码如下:
javascript
/** * Created by jasonborn on 14/11/19. */ var jade = require('jade'); var path = require('path'); var express = require('express'); var app = express(); app.set('env', 'development'); app.engine('jade', jade.__express); app.get('/', function(req, res) { res.render('article.jade', require('./static/articles/index.json'), function(err, html) { res.type('html'); res.send(html); }) }); app.get('/node/', function(req, res) { res.render('article.jade', require('./static/articles/node.json'), function(err, html) { res.type('html'); res.send(html); }) }); app.get('/javascript/', function(req, res) { res.render('article.jade', require('./static/articles/javascript.json'), function(err, html) { res.type('html'); res.send(html); }) }); app.use(express.static(path.join(__dirname, 'static'))); app.listen(1336);
自此实现所谓的MVC模式,然后使用H5 history API,所有的页面都会加载initilize.js
脚本文件:
javascript
/** * Created by jasonborn on 14/11/20. */ window.onload = function() { var title = document.querySelector('#title'); var content = document.querySelector('#content'); var node = document.querySelector('#node'); var javascript = document.querySelector('#javascript'); var resolveContent = function(target) { switch (true) { case target === '/': target = '/articles/index.json'; break; case /node/.test(target): target = '/articles/node.json'; break; case /javascript/.test(target): target = '/articles/javascript.json'; break; } superagent .get(target) .end(function(err, res) { title.textContent = res.body.title; content.textContent = res.body.content; }) }; window.history.replaceState({ "target": window.location.pathname }, null, './'); window.onpopstate = function(event) { resolveContent(event.state.target); }; node.addEventListener('click', function(event) { event.preventDefault(); window.history.pushState({ "target": "/node/" }, null, '/node/'); resolveContent('/node/'); window.onpopstate = function(event) { resolveContent(event.state.target); }; }); javascript.addEventListener('click', function(event) { event.preventDefault(); window.history.pushState({ "target": "/javascript/" }, null, '/javascript/'); resolveContent('/javascript/'); window.onpopstate = function(event) { resolveContent(event.state.target); }; }); };
pushState
修改地址记录,onpopstate
表示通过后退方式退回pushState
后的路径时,执行何种操作。这里是通过Ajax
请求拉取数据,然后呈现数据(较大型项目可能会使用前后端模板引擎来渲染,例如x-template
)。
Angularjs $location html5mode
开启html5mode
,通过config
来配置即可。
javascript
angular.module('shuffleApp', ['ngRoute', 'ngSanitize']) .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { $routeProvider .when('/love', { template:'<a href="/hate">To hate</a><p ng-bind="love"></p>', controller: 'LoveCtrl' }) .when('/hate', { template: '<a href="/love">To love</a><p ng-bind="hate"></p>', controller: 'HateCtrl' }) .otherwise('/love'); $locationProvider.html5Mode(true); }]);
上述配置,在加载入口文件之后,地址栏会变为http://hostname/love
,而不是http://hostname/#/love
。但是存在一个问题,后者可以直接访问,前者也许能访问,也许不能,但不会出现预期的结果,典型结果就是NG中文API站,每次向在新页面打开某个链接,结果就是华丽丽的报错,例如:http://docs.angularjs.cn/api,所以需要做重定向:
javascript
app.get('/love', function(req, res) { res.sendFile(path.join(__dirname, 'static/index.html')); });
具体的重定向上会导致路径设计上的一些问题,所以需要注意。
联系方式
QQ: 491229492
github: https://github.com/bornkiller