SPA的简单实现

单页面应用程序(SPA)的想法是创建一个流畅的浏览体验,就像是运行本地桌面应用程序。页面必需的所有代码只用加载一次,其内容通过JavaScript动态改变。如果SPA一切都正确,页面不会刷新,除非用户手动刷新。

目前有许多单页面应用程序框架,比如Backbone、Angular、React。我们前端得需要花大量时间和精力不断学习和巩固这些东西(更不用说要兼容使用过时框架编写的旧代码)。如果你的应用程序的不是太复杂,创建简单的单页面应用程序其实也不太难。本文将讲述我在项目中乃至的SPA简单实现。

单页面应用程序的核心技术是ajax。现在很多网站( facebook, twitter) 都支持这样的一种浏览方式, 当你点击一个站内的链接的时候, 不是做页面跳转,而是只是站内页面刷新。 这样的用户体验比起整个页面都闪一下来说好很多。 其中有一个很重要的组成部分,这些网站的ajax刷新是支持浏览器历史的。刷新页面的同时,浏览器地址栏位上面的地址也是会更改,用浏览器的回退功能也能够回退到上一个页面。也就是说,我们要像Angular等框架一样自己实现一个路由。我们该如何做呢?

一、SPA的一个思想就是无刷新加载,首先我们通过动态设置Location对象的锚点,来实现动态加载模板片段局部刷新。看下面代码:

var pills = $('.nav-pills');
// 改变锚点
pills.on('click', 'a', function() {
  var href = $(this).attr('href');
  if (href == '#') return;
  location.hash = href;
});
if (location.hash == '') {
  pills.find('li a:first').click();
}

点击前进后退或导航菜单项都会改变锚点,锚点为空时默认选中一级导航菜单第一项。锚点的改变会触发hashchange事件,接下来我们来监听它,看下面代码:

$(window).on('hashchange', function() {
    var hash = $.trim(location.hash).split('-')[0];
    // 设置一级导航菜单选中状态
    route.setActive(pills, hash);
    // 加载导航菜单模板
    route.loadTemplate($('#mainContent'), hash, function() {
      hash = location.hash;
      // 设置二级导航菜单选中状态
      route.setActive($('.nav-tabs'), hash);
      $('#subContent').load('tpl/' + hash.substring(1) + '.html');
    });
  }).trigger('hashchange');

在hashchange事件处理函数中,我们将获取到的Location对象的锚点通过“-”分隔符解析成一个数组。如果数组元素只有一个,则只对应一级导航菜单项,要加载的html片段的文件名为该元素的值;如果元素有两个,第一个元素对应一级导航菜单项要加载的html片段的文件名,锚点除去“#”部分对应二级导航菜单项加载的代码html片段的文件名。比如说点击锚点#menu1会加载menu1.html,而点击锚点#menu1-1会依次加载menu1.html和menu1-1.html。架构应该遵循规则优先,当然这个规则可以由你自己来定,对小组开发成员讲清楚就行了。对于简单SPA的路由,一般两级就足够了,多余二级最好考虑使用Angular这些成熟的框架。

route.js提供了两个工具方法。loadTemplate方法用来加载导航菜单模板,setActive方法用来设置导航菜单选中状态。代码详见:https://github.com/szriafan/simple-spa/

二、SPA的另一个思想就是按需加载。我们其用的js和css代码放在index.html中加载,而html片段(模板)会在该文件中加载自己所需的特定js和css代码。这样既然实现了按需加载,又实现了分模块化开发,避免了js和css的全局污染。我在整个项目是在加入了gurnt配置,主要是方便管理模板的静态资源。

需要说明一下的是这个简单架构主要是用于PC端口,为了兼容低版本浏览器也没有使用pushState来代替Location对象。可以考虑在移动项目中使用PJAX(Ajax + pushState)。

另外,我发现开源项目https://github.com/defunkt/jquery-pjax也提供了类似功能。不过它要在服务端设置带有X-PJAX头标识,这一点比较麻烦。

Github:https://github.com/szriafan/simple-spa

最终效果:https://szriafan.github.io/simple-spa/app

发表评论