对于单页应用中如何监听 URL 变化的思考
周末开发了一个在 GitHub 中给 repo 增加自定义备注的 chrome 扩展。
开发这个扩展的原因是我在 GitHub 中所 star 的项目实在太多了(截止目前 671 个),有的项目过个几天回来看就忘了为什么 star 了,有的轮子想找的时候发现忘记叫什么了,这么多一个个找实在浪费时间。于是我一直在想有这么个工具,可以自定义对 GitHub 中的项目进行备注,然后可以根据备注进行搜索,于是便有了这个扩展。
如需安装体验请点击 GitHub Remarks 进行安装,源码移步 github-remarks(没啥参考价值)。
当然本文并不是讲这个扩展的制作过程,而是在这过程中碰到的一个问题。
以我的 GitHub 账户举例,在 https://github.com/hanzichi?tab=stars 页面,我需要插入一些 dom,使得页面如下:

乍一想,似乎很简单,在这个页面插入 content.js 就行,在 manifest.json 中增加配置:
{
"matches": ["*://github.com/.*tabs=stars"],
"js": ["content-scripts/repoDetail.js"],
"css": [],
"run_at": "document_end"
}
但是问题来了,直接打开页面 https://github.com/hanzichi?tab=stars 并没有问题,但是如果从 https://github.com/hanzichi 点击跳转到 https://github.com/hanzichi?tab=stars,因为 GitHub 用了 pjax 技术,content.js 其实应该加载在页面 https://github.com/hanzichi 的,但是那个页面并没有参照的 dom 结构(好把我们需要的 dom 插入)。所以问题似乎就转为了,在 pjax 页面,如何能够监听到页面 url 的变化?
首先一个很简单的方案是,弄个定时器循环监听,但是太耗费性能了,pass
因为页面是 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 方法在原来页面其实并没有改变,所以也没什么卵用
然后从 chrome API 入手,发现有个 chrome API 可以检测当前 url 改变的 tab(参考 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 扩展需要在相应 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()
}
})
突然让人感觉,有时候 "真相" 往往总是那么简单。
对于单页应用中如何监听 URL 变化的思考的更多相关文章
- 009——VUE中watch监听属性变化实现类百度搜索栏功能
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Angular.js中使用$watch监听模型变化
$watch简单使用 $watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你. $watch(watchExpression, listener, objectEqua ...
- 新建Oracle数据库时,提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序
新建一个oracle数据库时,当提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序等字样的时候,问题是那个监听的服务没有启动,解决方法如下: 打开cmd命 ...
- Android中如何监听GPS开启和关闭
转自 chenming 原文 Android中如何监听GPS开启和关闭 摘要: 本文简单总结了如何监听GPS开关的小技巧 有时需要监听GPS的开关(这种需求并不多见).实现的思路是监听代表 GPS ...
- Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差
Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别 Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...
- oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序
15511477451 原文 oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序? 环境:win7 64位系统.oracle11g数据库 问题描述:在win7 64位系统 ...
- Android 关于ListView中按钮监听的优化问题(方法二)
关于ListView中按钮监听的优化问题(方法一)地址: http://www.cnblogs.com/steffen/p/3951901.html 之前的方法一,虽然能够解决position的传递, ...
- Android 关于ListView中按钮监听的优化问题(方法一)
在Android应用开发过程中经常会用到ListView,并且每次在item中都要对点击事件进行监听.在给按钮添加OnClickListener时,一般会下意识的在getView()中找到每一个But ...
- 如何在vue单页应用中使用百度地图
作为一名开发人员,每次接到开发任务,我们首先应该先分析需求,然后再思考技术方案和解决方案.三思而后行,这是一个好的习惯. 需求:本项目是采用vue组件化开发的单页应用项目,现需要在项目中引入百度的地图 ...
随机推荐
- 对比Cassandra、 Mongodb、CouchDB、Redis、Riak、 Membase、Neo4j、HBase
转自:http://www.cnblogs.com/alephsoul-alephsoul/archive/2013/04/26/3044630.html 导读:Kristóf Kovács 是一位软 ...
- JDK8安装时错误1335的解决
Win7安装JDK8 update65版本时,碰到错误1335,错误信息大概是一个cab文件损坏了,搜索了一下,有网站提供这个错误的修补工具,不过最终我没有下载这个工具,说是系统问题,但工具不是MS官 ...
- Python入门指南(超详细)
Python 是一门非常容易上手的语言,通过查阅资料和教程,也许一晚上就能写出一个简单的爬虫.但 Python 也是一门很难精通的语言,因为简洁的语法背后隐藏了许多黑科技.本文主要针对的读者是: 毫无 ...
- 用js实现算法:冒泡排序、插入排序和快速排序
一.冒泡排序 function bubbleSort(arr){ for(var i=0;i<arr.length;i++){ for(var j=0;j<arr.length-i-1;j ...
- var $this = $(this)是什么意思?
var $this = $(this) 声明一个变量,$this 是变量名,加$说明是jquery对象. 给声明的变量赋值,赋的值是将this元素转换为jQuery对象.
- 托管C++线程锁实现
最近由于工作需要,开始写托管C++,由于C++11中的mutex,和future等类,托管C++不让调用(报错),所以自己实现了托管C++的线程锁. 该类可确保当一个线程位于代码的临界区时,另一个线程 ...
- java动态绑定与静态绑定【转】
程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定.静态绑定: 在程序执行前方法已经被绑定(也就是说在 ...
- 洛谷 P1054 解题报告
P1054 等价表达式 题目描述 明明进了中学之后,学到了代数表达式.有一天,他碰到一个很麻烦的选择题.这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的 ...
- SSM-Spring-05:Spring的bean是单例还是多例,怎么改变?
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- Spring的bean是单例的 它在Spring容器初始化的时候创建对象 可以修改为多例,在此bean节点中添 ...
- OOP编程七大原则
OCP(Open-Closed Principle),开放封闭原则:软件实体应该扩展开放.修改封闭.实现:合理划分构件,一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里:一种可变性不应 ...