[关闭]
@bornkiller 2015-03-09T10:33:31.000000Z 字数 4342 阅读 4556

Angularjs $location html5mode浅析

angularjs


关于HTML5 history API

参考资料:http://diveintohtml5.info/history.html
参考资料为英文,内容较为简单,周末会做简单翻译,方便英语吃力的童鞋。

H5之前,一个URL对应一个页面,无论以何种方式修改地址栏地址,都会导致页面完全刷新,无论跳转页面之间是否有关联。简单来说,H5的history API提供接口,可以使用javascript修改地址栏路径,而不刷新页面。

使用场景

简化说明:未设置静态缓存。

假设后端数据输出模式为:获取数据---读取模板---渲染模板。不同页面之间共享模板,只是数据内容不同。在没有H5的新接口之前,不同页面之间的跳转,则相同的脚本,CSS文件会重新加载,不仅加剧服务器压力,在慢速网络下,用户体验也会变差。在有H5接口之后,不同路径跳转,可以拦截超链接默认行为,通过脚本控制地址栏,通过Ajax加载数据,在前端完成数据呈现,就能避免不必要的资源加载时间。

Demo浅析

  1. // index.json
  2. {
  3. "title": "Frontend develop language",
  4. "content": "FE encounter lots of trouble"
  5. }
  6. // node.json
  7. {
  8. "title": "Node theme develop",
  9. "content": "whether believe or not, node changes frontend develop way"
  10. }
  11. // javascript.json
  12. {
  13. "title": "Javascript theme develop",
  14. "content": "maybe javascript just tools, it enhance programmer skill"
  15. }
  1. doctype html
  2. html
  3. head
  4. title Shuffle History
  5. script(src="/libs/superagent.js")
  6. script(src="/js/initialize.js")
  7. body
  8. h3#title
  9. | #{title}
  10. p#content
  11. | #{content}
  12. p
  13. a(href="/") Index entrance
  14. p
  15. a(href="/node/" id="node") Node entrance
  16. p
  17. a(href="/javascript/" id="javascript") Javascript entrance

总计三个页面,共享同一个jade模板。数据源对应关系为
/ ---------- index.json
/node/ --- node.json
/javascript --- javascript.json

若直接访问/,/node/, javascript页面,express读取对应数据源,渲染模板,然后直接输出html,代码如下:

  1. /**
  2. * Created by jasonborn on 14/11/19.
  3. */
  4. var jade = require('jade');
  5. var path = require('path');
  6. var express = require('express');
  7. var app = express();
  8. app.set('env', 'development');
  9. app.engine('jade', jade.__express);
  10. app.get('/', function(req, res) {
  11. res.render('article.jade', require('./static/articles/index.json'), function(err, html) {
  12. res.type('html');
  13. res.send(html);
  14. })
  15. });
  16. app.get('/node/', function(req, res) {
  17. res.render('article.jade', require('./static/articles/node.json'), function(err, html) {
  18. res.type('html');
  19. res.send(html);
  20. })
  21. });
  22. app.get('/javascript/', function(req, res) {
  23. res.render('article.jade', require('./static/articles/javascript.json'), function(err, html) {
  24. res.type('html');
  25. res.send(html);
  26. })
  27. });
  28. app.use(express.static(path.join(__dirname, 'static')));
  29. app.listen(1336);

自此实现所谓的MVC模式,然后使用H5 history API,所有的页面都会加载initilize.js脚本文件:

  1. /**
  2. * Created by jasonborn on 14/11/20.
  3. */
  4. window.onload = function() {
  5. var title = document.querySelector('#title');
  6. var content = document.querySelector('#content');
  7. var node = document.querySelector('#node');
  8. var javascript = document.querySelector('#javascript');
  9. var resolveContent = function(target) {
  10. switch (true) {
  11. case target === '/':
  12. target = '/articles/index.json';
  13. break;
  14. case /node/.test(target):
  15. target = '/articles/node.json';
  16. break;
  17. case /javascript/.test(target):
  18. target = '/articles/javascript.json';
  19. break;
  20. }
  21. superagent
  22. .get(target)
  23. .end(function(err, res) {
  24. title.textContent = res.body.title;
  25. content.textContent = res.body.content;
  26. })
  27. };
  28. window.history.replaceState({
  29. "target": window.location.pathname
  30. }, null, './');
  31. window.onpopstate = function(event) {
  32. resolveContent(event.state.target);
  33. };
  34. node.addEventListener('click', function(event) {
  35. event.preventDefault();
  36. window.history.pushState({
  37. "target": "/node/"
  38. }, null, '/node/');
  39. resolveContent('/node/');
  40. window.onpopstate = function(event) {
  41. resolveContent(event.state.target);
  42. };
  43. });
  44. javascript.addEventListener('click', function(event) {
  45. event.preventDefault();
  46. window.history.pushState({
  47. "target": "/javascript/"
  48. }, null, '/javascript/');
  49. resolveContent('/javascript/');
  50. window.onpopstate = function(event) {
  51. resolveContent(event.state.target);
  52. };
  53. });
  54. };

pushState修改地址记录,onpopstate表示通过后退方式退回pushState后的路径时,执行何种操作。这里是通过Ajax请求拉取数据,然后呈现数据(较大型项目可能会使用前后端模板引擎来渲染,例如x-template)。

Angularjs $location html5mode

开启html5mode,通过config来配置即可。

  1. angular.module('shuffleApp', ['ngRoute', 'ngSanitize'])
  2. .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
  3. $routeProvider
  4. .when('/love', {
  5. template:'<a href="/hate">To hate</a><p ng-bind="love"></p>',
  6. controller: 'LoveCtrl'
  7. })
  8. .when('/hate', {
  9. template: '<a href="/love">To love</a><p ng-bind="hate"></p>',
  10. controller: 'HateCtrl'
  11. })
  12. .otherwise('/love');
  13. $locationProvider.html5Mode(true);
  14. }]);

上述配置,在加载入口文件之后,地址栏会变为http://hostname/love,而不是http://hostname/#/love。但是存在一个问题,后者可以直接访问,前者也许能访问,也许不能,但不会出现预期的结果,典型结果就是NG中文API站,每次向在新页面打开某个链接,结果就是华丽丽的报错,例如:http://docs.angularjs.cn/api,所以需要做重定向:

  1. app.get('/love', function(req, res) {
  2. res.sendFile(path.join(__dirname, 'static/index.html'));
  3. });

具体的重定向上会导致路径设计上的一些问题,所以需要注意。

联系方式

QQ: 491229492
github: https://github.com/bornkiller

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注