|
|
@@ -1,175 +0,0 @@
|
|
|
-class ComprehensiveURLObserver {
|
|
|
- constructor(options = {}) {
|
|
|
- this.options = {
|
|
|
- trackHash: true,
|
|
|
- trackSearch: true,
|
|
|
- trackPathname: true,
|
|
|
- debounce: 50,
|
|
|
- ...options
|
|
|
- }
|
|
|
-
|
|
|
- this.observers = []
|
|
|
- this.lastUrl = this.getCurrentURLState()
|
|
|
- this.debounceTimer = null
|
|
|
- this.init()
|
|
|
- }
|
|
|
-
|
|
|
- init() {
|
|
|
- // 监听标准事件
|
|
|
- window.addEventListener('popstate', this.handleChange.bind(this))
|
|
|
-
|
|
|
- if (this.options.trackHash) {
|
|
|
- window.addEventListener('hashchange', this.handleChange.bind(this))
|
|
|
- }
|
|
|
-
|
|
|
- // 拦截 History API
|
|
|
- this.interceptHistoryMethods()
|
|
|
-
|
|
|
- // 监听点击事件(捕获链接点击)
|
|
|
- document.addEventListener('click', this.handleClick.bind(this), true)
|
|
|
-
|
|
|
- // 可选:轮询作为备用方案
|
|
|
- if (this.options.polling) {
|
|
|
- this.startPolling()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- getCurrentURLState() {
|
|
|
- return {
|
|
|
- href: window.location.href,
|
|
|
- pathname: window.location.pathname,
|
|
|
- search: window.location.search,
|
|
|
- hash: window.location.hash,
|
|
|
- origin: window.location.origin
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- interceptHistoryMethods() {
|
|
|
- const methods = ['pushState', 'replaceState']
|
|
|
-
|
|
|
- methods.forEach(method => {
|
|
|
- const original = history[method]
|
|
|
- history[method] = (...args) => {
|
|
|
- const oldState = this.getCurrentURLState()
|
|
|
- const result = original.apply(history, args)
|
|
|
- this.handleChange('history', oldState)
|
|
|
- return result
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- handleClick(event) {
|
|
|
- const link = event.target.closest('a')
|
|
|
- if (link && link.href) {
|
|
|
- const targetUrl = new URL(link.href, window.location.origin)
|
|
|
- const currentUrl = new URL(window.location.href)
|
|
|
-
|
|
|
- // 如果是同源导航且不是当前页面
|
|
|
- if (targetUrl.origin === currentUrl.origin &&
|
|
|
- targetUrl.href !== currentUrl.href) {
|
|
|
- event.preventDefault()
|
|
|
-
|
|
|
- this.handleChange('click', this.getCurrentURLState())
|
|
|
-
|
|
|
- // 执行实际导航
|
|
|
- setTimeout(() => {
|
|
|
- window.location.href = link.href
|
|
|
- }, 0)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- handleChange(type, oldState = this.lastUrl) {
|
|
|
- if (this.debounceTimer) {
|
|
|
- clearTimeout(this.debounceTimer)
|
|
|
- }
|
|
|
-
|
|
|
- this.debounceTimer = setTimeout(() => {
|
|
|
- const newState = this.getCurrentURLState()
|
|
|
-
|
|
|
- if (this.hasUrlChanged(oldState, newState)) {
|
|
|
- const changeInfo = {
|
|
|
- oldUrl: oldState.href,
|
|
|
- newUrl: newState.href,
|
|
|
- type: type,
|
|
|
- timestamp: Date.now(),
|
|
|
- details: {
|
|
|
- pathname: { old: oldState.pathname, new: newState.pathname },
|
|
|
- search: { old: oldState.search, new: newState.search },
|
|
|
- hash: { old: oldState.hash, new: newState.hash }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this.notifyObservers(changeInfo)
|
|
|
- this.lastUrl = newState
|
|
|
- }
|
|
|
- }, this.options.debounce)
|
|
|
- }
|
|
|
-
|
|
|
- hasUrlChanged(oldState, newState) {
|
|
|
- if (oldState.href !== newState.href) return true
|
|
|
-
|
|
|
- if (this.options.trackPathname && oldState.pathname !== newState.pathname) return true
|
|
|
- if (this.options.trackSearch && oldState.search !== newState.search) return true
|
|
|
- if (this.options.trackHash && oldState.hash !== newState.hash) return true
|
|
|
-
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- startPolling() {
|
|
|
- setInterval(() => {
|
|
|
- const currentState = this.getCurrentURLState()
|
|
|
- if (this.hasUrlChanged(this.lastUrl, currentState)) {
|
|
|
- this.handleChange('polling', this.lastUrl)
|
|
|
- }
|
|
|
- }, this.options.pollingInterval || 1000)
|
|
|
- }
|
|
|
-
|
|
|
- subscribe(callback) {
|
|
|
- this.observers.push(callback)
|
|
|
- return () => this.unsubscribe(callback)
|
|
|
- }
|
|
|
-
|
|
|
- unsubscribe(callback) {
|
|
|
- this.observers = this.observers.filter(obs => obs !== callback)
|
|
|
- }
|
|
|
-
|
|
|
- notifyObservers(changeInfo) {
|
|
|
- this.observers.forEach(observer => {
|
|
|
- try {
|
|
|
- observer(changeInfo)
|
|
|
- } catch (error) {
|
|
|
- console.error('URL观察者错误:', error)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- destroy() {
|
|
|
- window.removeEventListener('popstate', this.handleChange)
|
|
|
- window.removeEventListener('hashchange', this.handleChange)
|
|
|
- document.removeEventListener('click', this.handleClick)
|
|
|
-
|
|
|
- if (this.debounceTimer) {
|
|
|
- clearTimeout(this.debounceTimer)
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// const unsubscribe = urlWatcher.subscribe((change) => {
|
|
|
-// console.group('URL变化详情');
|
|
|
-// console.log('类型:', change.type);
|
|
|
-// console.log('从:', change.oldUrl);
|
|
|
-// console.log('到:', change.newUrl);
|
|
|
-// console.log('路径变化:', change.details.pathname);
|
|
|
-// console.log('查询参数变化:', change.details.search);
|
|
|
-// console.log('哈希变化:', change.details.hash);
|
|
|
-// console.groupEnd();
|
|
|
-// });
|
|
|
-export default {
|
|
|
- urlWatcher: new ComprehensiveURLObserver({
|
|
|
- trackHash: true,
|
|
|
- trackSearch: true,
|
|
|
- trackPathname: true,
|
|
|
- debounce: 100
|
|
|
- })
|
|
|
-}
|