前端:将网站打造成单页面应用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. VSTO开发指南(VB2013版) 第二章 Office解决方案介绍

    实例2.1 通过控制台实现对Excel的自动化处理 书本第32页 注:添加两个引用: 第一个:程序集—框架—“System.Windows.Forms 4.0.0.0”第二个:程序集—扩展—“Micr ...

  2. 源码浅析:InnoDB聚集索引如何定位到数据的物理位置,并从磁盘读取

    索引结构概述: MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址.这与Oracle的索引结构相似,比较好理解.那么,常用的Innodb聚集索引结构是怎样的呢? InnoDB的数据文 ...

  3. SRAM结构框图解

    SRAM 即静态RAM.它也由晶体管组成,SRAM的高速和静态特性使它们通常被用来作为Cache存储器.计算机的主板上都有Cache插座. 下图所示的是一个SRAM的结构框图. 由上图看出SRAM一般 ...

  4. 聊聊spring之贯穿全局的重要对象BeanDefinition

    BeanDefinition 在 spring 中贯穿始终,spring 要根据 BeanDefinition 对象来实 例化 bean,只要把解析的标签,扫描的注解类封装成 BeanDefiniti ...

  5. JS的基本概念和数据类型

    什么是 JavaScript 语言 Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ ta ...

  6. Linux学习Day1:开班第一天

    其实这篇博客应该昨天就要写完的,算是补作业吧. 昨天(2020年2月14日)是参加Linux线上培训的第一天,当天培训结束后,老师要求学员每天写一篇博客来记录自己学到的知识,于是就有了这篇博客的诞生. ...

  7. 第1章 JavaScript 简介

    第1章 JavaScript 简介 1.1 JavaScript简史 1.2 JavaScript实现 1.2.1 ECMAScript 1.2.2 文档对象模型(DOM) 1.2.3 浏览器对象模型 ...

  8. MongoDB 添加用户名和密码

    MongoDB 添加用户名和密码 我用的是 mongodb3.6,如果没有的话先安装. sudo apt install mongodb 终端输入mongo,首先添加管理用户, show dbs // ...

  9. .Net Core程序最终只产生1个exe

    1.用VS新建一个.Net Core 命令行程序,程序里面写什么都行,我的是这样的 2.打开程序包控制台,或者使用PowerShell等可以执行dotnet命令的工具,把你的.Net 程序发布(编译) ...

  10. Dictionary的基本用法

    1.创建泛型哈希表,然后加入元素 Dictionary<string,string> openWith=new Dictionary<string, string>(); op ...