Backbone.js历史管理 #

一、History概述 #

1.1 什么是History #

Backbone.history 是全局路由器,用于监听URL变化并触发相应的路由处理。

text
History职责
├── 监听URL变化
├── 管理浏览器历史记录
├── 匹配路由规则
└── 触发路由处理函数

1.2 两种模式 #

模式 URL格式 特点
hash #/users 兼容性好,不需要服务器配置
pushState /users URL美观,需要服务器配置

二、启动History #

2.1 基本启动 #

javascript
var router = new AppRouter();

Backbone.history.start();

2.2 start选项 #

javascript
Backbone.history.start({
    pushState: true,
    hashChange: true,
    root: '/app/',
    silent: false
});
选项 默认值 说明
pushState false 使用HTML5 History API
hashChange true 监听hash变化
root ‘/’ 应用根路径
silent false 是否静默启动

2.3 启动时机 #

javascript
$(function() {
    var router = new AppRouter();
    Backbone.history.start();
});

2.4 检查启动状态 #

javascript
if (!Backbone.History.started) {
    Backbone.history.start();
}

三、pushState模式 #

3.1 启用pushState #

javascript
Backbone.history.start({
    pushState: true,
    root: '/myapp/'
});

3.2 URL格式 #

text
Hash模式:     http://example.com/#/users/1
pushState模式: http://example.com/users/1

3.3 服务器配置 #

服务器需要配置将所有请求重定向到入口页面:

Nginx配置:

nginx
location / {
    try_files $uri $uri/ /index.html;
}

Apache配置:

apache
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
</IfModule>

Node.js Express配置:

javascript
app.get('*', function(req, res) {
    res.sendFile(path.join(__dirname, 'index.html'));
});

3.4 处理链接 #

javascript
$(document).on('click', 'a[href^="/"]', function(e) {
    var href = $(this).attr('href');
    var root = Backbone.history.root;
    
    if (href.indexOf(root) === 0) {
        e.preventDefault();
        Backbone.history.navigate(href.replace(root, ''), { trigger: true });
    }
});

3.5 兼容性检测 #

javascript
if (Backbone.history.supportsPushState()) {
    Backbone.history.start({ pushState: true });
} else {
    Backbone.history.start();
}

四、hash模式 #

4.1 默认模式 #

javascript
Backbone.history.start();

4.2 URL格式 #

text
http://example.com/#/users
http://example.com/#/users/1
http://example.com/#/posts/2024/03

4.3 hashChange事件 #

javascript
$(window).on('hashchange', function() {
    console.log('Hash changed:', window.location.hash);
});

4.4 优点 #

  • 不需要服务器配置
  • 兼容所有浏览器
  • 部署简单

4.5 缺点 #

  • URL不够美观
  • 不利于SEO
  • 无法使用锚点

五、History API #

5.1 navigate方法 #

javascript
Backbone.history.navigate('users', { trigger: true });

Backbone.history.navigate('users/1', { trigger: true, replace: true });

5.2 loadUrl方法 #

javascript
var matched = Backbone.history.loadUrl();
console.log('是否匹配路由:', matched);

5.3 getFragment方法 #

javascript
var fragment = Backbone.history.getFragment();
console.log('当前路由:', fragment);

5.4 解析URL #

javascript
var fragment = Backbone.history.getFragment('/users/1?tab=profile');
console.log(fragment);

六、历史记录操作 #

6.1 原生History API #

javascript
window.history.length;

window.history.back();

window.history.forward();

window.history.go(-2);

window.history.pushState({ page: 1 }, 'Title', '/page1');

window.history.replaceState({ page: 2 }, 'Title', '/page2');

6.2 popstate事件 #

javascript
$(window).on('popstate', function(e) {
    console.log('状态:', e.originalEvent.state);
});

6.3 状态管理 #

