整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

深度解析各种网站页面跳转方式原理及优劣势对比分析「进阶篇」

黑帽SEO技术不论多么神奇,不管用户看到的页面多么的敏感,但是搜索引擎爬虫看到的永远都是正规的信息,而用户看到的是非常规信息。很明显,这是因为网站页面做了处理,能够分辨出普通用户和搜索引擎,不同的对象调用不同的页面,这个就是网站页面跳转。

什么是网站页面跳转以及为什么要进行页面跳转就不多去细说了,那么怎么进行页面跳转呢。关于网页跳转技术很多,文章也很多,方法代码都很多,但是这些方法差别在那里,那些方法速度更快,那些方法更灵活,那些方法用户能感觉到,为什么能感觉到。夜岛SEO看了一些文章,结合多年的黑帽SEO优化工作经验,通过绘制序列图等办法简单总结一下,换个角度理解一下,希望对大家有所帮助!

本文夜岛重点在于比较http跳转,html跳转,js跳转的工作流程,以及重点分析他们在时间开销上的情况,同时我们重点在于绘制一些图形,然后希望读者能够从图中体会到区别,需要读者体会的地方都用特别区域标识出来了。请大家注意。

一.什么是页面跳转


网站都是由各种各样页面组成,正常情况下A页面里面包含B页面,C页面的链接,用户在浏览A页面过程中,手工人为点击B链接,然后用户浏览器就显示到B页面。这个过程我们就可以叫页面跳转。

黑帽SEO中常用的站群排名技术99.9%是必须要做跳转的,不然排名上去了给客户看什么内容呢。我们考虑如下场景,一个网站由A,B,C页面构成。正常他们有他们自己显示内容。随着时间推移,发现A页面内容应该同C页面内容相同,但是由于A页面已经被大量用户收藏在浏览器的收藏夹中,或者被搜索引擎收录,若是现在取消A页面的地址(就是取消页面),则对用户是个非常糟糕的事情,但是同时维护两个页面A及C又是个麻烦的事情,稍有疏忽就会造成内容不一致,给用户造成困惑,同时搜索引擎也会不认可。

如何保证A地址不取消,同时又准确保证两个页面显示内容相一致?

我们今天要解决的是在没有用户干预下的页面跳转,完成当用户需要显示页面A时,我们给他显示页面C的内容。

另外为了说明问题方便,我们同时也假设页面B内容也指向内容C,只是采用跳转技术不一样,这样我们方便区别两种行为的差别。



如上图中,展示了用户访问服务器获取页面的一个基本过程。图中主要分为两个部分,左侧区域是用户端,右侧区域是服务端,用户端的用户通过手机或者电脑或者智能设备访问服务端页面。

服务端由若干页面构成,这里简化了服务端行为,并且抽象成三个页面A,B,C,正常情况下服务端应该有很多Action对象,Action对象同页面相对应,提供各种服务,我们仅仅简化Page:A,Page:B,Page:C;

用户端就是手机,包括手机操作操作系统,网络层(tcp/ip/udp等),http等协议层,以及浏览器,浏览器内部进行html的解析,css渲染,js执行引擎等等。

典型用户页面浏览行为如下:


1.用户启动浏览器

2.在浏览器地址中输入域名url地址

3.浏览器发起http请求

4.网络层发起tcp请求到服务器,传输http数据包

5.服务器接收到请求后进行处理,然后返回相关页面内容

6.Tcp接收返回数据给http协议解析系统

7.http将返回数据返回浏览器

8.浏览器解析html数据,处理htmlhead

9.根据head处理后续工作

10.解析body数据

11.处理装载事件Onload(已经开始js的执行,在装入数据过程中已经可以执行一些js事件,具体要根据页面以及浏览器特性而定)

12.根据css进行显示,执行js

13.用户进行后续事情

以上仅仅是典型浏览器行为,具体浏览器行为同页面内容、浏览器特性等都有关系,要具体分析。

二.页面跳转的分类


根据上面的图1,我们可以有如下分类方法:

一)以跳转地点发生系统那一侧分为:

服务端跳转


页面跳转发生在服务端,服务端负责将实际内容获取,然后发送给客户端,这个情况下,一般用户不会感觉到跳转的实际行为,因此有些时候我们也不叫做跳转。具体的服务端跳转行为有很多,各个技术都有各自的特点。例如:Struts2基于注解服务端跳转、<request.getRequestDispatcher(“xx.jsp”).forward(request,response)、<jsp:forwardpage=””/>等,php也有自己的放回,总之各自有各自的办法,夜岛就不一一举例了,大家自己去搜索吧。

