前端:将网站打造成单页面应用SPA
 

前言

不知你有没有发现,像Github、百度、微博等这些大站,已经不再使用普通的a标签做跳转了。他们大多使用Ajax请求替代了a标签的默认跳转,然后使用HTML5的新API修改了Url,你可以在F12的Network面板里发现这个秘密。

这项技术并没有特别标准的学名,大家都称呼为Pjax,意为PushState + Ajax。这并不完全准确,因为还有Hash + Ajax等方法,但为了方便,我们下文还是统称为Pjax。

为什么要这么做?

Pjax是一个优秀的解决方案,你有足够多的理由来使用它:

  • 可以在页面切换间平滑过渡,增加Loading动画。
  • 可以在各个页面间传递数据,不依赖URL。
  • 可以选择性的保留状态,如音乐网站,切换页面时不会停止播放歌曲。
  • 所有的标签都可以用来跳转,不仅仅是a标签。
  • 避免了公共JS的反复执行,如无需在各个页面打开时都判断是否登录过等等。
  • 减少了请求体积,节省流量,加快页面响应速度。
  • 平滑降级到低版本浏览器上,对SEO也不会有影响。

原理呢?

Pjax的原理十分简单。
1. 拦截a标签的默认跳转动作。
2. 使用Ajax请求新页面。
3. 将返回的Html替换到页面中。
4. 使用HTML5的History API或者Url的Hash修改Url。

HTML5 History API

我们来看看HTML5在History里增加了什么:

history.pushState(state, title, url)

pushState方法会将当前的url添加到历史记录中,然后修改当前url为新url。请注意,这个方法只会修改地址栏的Url显示,但并不会发出任何请求。我们正是基于此特性来实现Pjax。它有3个参数:

  • state: 可以放任意你想放的数据,它将附加到新url上,作为该页面信息的一个补充。
  • title: 顾名思义,就是document.title。不过这个参数目前并无作用,浏览器目前会选择忽略它。
  • url: 新url,也就是你要显示在地址栏上的url。

history.replaceState(state, title, url)

replaceState方法与pushState大同小异,区别只在于pushState会将当前url添加到历史记录,之后再修改url,而replaceState只是修改url,不添加历史记录。

window.onpopstate 事件
一般来说,每当url变动时,popstate事件都会被触发。但若是调用pushState来修改url,该事件则不会触发,因此,我们可以把它用作浏览器的前进后退事件。该事件有一个参数,就是上文pushState方法的第一个参数state。

一个实例:

这里我们以daipig为例,打开daipig,地址栏是http://www.daipig.com 。接下来打开F12 Console,输入:

history.pushState({ a: 1, b: 2 }, null, "http://www.daipig.com/abcdefg");

可以发现,url已经变成我们输入的url了,但页面并没有刷新,也没有发出任何请求。现在再输入history.state,就可以看到我们刚刚传过来的第一个参数state了。

这时点击后退,url会回到www.daipig.com,同样是没有刷新。只不过后退的时候其实是触发了window.onpopstate事件的。

详细文档可以查阅MDN: https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_...

怎么完整的实现Pjax?

Pjax的原理上文已经讲了,并不复杂。我实现了一个比较粗糙的Pjax库,已经能满足不少需求,如果你有兴趣,可以上Github帮忙完善一下代码。地址是:https://github.com/Coffcer/coffce-pjax 。

完整的代码见Github,这里我们只谈需要注意的一些地方。

匹配选择器

要实现Pjax,难免就会有匹配选择器的需求。你需要判断当前点击的元素,是否匹配指定选择器。这里我给出一个兼容至IE8的解决方法:

// 判断element是否匹配选择器selector
 function matchSelector(element, selector) {
     var match = 
         document.documentElement.webkitMatchesSelector ||                  document.documentElement.mozMatchesSelector || 
         document.documentElement.msMatchesSelector || // 兼容IE8及以下浏览器 
         function(selector, element) {

// 这是一个好方法,可惜IE8连indexOf都不支持
           // return Array.prototype.indexOf.call(document.querySelectorAll(selector), this) !== -1;

if (element.tagName === selector.toUpperCase()) return true; 
          var elements = document.querySelectorAll(selector), 
          length = elements.length;

while (length--) { 
             if (elements[length] === this) return true; 
          } 
          return false; 
     };  
   // 重写函数自身,使用闭包keep住match函数,不用每次都判断兼容 
   matchSelector = function(element, selector) { return match.call(element, selector); }; return             matchSelector(element, selector);
}
 
// 验证一下
matchSelector(document.getElementById("abc"), "#abc"); // true
matchSelector(document.querySelector("a"), "p"); // false

在现代浏览器上,优先使用原生的matchesSelector方法来判断,在IE8及以下的浏览器里,循环document.querySelector的结果集,依次对比。

这个方法利用了闭包,然后重写自身,只有在第一次调用时需要判断加哪个前缀执行哪个方法,其后都是调用了闭包的match函数。

不支持HTML5 PushState的浏览器怎么办?

IE6到IE9是不支持pushState的,要修改Url,只能利用Url的Hash,也即是#号。

你可以随意找个网站试一下,在url后面加上#号和任意内容,页面并不会刷新。此时点击后退也只会回到上一条#号,同样不会刷新。

那么我们只需把pushState(新url)换成localtion.hash = 新url,把onpopstate事件换成onhashchange事件就可以兼容IE了。
QQ音乐,网易云音乐等就是使用这种方式。

现成的库

https://github.com/Coffcer/coffce-pjax ,欢迎PR和Star。