javascript
var AppRouter = Backbone.Router.extend({
    navigate: function(fragment, options) {
        options = options || {};
        
        var state = {
            route: fragment,
            timestamp: Date.now()
        };
        
        if (options.replace) {
            window.history.replaceState(state, '', '#' + fragment);
        } else {
            window.history.pushState(state, '', '#' + fragment);
        }
        
        if (options.trigger) {
            this.loadUrl(fragment);
        }
        
        return this;
    }
});

七、实用示例 #

7.1 历史管理器 #

javascript
var HistoryManager = {
    maxLength: 50,
    history: [],
    
    init: function() {
        var self = this;
        
        Backbone.history.on('route', function(router, name) {
            self.add({
                route: Backbone.history.getFragment(),
                name: name,
                timestamp: Date.now()
            });
        });
    },
    
    add: function(item) {
        this.history.push(item);
        
        if (this.history.length > this.maxLength) {
            this.history.shift();
        }
    },
    
    getLast: function() {
        return this.history[this.history.length - 1];
    },
    
    getPrevious: function() {
        return this.history[this.history.length - 2];
    },
    
    canGoBack: function() {
        return this.history.length > 1;
    }
};

7.2 路由恢复 #

javascript
var RouteRecovery = {
    storageKey: 'lastRoute',
    
    save: function() {
        var fragment = Backbone.history.getFragment();
        localStorage.setItem(this.storageKey, fragment);
    },
    
    restore: function() {
        var fragment = localStorage.getItem(this.storageKey);
        if (fragment) {
            Backbone.history.navigate(fragment, { trigger: true });
        }
    },
    
    clear: function() {
        localStorage.removeItem(this.storageKey);
    }
};

$(window).on('beforeunload', function() {
    RouteRecovery.save();
});

$(function() {
    var router = new AppRouter();
    Backbone.history.start();
    RouteRecovery.restore();
});

7.3 页面状态保存 #

javascript
var PageState = {
    states: {},
    
    save: function(key, state) {
        this.states[key] = {
            state: state,
            route: Backbone.history.getFragment(),
            timestamp: Date.now()
        };
    },
    
    get: function(key) {
        return this.states[key];
    },
    
    restore: function(key) {
        var saved = this.states[key];
        if (saved) {
            return saved.state;
        }
        return null;
    },
    
    clear: function(key) {
        if (key) {
            delete this.states[key];
        } else {
            this.states = {};
        }
    }
};

var UserListView = Backbone.View.extend({
    initialize: function() {
        var saved = PageState.restore('userList');
        if (saved) {
            this.page = saved.page;
            this.filter = saved.filter;
        }
    },
    
    saveState: function() {
        PageState.save('userList', {
            page: this.page,
            filter: this.filter
        });
    }
});

7.4 智能后退 #

javascript
var SmartBack = {
    defaultRoute: '',
    excludedRoutes: ['login', 'register'],
    
    init: function(defaultRoute) {
        this.defaultRoute = defaultRoute || '';
    },
    
    goBack: function() {
        var current = Backbone.history.getFragment();
        
        if (this.excludedRoutes.indexOf(current) !== -1) {
            Backbone.history.navigate(this.defaultRoute, { trigger: true });
            return;
        }
        
        if (window.history.length > 1) {
            window.history.back();
        } else {
            Backbone.history.navigate(this.defaultRoute, { trigger: true });
        }
    }
};

SmartBack.init('dashboard');

$('.btn-back').on('click', function() {
    SmartBack.goBack();
});

八、总结 #

8.1 History方法 #

方法 说明
start(options) 启动历史管理
navigate(fragment, options) 导航到指定路由
loadUrl(fragment) 加载URL
getFragment() 获取当前路由片段

8.2 模式对比 #

特性 hash模式 pushState模式
URL美观
服务器配置 不需要 需要
SEO友好
兼容性 较好

8.3 最佳实践 #

  1. 根据需求选择合适的模式
  2. pushState模式需要服务器配置
  3. 保存页面状态提升用户体验
  4. 处理浏览器前进后退
  5. 实现智能后退逻辑
最后更新:2026-03-28