SPA
单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
单页面应用的好处是不必使得用户在加载新页面时感觉到闪烁刷新的效果,有更好的用户体验。当然,单页面应用也有其问题,例如首屏渲染问题以及SEO问题,但如果构建的是类似于应用型的web页面的话,SPA的好处是远远大于其弊端的。
Hexo
Hexo 是一个静态页面生成框架,一般用来其来搭建博客或者文档型的网站。
Hexo => SPA
如何在Hexo的基础上转换为SPA呢?基本的思路就是,监管所有的点击跳转链接,阻止默认行为采用ajax调用的方式获取文档然后替换当前页面。为了使替换时的影响较小,当前文档中数据没有变化的元素就不用替换了,只替换变化的部分。例如,一个采用Hexo的文档型的网站,一般而言包括三个部分header,nav,以及article,header及nav部分是不用变的,只需要替换article部分即可。需要注意的事,在替换后需要注意article内部的事件,需要重新绑定一遍,同时也需要处理nav部分的一些事件,比如focus的变化及样式上的变化,以及其它的一些变化,比如页面title的变化。这样当用户点击左侧nav时,只有article内部内容的替换,看起来就像一个应用一样。但这样修改还有两个问题,一是当用户点击后退时,浏览器不能很好的处理,我们希望记录每一次用户的点击跳转,当用户点击后退时能够回到上一次点击时的页面,另外一个问题是地址栏的没有变化,我们希望每一次用户点击跳转时地址栏也显示相应页面的URL。这时就需要用到History API了.
History 对象包含用户(在浏览器窗口中)访问过的 URL。
History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问。
属性
History.length 只读
History.scrollRestoration
允许Web应用程序在历史导航上显式地设置默认滚动恢复行为。此属性可以是自动的(auto)或者手动的(manual)。
History.state 只读
返回一个表示历史堆栈顶部的状态的值。这是一种可以不必等待 popstate 事件而查看状态而的方式。
方法
History.back()
前往上一页, 用户可点击浏览器左上角的返回按钮模拟此方法. 等价于 history.go(-1).
History.forward()
在浏览器历史记录里前往下一页,用户可点击浏览器左上角的前进按钮模拟此方法. 等价于 history.go(1).
History.go()
通过当前页面的相对位置从浏览器历史记录( 会话记录 )加载页面。
History.pushState()
按指定的名称和URL(如果提供该参数)将数据push进会话历史栈,数据被DOM进行不透明处理;你可以指定任何可以被序列化的javascript对象。
History.replaceState()
按指定的数据,名称和URL(如果提供该参数),更新历史栈上最新的入口。这个数据被DOM 进行了不透明处理。
这里我们需要用到History.pushState()
这个方法接收三个参数,第一个state,记录页面状态,第二个title,并不是页面title,感觉没有什么用,第三个是页面URL。在页面变化时执行History.pushState()把将要跳转的页面记录到history中,地址栏中的地址就会同时改变。同时,需要在window.onpopstate中监听后退事件,在后退时也通过ajax方法获取article内容。如果想要减少请求,还可以将ajax获取到的文档缓存下来,当用户点击新URL时再去获取,如果用户点击的是一个已经访问过的URL时,从本地获取。
代码
让我们总结一下,我们需要改变的是如下几个部分(代码中引入了jQuery):
- 阻止点击链接的默认行为
1 | e.preventDefault(); |
- ajax获取文档内容
1 | // href 跳转url |
成功替换文章内容之后需要重新绑定文章内部的事件,同时对一些样式(例如选中的样式)进行改变。
ajax失败则直接通过改变url的方式刷新页面。
- 页面title变化
1 | doc.title = $(res).filter('title').html(); |
- window.history.pushState()
- 页面url变化
- 记录跳转页面信息到历史记录中,当用户点击后退时能够正确响应
1 | win.history.pushState(href, '', href); |
- window.onpopstate() 处理用户点击后退/前进时的逻辑
1 | $(window).on('popstate', function (e) { |
如上代码中,getArticle封装了获取文章的action,加入callback是考虑到获取文章内容之后如果url中包含锚点,页面应该滑动到锚点所在处。