客户端跳转


跳转行为需要客户单程序参与的一种行为(自然不是用户参与的,那个不是本文讨论的)。在这个过程中,一般用户一定会知道的,浏览器地址栏会发生变化,这个分类比较多,我们专门进行一个分类。

二)用户端跳转中,我们根据跳转行为发生在那个软件层次,分为:http层跳转、应用层跳转;应用层跳转继续分为:htmlhead跳转、js跳转等。

http层跳转


http跳转是指server根据工作情况通过http返回状态码,指示客户端浏览器跳转到相应页面的过程,一般返回码是302.,下面是http302状态码的定义:

Htmlhead头指令跳转

在html代码的head中添加特殊标签,如下

<metahttp-equiv=”refresh”content=”5;url=http://www.heimaoke.com/”/>

表示:5秒之后转到黑帽客网站首页。

这个跳转需要浏览器具体解析html后才能进行,慎重更多时间才能进行,或者情况更复杂。

Js跳转


通过在html代码中添加js代码,通过js代码实现跳转

<scriptlanguage=”javascript”type=”text/javascript”>

window.location.href=”login.jsp?backurl=”+window.location.href;

</script>
这个跳转应该比htmlhead跳转更向后延迟。

服务端跳转流程


页面跳转显示的内容发生在服务端,服务端负责将实际内容获取到,然后发送给客户端。一般用户不会感觉到跳转的实际行为,因此有些时候我们也不叫做跳转

具体工作的参考过程如下:



如上图,用户请求访问PageA,页面A内容指向页面C,相关过程如下:

1.用户通过浏览器访问PageA

2.浏览器通过http处理模块请求GetPageA
3.http处理模块同服务器建立tcp连接,并发出请求获取PageA指令

4.PageA内容指向PageC,通过内部程序将内容C获取到本地

5.PageA接收到PageC的数据后,将数据返回给http模块

6.http模块接收到数据后返回给浏览器

7.浏览器接收到http返回的html数据后,解析html的head

8.处理html的body

9.处理html的onload方法

10.浏览器最后将数据等显示给用户

注意:图中不同斜线的区域

通过server跳转后,用户看到的是PageC的内容,但是浏览器地址栏中地址是PageA的地址。

优点:跳转行为在server进行,一次tcp连接完成相关操作,对用户是透明的,不会造成疑惑。

缺点:对用户隐藏了信息,跳转行为都发生在server端,对server有压力。

server端功能各异,需要分工负责,当用户访问某功能后,需要返回另外一个功能,这个时候没必要把全部功能都放到一个服务器上。

例如:单点登录:用户在某个服务器上登录成功后,一定要在重新跳转到功能服务器上。

网络支付:用户在银行的网站支付完成后,必须重新定向到另外企业应用服务器上。

适用范围:应用内部系统,适当的包含关系时。

http跳转流程


http跳转是指server根据工作情况通过http返回状态码,指示客户端浏览器跳转到相应页面的过程,一般返回码是302,下面是http302跳转的相关参考流程

注意图中,区域,颜色,斜线等等。



如上图,用户请求访问PageB,页面B内容指向页面C,相关过程如下:

1.用户通过浏览器访问PageB

2.浏览器通过http处理模块请求GetPageB

3.http处理模块同服务器建立tcp连接,并发出请求获取PageB

4.PageB内容指向PageC,PageB的处理模块通过http的重定向协议通知客户端程序,通过发送消息,302,以及跳到目的地址等进行

5.http处理模块接收到消息后直接跳转到目标地址,同时通知浏览器(修改地址栏)

6.http处理模块请求PageC页面内容

7.PageC处理模块处理数据,生成html代码,返回数据给http处理模块

8.http处理模块接收到数据后放回数据给浏览器

9.浏览器接收到http返回的html数据后,解析html的head

10.处理html的body

11.处理html的onload方法

12.浏览器最后将数据等显示给用户

优点:响应速度快,在http1.1协议下通过合适的设置可以使用同一个tcp连接,节省网络时间,服务器及用户端都不需要进行额外的数据处理工作,节省时间。

缺点:仅仅能做跳转没有其他功能,基于js及html的跳转可以选择延时跳转,但是302无法选择延时跳转等

适用范围:快速跳转,不需要延时,经常用在两个系统之间跳转等。

Htmlrefresh跳转流程


通过在htmlhead中添加<meta>标签,在标签里指定相关参数,指示浏览器跳转到相应页面,相关跳转必须在http层面将html数据传输给浏览器后,浏览器解释html代码过程中,发现跳转并且根据跳转指令跳转到相应页面。

