开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情
-
我们通过两种方式来实现轮播图,这两种方式其实就是我们使用框架的时候,路由的底层实现,通过
history
和hash
这两种方式来实现, 这两种方式都可以实现刷新页面保存状态 -
让我们先了解以下,这两种方式都是怎么实现的
一、 使用 history 实现历史记录
- 使用 history.pushState(参数1,参数2,参数3) 存储对应参数, 参数1是要存的数据,是字符串格式,参数2和参数3是字符串格式,对应的名字
- history.pushState() 事件绑定给需要拿到历史记录的事件中, 将对应的数据存入第一个参数
- history.state 得到pushState中存储的第一个参数
- 使用window 监听 'popstate' 事件, 当点击上一页或下一页的时候触发这个事件
- 事件触发以后会得到 histort.state, 就是每一次历史记录, 也就是对应事件触发的时候存储的数据
二、 使用 hash 实现历史记录
- 使用window 监听 'hashchange' 事件, 当点击上一页下一页的时候,触发这个事件
- 触发事件以后,能够拿到上一页对应的锚点信息,使用锚点后面的信息处理页面,如轮播图
- 使用location.hash.slice(1) 拿到对应锚点的 id 值, 第一位是#,第二位是数字
- 下面咱们来看一下这两种方式,都分别有什么优缺点? 这也是面试的时候比较常问的问题了,面试官一般会问: 实现路由的原理了解吗? 了解, 我们也不要等着面试官继续问,都是什么请说一下,这种被动提问的方式是面试官最讨厌的。 我们可以直接说出来这两种方式, 实现原理就是我上面写的了, 每个分别是侦听了什么事件, 这个点答上来其实就可以了,当然,流程能全部答上来也是更好的,然后再把这二者的区别说一下就ok了。
二者的区别
- hash 就是指 url 后面的 # 号以及后面的字符,history没有带#,外观上比hash 模式好看些
- hash 能兼容到 IE8 , history 只能兼容到 IE10
- hash 值的改变不会导致浏览器向服务器发出请求,因为hash只出现在 URL 中,history模式的 URL 要和 后端一致,刷新会从新请求(前进和后退不会),后端没有及时响应 或没有对应路径路由的处理 会报 404, 所以一般后端要处理匹配不到的时候 应该重定向到 index 页面
- pushState()设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中
- pushState()可额外设置title属性供后续使用
- 下面是我们实现轮播图的一个示例,我会先粘贴 css 和 html 代码, js代码 单独来, 因为是两个逻辑
先上 css 和 html
<style>
body{
margin: 0;
}
#crousel{
width: 100vw;
height: 45vw;
position: relative;
}
#crousel>img{
width: 100%;
height: 100%;
display: block;
position: absolute;
opacity: 0;
transition: all 0.5s;
}
#crousel>ul{
position: absolute;
list-style: none;
margin: 0;
padding: 0;
top: 45vw;
}
#crousel>ul>li{
width: 20%;
float: left;
}
#crousel>ul img{
width: 100%;
border:2px solid transparent;
box-sizing: border-box;
}
</style>
<body>
<div id="crousel">
<img class="showImg" src="./img/a.jpg">
<img class="showImg" src="./img/b.jpg">
<img class="showImg" src="./img/c.jpg">
<ul>
<li><a href="#0"><img class="icon" src="./img/a.jpg"></a></li>
<li><a href="#1"><img class="icon" src="./img/b.jpg"></a></li>
<li><a href="#2"><img class="icon" src="./img/c.jpg"></a></li>
</ul>
</div>
</body>
- 以上我使用了三种图片,这个图片大家可以在自己本地随便找三张 替换掉就可以了
下面上 hash js 的实现
<script>
var icons,showImgs;
var prevList={};
init();
function init(){
var index=Number(location.hash.slice(1)) || 0;
icons=Array.from(document.getElementsByClassName("icon"));
showImgs=Array.from(document.getElementsByClassName("showImg"));
changePrev(index,"icon","borderColor");
changePrev(index,"showImg","opacity");
window.addEventListener("hashchange",hashChangehandler);
}
function hashChangehandler(e){
var index=Number(location.hash.slice(1));
changePrev(index,"icon","borderColor");
changePrev(index,"showImg","opacity");
}
// 这两个逻辑其实差不多,上面的逻辑都是用来操作历史的, 大家可能对这一步会有一些疑问,prevList
// 这个其实就是有一个暂存区,用来存储我们当前选中的信息的,每一次历史记录的切换,或者点击事件,都
// 会执行这里,当前展示的图片样式进行处理
function changePrev(index,name,styleName){
if(prevList[name]){
prevList[name].style[styleName]=styleName==="borderColor" ? "transparent" : 0;
}
prevList[name]= name==="icon" ? icons[index] : showImgs[index];
prevList[name].style[styleName]=styleName==="borderColor" ? "red" : "1";
}
</script>
下面上 history js 的实现
<script>
var icons,showImgs;
var prevList={ };
init();
function init(){
var index=history.state || 0;
icons=Array.from(document.getElementsByClassName("icon"));
showImgs=Array.from(document.getElementsByClassName("showImg"));
icons.forEach(function(item){
item.addEventListener("click",clickHandler);
});
history.replaceState(index,"showimg");
changePrev(index,"icon","borderColor");
changePrev(index,"showImg","opacity");
// popstate只有点击历史回退和前进时,才会触发popstate事件
window.addEventListener("popstate",popstateHandler);
}
function popstateHandler(e){
changePrev(history.state,"icon","borderColor");
changePrev(history.state,"showImg","opacity");
}
function clickHandler(e){
var index=icons.indexOf(this);
history.pushState(index,"showimg");
changePrev(index,"icon","borderColor");
changePrev(index,"showImg","opacity");
}
function changePrev(index,name,styleName){
if(prevList[name]){
prevList[name].style[styleName]=styleName==="borderColor" ? "transparent" : 0;
}
prevList[name]=name==="icon" ? icons[index] : showImgs[index];
prevList[name].style[styleName]=styleName==="borderColor" ? "red" : "1";
}
</script>