从浏览器跳转到前端框架的路由实现

1,901 阅读7分钟

回忆

还记得jquery时代吗,那时前端三大框架还没流行,前端开发者疯狂查着jquery的API文档来进行dom操作,还记得这个地址吗 jquery.cuishifeng.cn/ 是不是很有感觉!(想了解前端历史的,请看我这篇文章前端技术发展 juejin.cn/post/684490…

记得在学校里刚开始写网页的时候,只是对JavaScript学了个大概,然后就开始学jquery进行网页开发了。

如果你曾经也是jquery开发者,看到这块代码时,杯中的水开始颤抖,啊!我的青春啊!

那时毕业后的大半年写前端就只用了 jquery ,没有现在这些前端框架提供的工程化,模块化,组件化(其实有,但我还没学没用,而且我进的第一个公司那时我是公司的第一个前端,没有前辈的教导,自己摸索)。写前端就用了js(jquery),css,html,来进行项目的工程文件目录结构搭建。想想那段日子可真累啊!

引子

那时写网页时遇到个需求:

假设一个页面左侧是若干导航链接,右侧是内容,同时导航时只有右侧的内容需要更新,那么刷新整个页面无疑是浪费的(原本每个导航链接都是a标签href跳转网页)。这时我们可以使用 AJAX 来拉取右面的数据。但是如果仅仅这样,地址栏是不会改变的,用户无法前进、后退,也无法收藏当前页面或者把当前页面分享给他人;搜索引擎抓取也有困难。

这时,查到 HTML5 的 History API 就可以解决这个问题。

思路:

  • 首先绑定click事件。当用户点击一个链接时,通过preventDefault函数防止默认的行为(页面跳转),同时读取链接的地址(如果有jQuery,可以写成 $(this).attr('href')),把这个地址通过pushState塞入浏览器历史记录中,再利用 AJAX 技术拉取(如果有 jQuery,可以使用$.get方法)这个地址中真正的内容,同时替换当前网页的内容。

  • 为了处理用户前进、后退,我们监听popstate事件。当用户点击前进或后退按钮时,浏览器地址自动被转换成相应的地址,同时popstate事件发生。在事件处理函数中,我们根据当前的地址抓取相应的内容,然后利用 AJAX 拉取这个地址的真正内容,呈现,即可。

  • 最后,整个过程是不会改变页面标题的,可以通过直接对document.title赋值来更改页面标题。

代码类似于这样:

window.history.pushState(null, null, "/profile/");

History API简单应用

pushState 方法

这套 API 提供一种「人为操纵」浏览器历史记录的方法。

浏览器历史记录可以看作一个「栈」。栈是一种后进先出的结构,可以把它想象成一摞盘子,用户每点开一个新网页,都会在上面加一个新盘子,叫「入栈」。用户每次点击「后退」按钮都会取走最上面的那个盘子,叫做「出栈」。而每次浏览器显示的自然是最顶端的盘子的内容。

var state = {
    id: 2,
    name: "profile"
};
window.history.pushState(state, "My Profile", "/profile/");
// 它可以接收三个参数,按顺序分别为:
// 1.一个对象或者字符串,用于描述新记录的一些特性。这个参数会被一并添加到历史记录中,以供以后使用。这个参数是开发者根据自己的需要自由给出的。
// 2.一个字符串,代表新页面的标题。当前基本上所有浏览器都会忽略这个参数。
// 3.一个字符串,代表新页面的相对地址。

popstate 事件

当用户点击浏览器的「前进」、「后退」按钮时,就会触发popstate事件。你可以监听这一事件,从而作出反应。

window.addEventListener("popstate", function(e) {
    var state = e.state;
    // do something...
});
// 这里e.state就是当初pushState时传入的第一个参数。例如,在我们的例子中,有:
// e.state.id == 2;
// e.state.name == "profile";

replaceState 方法

有时,你希望不添加一个新记录,而是替换当前的记录(比如对网站的 landing page),则可以使用replaceState方法。这个方法和pushState的参数完全一样。

Location的hash简单应用

hash也是一种改变浏览器url而不会刷新页面的方案

url中#(hash)的作用

1.锚点定位

“#”代表网页中的一个位置。其右面的字符,就是该位置的标识符

比如:juejin.cn/post/684490…

浏览器读取这个URL后,会自动滚动至comment评论位置区域。

为网页位置指定标识符,有两个方法。

  • 一是使用锚点
<a name="comment"></a>
  • 二是使用id属性
<div id="comment"></div>

所以当我们使用的时候不指定标识符,网页就不会定位,但又能得到我们想要的改变浏览器url而不会刷新页面

2.HTTP请求不包括'#'后的数据

‘#’是用来指导浏览器动作的,对服务器端完全无用。所以,HTTP请求中不包括#。

比如,访问下面的网址, juejin.cn/post/684490… 我们打开F12查看network的all,看第一条,我们发现并没有#comment这部分数据传参

3.改变#会改变浏览器的访问历史

history是浏览器的历史记录,location是浏览器当前url信息(hash就是其中的值),他们都是浏览器BOM提供的api

onhashchange事件

这是一个HTML 5新增的事件,当#值发生变化时,就会触发这个事件。

function hashHandler() {
  console.log('The hash has changed!');
}

window.addEventListener('hashchange', hashHandler, false);

前端路由

这块内容都是找的网上的文章,网上资料很多,学习了两篇 juejin.cn/post/684490… juejin.cn/post/684490…

1.什么是前端路由

路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由,当接收到客户端发来的 HTTP 请求,就会根据所请求的相应 URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有 URL 的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。它的好处与缺点非常明显:

  • 好处:安全性好,SEO 好;
  • 缺点:加大服务器的压力,不利于用户体验,代码冗合不好维护;

也正是由于后端路由还存在着自己的不足,前端路由才有了自己的发展空间。对于前端路由来说,路由的映射函数通常是进行一些 DOM 的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。

2.什么是 SPA

SPA 是 single page web application 的简称,译为单页Web应用。

简单的说 SPA 就是一个WEB项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。

3.从传统页面到spa视图

  • 在传统的网站设计中,每个HTML文件都是一个完成的HTML页面,涵盖了完整的HTML结构。
  • 在 SPA 的应用设计中,一个应用只有一个HTML文件,在HTML文件中包含一个占位符(即图中的 container),占位符对应的内容由每个视图来决定,对于 SPA 来说,页面的切换就是视图之间的切换。

4.路由实现

前端路由不是什么新技术,我们只是使用了后端使用的路由这个名称,其实他就是对浏览器的hash模型和history模式的封装(各个前端框架都封装了各自的路由,我们查阅文档按照规则规范使用就行)。

所以当我们知道浏览器的hash和history后就能明白一些之前我们觉得神奇的地方。

我们不造轮子,但我们得知道这个轮子出来的目的及意义,基于什么原理。如果你对这个轮子很感兴趣,那你可以阅读源码,学习里面的代码思路,设计模式,但不要鉴于已有的东西再造一个,没有意义。

如果你想了解一些路由的简单封装,点击前面贴出的两篇文章地址,学习一下。深入学习请看三大框架路由源码,自行摸索。