beita1991 / blogs

4 stars 0 forks source link

移动端H5单页面应用路由变化与默认返回按钮效果不搭配解决方案 #1

Open beita1991 opened 7 years ago

beita1991 commented 7 years ago

移动端H5单页面应用路由变化与默认返回按钮效果不搭配解决方案

功能与需求

做微信企业应用开发时,业务代码用的是react,路由用的react-router。遇到一种场景,列表页面(A)、选中项详情(B)、编辑功能(C)、选人功能(D),正常效果是 A-->B-->C-->D,然而D操作完后会跳转到B页面。用户点击左上角的微信应用自带返回按钮,这个时候返回到的是D页面,但是这个不是我想要的效果,我希望的是返回到A页面。

分析与提出问题

解决

从分析中可以看出应该与对应的 history Api 有关 ,查看相关 Api 与google了一番,发现了这遍blog 如何监听用户点击浏览器后退按钮。 代吗如下:

        //监听hashchange事件
        window.addEventListener('hashchange', function() {

            //为当前导航页附加一个tag
            this.history.replaceState('hasHash', '', '');
            console.error('==============监听hashchange事件================')
        }, false);

        window.addEventListener('popstate', function(e) {
            if (e.state) {
                //侦测是用户触发的后退操作, dosomething
                //这里刷新当前url
                console.error('侦测是用户触发的后退操作',e.state)
            }
        }, false);

构建一个浏览历史存储对象,每次跳转将数据存储进去

 historyObject: {
     urlList: [],  // 浏览历史
     currentIndex: -1,
 }

这样可以知道浏览器的前进后退,然后将 对应的url与 urlList 中数据作比较,设置对应的index。 回到问题中用户D跳转到B,已知URL只要匹配 urlList 中数据然后找到对应的index,然后执行 history.go 去跳转。考虑到用户刷新操作,所以将数据变更存储到 localStorage 中 之后的工作就维护好这个 historyObject 对象。 丰富后的代码如下:


function urlFilter(url) {
    let index = url.indexOf('_k=');
    if (index > 0) {
        url = url.substring(0, index - 1);
    }
    return url;
}

let HistoryManager = {
    /**
     * 记录 history 列表
     * {
     *     urlList:[],   ['url_a','url_b']
     *     currentIndex: -1,
     * }
     */
    historyObject: {
        urlList: [],
        currentIndex: -1,
    },

    localStorageHistoryKey: 'HISTORY_MANAGER_LIST_KEY',

    // 初始化调用
    initialize() {
        // 初始化的时候判断localStory中是否有相关数据,如果有将其取出
        let historyObject = window.localStorage.getItem(this.localStorageHistoryKey);
        if (historyObject) {
            this.historyObject = JSON.parse(historyObject);
        }

        //监听hashchange事件
        window.addEventListener('hashchange', ()=>this.hashchangeEvent(), false);

        window.addEventListener('popstate', (e)=>this.popstateEvent(e), false);
    },

    hashchangeEvent(){
        window.history.replaceState('hasHash', '', '');
    },

    popstateEvent(e){
        if (e.state) {
            let url = urlFilter(window.location.href);

            let index = this.historyObject.urlList.findIndex((item, i) => item == url);

            this.setHistoryObject({
                currentIndex: index,
                urlList: this.historyObject.urlList,
            });

        }
    },

    removeEventListenter(){
        window.removeEventListener('hashchange',this.hashchangeEvent);
        window.removeEventListener('popstate',this.popstateEvent);

    },

    getHistoryObject(){
        return this.historyObject;
    },

    setHistoryObject(historyObject){
        this.historyObject = historyObject;
        if (window.localStorage.getItem(this.localStorageHistoryKey)) {
            window.localStorage.removeItem(this.localStorageHistoryKey);
        }
        window.localStorage.setItem(this.localStorageHistoryKey, JSON.stringify(this.historyObject));
        window.beitaTestHistory = this.historyObject;
    },

    compareUrl(newUrl, oldUrl){
        if (newUrl && oldUrl) {
            return newUrl === oldUrl;
        }
        throw {errorMessage: "HistoryManager.compareUrl 方法传入参数有误"};
        return false;
    },

    /**
     * 跳转 --这里只是存储数据,跳转是在调用当前方法时候用的 react-router 封装history对象 的 push方法
     */
    navTo(url){

        let {currentIndex, urlList}= this.historyObject;

        if (currentIndex > -1) {
            // 删除当前 currentIndex 后面的数据 再将 url添加进去
            if (currentIndex != (urlList.length - 1))
                urlList = urlList.splice(0,currentIndex+1);

            urlList.push(url);
            this.setHistoryObject({
                currentIndex: currentIndex + 1,
                urlList: urlList,
            })

        } else {
            throw {errorMessage: "未找到当前url"};
        }
    },

    popTo(url){
        url = urlFilter(url);
        let index = this.historyObject.urlList.findIndex((item, i) => item == url);
        this.go(index-this.historyObject.currentIndex);
    },

    /**
     * 替换 --这里只是存储数据,跳转是在调用当前方法时候用的 react-router 封装 history 对象 的 replace 方法
     */
    replaceTo(url){
        url = urlFilter(url);
        let {currentIndex, urlList}= this.historyObject;
        urlList[currentIndex]= url;
        this.setHistoryObject({
            currentIndex: currentIndex,
            urlList: urlList,
        })
    },

    getCurrentHistory(){
        return this.historyObject.urlList[this.historyObject.currentIndex] || undefined;
    },

    getCurrentHistoryIndex(){
        return this.historyObject.currentIndex;
    },

    back(){
        this.go(-1);
    },

    forward(){
        this.go(1);
    },

    go(n){
        window.history.go(n);
    },
};

module.exports = HistoryManager;

总结

maiqingqiang commented 7 years ago

能否详细的讲一下HistoryManager怎么用吗?或者有没有demo之类的?

tedyhy commented 7 years ago

@beita1991 赞👍