参考流程如下图:



如上图,用户请求访问PageB,页面B内容指向页面C,相关过程如下:

1.用户通过浏览器访问PageB

2.浏览器通过http处理模块请求GetPageB

3.http处理模块同服务器建立tcp连接并发出请求获取PageB

4.PageB处理模块处理数据,生成html代码,最后将html通过http协议传输回去。

5.http后将数据放回给浏览器,浏览器开始处理html

6.浏览器首先会处理html的head部分,最后发现有跳转的相关指令

7.浏览器根据跳转指令,重新联系http模块,发出获取PageC的指令

8.http通过tcp连接到服务器,获取PageC内容,然后返回给浏览器

9.浏览器接收到http返回的html数据后重新处理html,首先解析html的head

10.处理html的body

11.处理html的onload方法

12.浏览器最后将数据等显示给用户

优点:跳转方式灵活,可以指定延时跳转等等

缺点:可能多次建立tcp连接,在低速网络下效率更低,浪费客户端的时间

Htmljs实现跳转工作流程


最后来看一下js跳转,工作中每个浏览器都有自己的js执行引擎,执行引擎根据js代码,来动态调用浏览器进行跳转,相关参考代码如下:

<scriptlanguage=”javascript”type=”text/javascript”>

window.location.href=”login.jsp?backurl=”+window.location.href;

</script>

具体js跳转过程如下图:



如上图,用户请求访问PageB,页面B内容指向页面C,相关过程如下:

1.用户通过浏览器访问PageB

2.浏览器通过http处理模块请求GetPageB

3.http处理模块同服务器建立tcp连接同server建立连接,并发出请求获取PageB

4.PageB处理模块处理数据,生成html代码,最后将html通过http协议传输回去。

5.http后将数据放回给浏览器,浏览器开始处理html

6.浏览器首先会处理html的head部分,最后会发现有跳转的相关指令

7.浏览器处理html的body,以及js等,最后根据js的指令指示浏览器获取页面C

8.最后根据js的指令指示浏览器获取页面C浏览器会根据跳转指令,重新联系http模块,发出获取PageC的指令

9.http通过tcp连接到服务器,最后获取PageC的内容,然后返回给浏览器

10.浏览器接收到http返回的html数据后重新处理html,首先解析html的head

11.处理html的body

12.处理html的onload方法
13.浏览器最后将数据等显示给用户

优点:跳转方式灵活,可以指定延时跳转等等。

缺点:可能多次建立tcp连接,在低速网络下效率更低,浪费客户端的时间。

使用访问:快速跳转,不需要延时,经常用在两个系统之间跳转等。

页面跳转最糟糕的情况


当A跳转到B时,我们用符号A–>B表示,下面的循环跳转A–>B–>C–>A,会发生什么事情。若是循环跳转仅仅发生在server端,则相关系统会迅速被拖垮。若是循环跳转发生在客户端参与的系统中,很快客户端及server端都会发生问题。

因此循环跳转我们是要严格避免的,解决办法:

1.不跳转,但是不可能完全避免,并且不太可能实现,一般系统都是开放的系统,会不断添加功能,即使当前没有跳转,但是过几个月。。。。。。

2.打破跳转的循环,加强系统的检查力度避免循环跳转的发生。

3.最重要的,监控系统,当发现某个客户端或者系统在单位时间内有过多的访问时,主动断开连接或者拒绝这个客户端的访问等等。这个非常重要,一个好的系统是必须有这个功能的,否则即使没有循环跳转,但是若是用户连续快速访问一个页面也是有很大问题的,例如ie中按下F5键循环刷新页面,若是没有检测机制。。。。

三.总结


每种跳转方法对于用户来讲都带来了内容上的变化,原以为A页面的内容变成C页面内容。跳转的方法有很多,夜岛SEO无法为大家一一列举,当我们使用时如何选择那种类别时,需要弄明白每一种跳转的特点,包括:性能,功能等,根据不同网络情况进行不同的选择,例如有的网络建立tcp连接速度慢,这个时候就适宜选择server端的跳转等。

若是保证系统之间的耦合关系更小,系统之间更灵活则需要采用http方式跳转、js跳转,html跳转等。

有的时候需要在跳转前进行一些判断或者额外的操作等,就是js跳转比较方便,但是也有个浏览器适配的问题。有时候一个js兼容性不好的js代码可能不工作,造成部分用户无法跳转。
更多黑帽SEO技术知识学习教程请访问夜岛SEO技术博客(www.yeadao.com),一个专注于黑帽SEO快速排名技术研究与学习教程分享的网站!

