Hilshire / blog

temporary blog
2 stars 0 forks source link

监听 vue 中的路由变化 #38

Closed Hilshire closed 4 years ago

Hilshire commented 4 years ago

先说一下场景,使用iframe嵌入单页应用(hash模式),当iframe中的页面发生变化的时候,需要父页面的菜单跟着变化。因此,直观的需要知道子页面的url发生变化。

由于是单页应用,onload显然是不好使的。一个很容易想到的方法是使用 onhashchange,这样就来到了我们这次问题的中心:如果你尝试一下,就会发现,vuehash改变不是每次都会触发hashchange事件的——准确来说,通过push的更改的路由是不会触发hashchange的。vue-router 底层调用的是history.pushStatehistory.replaceState, 这2个api对url的更新不限于hash, 不会触发hashchange

如果是普通的vue项目,可以通过使用watch监听$route来解决这个问题。但是对于iframe我不是很想做出这样强侵入性的改动,更别说他还要我和父页面交互。

但是我们依然可以做一些事情: How to get notified about changes of the history via history.pushState?

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        // ... whatever else you want to do
        // maybe call onhashchange e.handler
        return pushState.apply(history, arguments);
    };
})(window.history);

window.onpopstate = history.onpushstate = function(e) { ... }

onpopstate的业务范围很狭窄,只处理游览器动作,或者history.back()history.forward()。 经过处理之后,它就可以监听到push发生的变化。

看到上面的代码好像问题已经解决了——且慢!这里有一个前提。我的系统 iframe 是跨域的,而iframe 一旦跨域,那么父页面就无法对子页面做什么,就只能看看而已了。

好在我 iframe 的页面主域名相同,可以简单的用 document.domain 解决

其实这个问题是我的场景下需要嵌入的系统比较多,不然可以简单的用 postMessage 解决,根本不用纠结这么多。

yxzlwz commented 1 year ago

感谢!这个重写 history.pushState 的方法解决了我的问题