前端:将网站打造成单页面应用SPA的更多相关文章

  1. (转)前端:将网站打造成单页面应用SPA

    前端:将网站打造成单页面应用SPA(一) Coffce 680 6月19日 发布 推荐 6 推荐 收藏 85 收藏,3.1k 浏览 前言 不知你有没有发现,像Github.百度.微博等这些大站,已经不 ...

  2. PushState+Ajax实现简单的单页面应用SPA

    http://www.helloweba.com/view-blog-386.html 单页面应用(Single Page Application)简称SPA,使用SPA构建的应用优点有用户体验好.速 ...

  3. 单页面应用SPA架构

    个人认为单页面应用的优势相当明显: 前后端职责分离,架构清晰:前端进行交互逻辑,后端负责数据处理. 前后端单独开发.单独测试. 良好的交互体验,前端进行的是局部渲染.避免了不必要的跳转和重复渲染. 当 ...

  4. 使用Angular构建单页面应用(SPA)

    什么是SPA?看下图就是SPA: 下面说正经的,个人理解SPA就是整个应用只有一个页面,所有的交互都在一个页面完成,不需要在页面之间跳转. 单页面的好处是更快的响应速度,更流畅的用户体验,甚至和桌面应 ...

  5. 单页面应用SPA和多页面应用MPA

    单页面应用(SinglePage Web Application,SPA) 只有一张Web页面的应用,是一种从Web服务器加载的富客户端,单页面跳转仅刷新局部资源 ,公共资源(js.css等)仅需加载 ...

  6. [后端人员耍前端系列]KnockoutJs篇:使用WebApi+Bootstrap+KnockoutJs打造单页面程序

    一.前言 在前一个专题快速介绍了KnockoutJs相关知识点,也写了一些简单例子,希望通过这些例子大家可以快速入门KnockoutJs.为了让大家可以清楚地看到KnockoutJs在实际项目中的应用 ...

  7. react_app 项目开发 (3)_单页面设计_react-router4

    (web) 利用 react-router4 实现 单页面 开发 SPA 应用 ---- (Single Page Web Application) 整个应用只有 一个完整的页面 单击链接不会刷新页面 ...

  8. Vue-router进阶、单页面应用(SPA)带来的问题

    一 . vue-router 进阶 回顾学过的vue-router,并参考官方文档学习嵌套路由等路由相关知识. 二 . 单页面应用(SPA)带来的问题 1 . 虽然单页面应用有优点 , 但是,如果后端 ...

  9. angular(3)服务 --注入---自定义模块--单页面应用

    ng内部,一旦发生值改变操作,如$scope.m=x,就会自动轮询$digest队列,触发指定的$watch,调用其回调函数,然后修改dom树. 干货:https://github.com/xufei ...

随机推荐

  1. JS实现斐波那契数列的几种方法

    斐波那契数列指的是这样一个数列:1.1.2.3.5.8.13.21.34.…… 前两项为1,从第三项起,每一项等于前两项的和,即F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n& ...

  2. SignalR—实例

    本例子将讲述使用SignalR实现页面实时显示数据,而不是需要用户刷新页面或使用Ajax轮询才能实现实时显示数据. 1.使用NuGet安装SignalR. 2.创建Hub类,起名为testHub. 3 ...

  3. mysql设置编码格式--支持中文

    创建table的时候就使用utf8编码 在每次创建表的时候都在最后加上 character set = utf8就可以很好的支持中文 create table xxx ( id int auto_in ...

  4. 从零构建Flink SQL计算平台 - 1平台搭建

    一.理想与现实 Apache Flink 是一个分布式流批一体化的开源平台.Flink 的核心是一个提供数据分发.通信以及自动容错的流计算引擎.Flink 在流计算之上构建批处理,并且原生的支持迭代计 ...

  5. 剑指offer-字符的所有组合,复制复杂链表,二叉树中和为某一值的路径

    字符的所有组合 描述: 输入一个字符串,求这个字符串中的字符的所有组合.如:"abc",组合为"a" "b" c" "a ...

  6. PMP--1.5 项目管理描述

    项目所处的环境将影响每个项目管理过程的实施方式以及项目制约因素的优先顺序. 一. 管理一个项目的过程 ​管理一个项目通常包括(但不限于): 1. 识别项目需求 2. 处理相关方的各种需要.关注和期望 ...

  7. Educational Codeforces Round 32 E 二分

    题意:从数组中选几个(任意),使他们的和modm的值最大 题解:我一开始是直接暴力写,然后就会t 其实这题可以用二分的方法写,一半数组的值用来遍历,一般数组的值用来查询. 二分查询就能把时间继续缩短 ...

  8. robotframework报错

    问题一:RobotFramework RIDE在运行测试用例时无法打开谷歌浏览器,原因是1.安装ride时没有安装chromedriver驱动   2.驱动版本与本机浏览器不兼容 解决方法:在pyth ...

  9. CF1311E Construct the Binary Tree

    膜这场比赛的 \(rk1\) \(\color{black}A\color{red}{lex\_Wei}\) 这题应该是这场比赛最难的题了 容易发现,二叉树的下一层不会超过这一层的 \(2\) 倍,所 ...

  10. 用Excel排值班表用到的几个公式

    用Excel排值班表用到的几个公式 最近,疫情得到了一定的缓解,但还不能放松.所以,各单位都加强值班.那就得排值班表.提到的表当然要用Excel,为什么?因为Excel中的公式真得能让我们提高工作效率 ...