pringMVC默认的参数对象

  • SpringMVC默认的参数对象是指,不用再另行创建,相当于SpringMVC内置对象,可以直接声明并使用
  • 默认的参数对象有:HttpServletRequest,HttpServletResponse,HttpSession,Model,Map,ModelMap
  • 注意:Model,Map,ModelMap和HttpServletRequest对象一样都使用请求作用域,所以在页面跳转时,只可以采用转发方式
  • 如果要采用重定向的方式完成页面跳转而且还要保证数据传递的正确性,只能使用HttpSession

进行携带数据的页面跳转

  • SpringMVC的项目配置和前面SpringMVC博集中(指SpringMVC 02)配置相同
  • webapp/index.jsp:网站的首页,用来向后端发送一个简单的携带数据的get请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index.jsp</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/data.action?name=饺子">携带数据进行页面跳转</a>
</body>
</html>
  • webapp/admin/main.jsp如下:作为请求的最终响应页面,测试在经过页面跳转后,后端放入对应作用域中的数据是否还有效
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>main.jsp</title>
</head>
<body>
<h2>显示页面跳转时携带的数据......</h2>
    <!-- 在经过页面跳转后,在跳转到的页面里,尝试获取之前存放的数据-->
	request: ${requestUser}<br>
	httpSession: ${sessionUser}<br>
	model: ${modelUser}<br>
	map: ${mapUser}<br>
	modelMap: ${modelMapUser}<br>
    
    <!-- 尝试直接获取请求地址中携带的参数数据-->
	param: ${param.name}
</body>
</html>
  • 创建SpringMVC控制器:DataAction。在控制器的action方法中利用SpringMVC内置对象,将数据存放到相应作用域中
package com.example.controller;

import com.example.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
public class DataAction {
    @RequestMapping("/data")
    //这几个参数都是SpringMVC内置的,可以直接声明使用
    public String data(HttpServletRequest request, HttpSession httpSession, Model model, Map<Object, Object> map, ModelMap modelMap){
        
        //User实体类含有两个属性:name(String), age(int)。无参构造方法。全属性的有参构造方法,getter,setter,toString方法
        User user = new User("荷包蛋", 20);
        //将user对象利用各SpringMVC内置对象存放到相应作用域中
        request.setAttribute("requestUser", user);
        httpSession.setAttribute("sessionUser", user);
        model.addAttribute("modelUser", user);
        map.put("mapUser", user);
        modelMap.addAttribute("modelMapUser", user);
        //最后完成页面转发跳转
        return "main";
    }
}
  • 当控制器中的action方法以转发的方式跳转到webapp/admin/main.jsp页面时
  • 部署并启动tomcat进行测试
  • 网站首页(left),响应页面(right):可见对于放入相应作用域中的数据,在经过页面转发跳转后仍然可以获取到之前存放的数据
  • 而且对于param也可以在响应的页面中直接获取到随着前端请求携带而来的数据(携带而来的数据放在请求域中,转发是一次请求,请求域还在,自然可以获取到)

  • 当控制器中的action方法以重定向的方式跳转到webapp/admin/main.jsp页面时
package com.example.controller;

import com.example.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
public class DataAction {
    @RequestMapping("/data")
    public String data(HttpServletRequest request, HttpSession httpSession, Model model, Map<Object, Object> map, ModelMap modelMap){
        User user = new User("荷包蛋", 20);
        request.setAttribute("requestUser", user);
        httpSession.setAttribute("sessionUser", user);
        model.addAttribute("modelUser", user);
        map.put("mapUser", user);
        modelMap.addAttribute("modelMapUser", user);
        
        //最后完成页面的重定向跳转
        return "redirect:/admin/main.jsp";
    }
}
  • 网站首页(left),响应页面(right):可见对于放入相应作用域中的数据,在经过页面重定向跳转后只有session域中可以获取到之前存放的数据
  • 因为本例中的重定向是两次请求,凡是放在第一次请求域中的数据,在第二次请求发起后,之前存放的数据对于第二次请求来说已经失效,无法访问原来存放的数据,而由于浏览器并未关闭,则session域仍然有效,可以正确获取之前存放在session域中的数据

目中经常会出现点击跳转锚点的方法,比如给一个a标签一个href=“#锚点”,然后要跳的锚点给个id=“锚点”,这样就实现简单的跳转,但是这样在url地址栏后面都会出现一个诸如www.csdn.net#锚点,然后你点击给一次后退都是退回上一个选择的锚点url,这里总结一些跳转锚点的方法。

