对于单页应用中如何监听 URL 变化的思考

周末开发了一个在 GitHub 中给 repo 增长自定义备注的 chrome 扩展。javascript

开发这个扩展的缘由是我在 GitHub 中所 star 的项目实在太多了(截止目前 671 个),有的项目过个几天回来看就忘了为何 star 了,有的轮子想找的时候发现忘记叫什么了,这么多一个个找实在浪费时间。因而我一直在想有这么个工具,能够自定义对 GitHub 中的项目进行备注,而后能够根据备注进行搜索,因而便有了这个扩展。css

如需安装体验请点击 GitHub Remarks 进行安装,源码移步 github-remarks(没啥参考价值)。java

固然本文并非讲这个扩展的制做过程,而是在这过程当中碰到的一个问题。jquery

以个人 GitHub 帐户举例,在 https://github.com/hanzichi?t... 页面,我须要插入一些 dom,使得页面以下:git

clipboard.png

乍一想,彷佛很简单,在这个页面插入 content.js 就行,在 manifest.json 中增长配置:github

{
  "matches": ["*://github.com/.*tabs=stars"],
  "js": ["content-scripts/repoDetail.js"],
  "css": [],
  "run_at": "document_end"
 }

可是问题来了,直接打开页面 https://github.com/hanzichi?t... 的,可是那个页面并无参照的 dom 结构(好把咱们须要的 dom 插入)。因此问题彷佛就转为了,在 pjax 页面,如何可以监听到页面 url 的变化web

首先一个很简单的方案是,弄个定时器循环监听,可是太耗费性能了,passchrome

由于页面是 pjax 跳转,因此 hashchange 这样的事件天然也用不上;而 pushstate 事件是监听浏览器前进后退的,因此也并不符合场景json

有个想法很好,重写 pushState 事件(代码来自 How to detect when HTML5's history.pushState() is called?):浏览器

(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);

可是由于 chrome 扩展和源代码之间并不共享做用域,因此重写的 history.pushState 方法在原来页面其实并无改变,因此也没什么卵用

而后从 chrome API 入手,发现有个 chrome API 能够检测当前 url 改变的 tab(参考 [Chrome extension WebNavigation listener for hash change
](https://stackoverflow.com/que...):

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  if (changeInfo.url) {
      console.log('Tab %d got new URL: %s', tabId, changeInfo.url)
  }
})

这段代码是在 background.js 中,而后一旦侦测到 URL 变化,background.js 和 content.js 通讯告知便可。

可是,可是,也有问题,url 一改变确实能监听到,可是 chrome 扩展须要在相应 dom ready 后才能做用(才能在以前 dom 的基础上插入新的 dom),你没法检测到何时 dom 已经准备就绪了,因此在这里加个定时器延迟做用是可行的,时间须要本身估算下

这个方法不是很优雅,最后想到,pjax 的过程当中,dom 确定有变化,因此监听 dom 变化是否可行?答案是能够的:

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
  let url = location.href 
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

observer.observe(document.getElementById('js-pjax-container'), {
  childList: true
})

$(document).ready(() => {
  let url = location.href 
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

写到最后,其实并非监听了 URL,而是监听了 dom 的变化。

最后的最后,意外在 so 上找到 这个答案 彷佛就是对应个人问题,大概看了下,除了我举例的几个方案外,其实我遗漏了最简单的一个方案,既然页面使用了 pjax,那么直接监听 pjax:complete 事件便可:

$(document).on('pjax:complete', function() {
  let url = location.href 
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

忽然让人感受,有时候 "真相" 每每老是那么简单。

+ 鸟宿池边树,僧敲月下门
- 鸟宿池边树,僧推月下门
相关文章
相关标签/搜索