SimonZhangITer / MyBlog

:wave: My Personal Blog
210 stars 48 forks source link

使用pushState实现微信“返回”按钮控制单页应用页面的无刷新跳转 #4

Open SimonZhangITer opened 7 years ago

SimonZhangITer commented 7 years ago

相信很多微信开发者都会遇到过这样的问题:为了提高用户体验,把多个页面内容放在一个HTML页面进行展示,通过display属性以及transition动画来实现页面的跳转动画,但是点击微信顶部的“返回”按键之后就会直接跳出整个页面。

所以针对以上的情况,一般的应用的解决方案都是在页面的顶部自己画一个导航栏,需要点击自定义的导航栏来实现返回效果。

弊端

但是不觉得和微信的导航栏放在一起,并且微信上面也有返回,两个返回很别扭吗?

并且对于安卓用户来说,底部的返回键岂不是没用了?万一手抖点到了怎么办?

没关系,使用history可以解决:

History

我们知道,浏览器的跳转以及返回都是存在history历史里面的,它是栈,后进先出。 跳转一个新页面就进栈一个,返回一次就出栈一个。所以我们可以控制它的栈来实现返回的效果

pushState

history提供了一个方法pushState,可以手动的添加页面进栈。

使用语法:history.pushState(state, title, url);

如:

history.pushState({
     "page": "productList"
 }, "首页", "");

replaceState

用新的state和URL替换当前。不会造成页面刷新。语法和pushState相同,该方法一般在首页使用,更改一下首页的参数名,方便监控和判断。

onpopstate

history.go和history.back(包括用户按浏览器历史前进后退按钮)触发,并且页面无刷的时候(由于使用pushState修改了history)会触发popstate事件,事件发生时浏览器会从history中取出URL和对应的state对象替换当前的URL和history.state。通过event.state也可以获取history.state。

window.onpopState = function(){
    var json = window.history.state;//获取当前所在的state
    if (json && json.page == "home") { //page即是pushState时设置的参数
        if (searchParams.sourceUrl && !selected) {
            history.go(-1);
        } else {
            document.querySelector('.product-page').removeCls('show');
            document.querySelector(".visit-hospital-page").classList.remove("show");
        }
    }}

history.go

使用该方法即可实现出栈

history.go(-1);//返回一个页面
history.go(-2);//返回两个页面
以此类推

结合以上所述,即可实现页面的无刷新跳转,希望对大家有帮助

yajore commented 7 years ago

你这样有个问题,就是微信的浏览器的标题不会更改,我这个bug一直没解决(document.title我忘记是在苹果还是安卓上是无效的),上次看到有个人通过iframe的方式实现了,还没去弄,你可以试着找下,把你这个方法实现完美

SimonZhangITer commented 7 years ago

@yajore 你说的对,关于微信在ios上面的标题问题,我之前也研究过,使用iframe也解决不了,有点头疼...

Liqihan commented 7 years ago

@SimonZhangITer 微信在ios上的标题,我试过是可以的,也是使用iframe document.title = title; var mobile = navigator.userAgent.toLowerCase(); // 针对微信 if (/iphone|ipad|ipod/.test(mobile)) { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; // 替换成站标favicon路径或者任意存在的较小的图片即可 iframe.setAttribute('src', 'logo.png'); var iframeCallback = function () { setTimeout(function () { iframe.removeEventListener('load', iframeCallback); document.body.removeChild(iframe); }, 0); }; iframe.addEventListener('load', iframeCallback); document.body.appendChild(iframe); } 这篇文章的方法我昨天试验过,是可以的,但是在ios上有个问题,如果什么都不做,返回还是跳出页面的,但是触碰页面之后就可以指定返回到指定页面去了,不知道是不是bug,楼主有空可以交流下,

SimonZhangITer commented 7 years ago

@Liqihan 奇了怪了,咱俩写的代码一样一样的,但是我记得我之前测试不行的呀-.-

function wxSetTitle(title) {
    document.title = title;
    var mobile = navigator.userAgent.toLowerCase();
    if (/iphone|ipad|ipod/.test(mobile)) {
        var iframe = document.createElement('iframe');
        iframe.style.visibility = 'hidden';
        iframe.setAttribute('src', 'loading.png');
        var iframeCallback = function() {
            setTimeout(function() {
                iframe.removeEventListener('load', iframeCallback);
                document.body.removeChild(iframe);
            }, 0);
        };
        iframe.addEventListener('load', iframeCallback);
        document.body.appendChild(iframe);
    }
}
Liqihan commented 7 years ago

@SimonZhangITer 会不会是iframe src的问题,我完整的图片地址没贴出来,微信很坑的,我们的图片地址放在阿里云的cdn上,还因此被微信给封过,既然代码一样,我想问题会不会出在这里,仅供参考,

xymingjun commented 6 years ago

onpopState 中的 s 是小写 -_-||