第一种方法,也是最简单的方法是锚点用<a>标签,在href属性中写入DIV的id。如下:

<!DOCTYPE html>

<html>

<head>

<style>

div {

height: 800px;

width: 400px;

border: 2px solid black;

}

h2 {

position: fixed;

margin:50px 500px;

}

</style>

</head>

<body>

<h2>

<a href="#div1">to div1</a>

<a href="#div2">to div2</a>

<a href="#div3">to div3</a>

</h2>

<div id="div1">div1</div>

<div id="div2">div2</div>

<div id="div3">div3</div>

</body>

</html>

这种方法的缺点是点击锚点之后,浏览器的URL会发生变化,如果刷新可能会出现问题。

第二种方法是在js事件中通过window.location.hash="divId"跳转,但地址也会发生变化,感觉跟第一种方法没区别,甚至更麻烦。

第三种方法是用animate属性,当点击锚点后,页面滚动到相应的DIV。接着上面的代码,具体添加如下代码:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.0.min.js"></script>

<script type="text/javascript">

$(document).ready(function() {

$("#div1Link").click(function() {

$("html, body").animate({

scrollTop: $("#div1").offset().top }, {duration: 500,easing: "swing"});

return false;

});

$("#div2Link").click(function() {

$("html, body").animate({

scrollTop: $("#div2").offset().top }, {duration: 500,easing: "swing"});

return false;

});

$("#div3Link").click(function() {

$("html, body").animate({

scrollTop: $("#div3").offset().top }, {duration: 500,easing: "swing"});

return false;

});

});

</script>

注意:运行上面的脚本的之前,先将为锚点增加相应的id,同时去掉href属性。

$("html, body")可以替换为响应的div,如果不起作用,试着给该div增加overflow:scroll属性。

另外,脚本可以进一步优化,自己来试试

这样做的好处是:URL地址不会变,同时点击锚点时会自动响应scroll事件,不需要重新绑定。

缺点是:如果页面复杂的话,偏移值可能会发生变化需要算法辅助。

第四种方法是用js的srollIntoView方法,直接用:

document.getElementById("divId").scrollIntoView();

比如:

document.querySelector("#roll1").onclick = function(){

document.querySelector("#roll1_top").scrollIntoView(true);

}

这里就是点击id是#roll1的元素可以滚动到id是#roll1_top的地方,这里的#roll1和#roll1_top最好是一一对应的,

这种方法的好处,是URL不会变,同时能够响应相应的scroll事件,不需要算法什么的。代码如下:

<html>

<head>

<title>HTML5_ScrollInToView方法</title>

<meta charset="utf-8">

<script type="text/javascript">

window.onload = function(){

/*

如果滚动页面也是DOM没有解决的一个问题。为了解决这个问题,浏览器实现了一下方法,

以方便开发人员如何更好的控制页面的滚动。在各种专有方法中,HTML5选择了scrollIntoView()

作为标准方法。

scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素,

调用元素就可以出现在视窗中。如果给该方法传入true作为参数,或者不传入任何参数,那么

窗口滚动之后会让调动元素顶部和视窗顶部尽可能齐平。如果传入false作为参数,调用元素

会尽可能全部出现在视口中(可能的话,调用元素的底部会与视口的顶部齐平。)不过顶部

不一定齐平,例如:

//让元素可见

document.forms[0].scrollIntoView();

当页面发生变化时,一般会用这个方法来吸引用户注意力。实际上,为某个元素设置焦点也

会导致浏览器滚动显示获得焦点的元素。

支持该方法的浏览器有 IE、Firefox、Safari和Opera。

*/

document.querySelector("#roll1").onclick = function(){

document.querySelector("#roll_top").scrollIntoView(false);

}

document.querySelector("#roll2").onclick = function(){

document.querySelector("#roll_top").scrollIntoView(true);

}

}

</script>

<style type="text/css">

#myDiv{

height:900px;

background-color:gray;

}

#roll_top{

height:900px;

background-color:green;

color:#FFF;

font-size:50px;

position:relative;

}

#bottom{

position:absolute;

display:block;

left;0;bottom:0;

}

</style>

</head>

<body>

<button id="roll1">scrollIntoView(false)</button>

<button id="roll2">scrollIntoView(true)</button>

<div id="myDiv"></div>

<div id="roll_top">

scrollIntoView(ture)元素上边框与视窗顶部齐平

<span id="bottom">scrollIntoView(false)元素下边框与视窗底部齐平</span>

</div>

</body>

</html>

个人建议使用第四种方法。