Мысли о том, как отслеживать изменения URL-адресов в одностраничных приложениях

внешний интерфейс GitHub JavaScript Chrome

На выходных разработал расширение для Chrome, которое добавляет пользовательские заметки в репозиторий на GitHub.

Причина разработки этого расширения в том, что я пометил слишком много проектов в GitHub (пока 671 проект), а некоторые проекты забыли, почему они помечены, когда возвращаются через несколько дней.Как это называется, пустая трата времени найти так много по одному. Так что я думал о таком инструменте,Проекты в GitHub можно настроить так, чтобы они отмечались, а затем выполнялись поиск по заметкам., так что есть это расширение.

Для опыта установки, пожалуйста, нажмитеGitHub RemarksУстановка, исходный кодgithub-remarks

Конечно, эта статья не о процессе производства этого расширения, а о проблеме, возникшей в процессе.

Возьмите мою учетную запись GitHub в качестве примера, наGitHub.com/Человек ест?Он…page, мне нужно вставить какой-то дом, чтобы страница выглядела так:

На первый взгляд кажется очень простым, достаточно вставить content.js на эту страницу, а в manifest.json добавить конфигурацию:

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

Но проблема приходит, откройте страницу напрямуюGitHub.com/Человек ест?Он…без проблем, но еслиgithub.com/hanzichiНажмите, чтобы перейти кGitHub.com/Человек ест?Он…, так как GitHub использует технологию pjax, content.js на самом деле должен быть загружен на страницу.github.com/hanzichi, но на этой странице нет ссылочной структуры dom (чтобы вставить нужный нам dom). Так что проблема, похоже, в том,Как прослушать изменения URL-адреса страницы на странице PJAX?

Во-первых, очень простое решение — создать цикл таймера для прослушивания, но это слишком требовательно к производительности.

Поскольку страница представляет собой переход pjax, такие события, как hashchange, естественно, не используются, а событие pushstate предназначено для отслеживания браузера для перемещения вперед и назад, поэтому оно не соответствует сцене.

Рекомендуется переопределить событие 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 фактически не изменяет исходную страницу, поэтому он бесполезен.

Затем начните с API Chrome и обнаружите, что существует API Chrome, который может обнаружить текущую вкладку с измененным URL-адресом (см.Chrome extension WebNavigation listener for hash change ):

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 должно работать после того, как соответствующий дом будет готов (чтобы вставить новый дом на основе предыдущего дома), вы не можете определить, когда дом готово, так что сюда можно добавить отсрочку по таймеру, а время надо прикидывать самому

Этот метод не очень элегантный.В конце концов, я подумал, что в процессе 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.

Наконец, я случайно нашел его на такэтот ответКажется, это соответствует моей проблеме. Я, вероятно, посмотрел на нее. В дополнение к нескольким решениям, которые я дал, я фактически пропустил самое простое решение. Поскольку страница использует pjax, я могу напрямую отслеживать событие pjax:complete:

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

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

Внезапно возникает ощущение, что иногда «правда» всегда так проста.