一、什么是SPA应用
SPA的主要思想是利用js动态地将内容加载到当前页面,而不需要从服务器加载整个页面。会给你一种桌面应用的感觉。
所有的 HTML
、JavaScript
和 CSS
都可以在第一次加载应用程序时从服务器中下载,而不是像传统应用那样每次加载都要重新下载所有的资源文件。
另一个要点是,主页或内容永远不会重新加载,但是 hash
或 HTML5
history API
,您仍然拥有不同的url和浏览器历史记录。
二、它能带来什么好处
让我们来说一说SPA应用的优势:
SPA只有第一次需要加载所有的资源到本地,后面的请求都只会加载必要的资源,这大大节省了资源下载和页面渲染的时间。
页面加载资源减少了,页面渲染的速度更快了,用户自然会感觉到更好的交互体验。
当然,凡事有利就有弊,现在我们来说一下SPA应用的缺点:
三、如何实现一个SPA
通过 hash
来实现一个SPA应用:
目录结构
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Single Page Application</title> </head> <body> <ul> <li><a href="#home">Home</a></li> <li><a href="#about">About</a></li> </ul> <div id="app">
</div> <script src="js/route.js"></script> <script src="js/router.js"></script> <script src="js/app.js"></script> </body> </html>
|
Route JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 'use stict';
function Route(name, htmlName, defaultRoute) { try { if(!name || !htmlName) { throw 'error: name and htmlName params are mandatories'; } this.constructor(name, htmlName, defaultRoute); } catch (e) { console.error(e); } }
Route.prototype = { name: undefined, htmlName: undefined, default: undefined, constructor: function (name, htmlName, defaultRoute) { this.name = name; this.htmlName = htmlName; this.default = defaultRoute; }, isActiveRoute: function (hashedPath) { return hashedPath.replace('#', '') === this.name; } }
|
Router JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| 'use strict';
function Router(routes) { try { if (!routes) { throw 'error: routes param is mandatory'; } this.constructor(routes); this.init(); } catch (e) { console.error(e); } }
Router.prototype = { routes: undefined, rootElem: undefined, constructor: function (routes) { this.routes = routes; this.rootElem = document.getElementById('app'); }, init: function () { var r = this.routes; (function(scope, r) { window.addEventListener('hashchange', function (e) { scope.hasChanged(scope, r); }); })(this, r); this.hasChanged(this, r); }, hasChanged: function(scope, r){ if (window.location.hash.length > 0) { for (var i = 0, length = r.length; i < length; i++) { var route = r[i]; if(route.isActiveRoute(window.location.hash.substr(1))) { scope.goToRoute(route.htmlName); } } } else { for (var i = 0, length = r.length; i < length; i++) { var route = r[i]; if(route.default) { scope.goToRoute(route.htmlName); } } } }, goToRoute: function (htmlName) { (function(scope) { var url = 'views/' + htmlName, xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { scope.rootElem.innerHTML = this.responseText; } }; xhttp.open('GET', url, true); xhttp.send(); })(this); } };
|
初始化路由
app.js
里面初始化路由:
1 2 3 4 5 6 7 8 9 10 11
| 'use strict';
(function () { function init() { var router = new Router([ new Route('home', 'home.html', true), new Route('about', 'about.html') ]); } init(); }());
|
通过调用自执行的 init
函数,定义了两条路由,并且将home设置为默认路由。
四、总结
这篇文章展示了实现SPA应用的实践,也希望能帮助你理解现代前端框架在处理SPA单页应用的思想。
全文完。