多个视图的一个 Backbone 模型,谁处理 fetch()?

One Backbone model for several views, who handles the fetch()?

在多个视图之间共享一个 Backbone 模型是一种相当普遍的情况。尽管如此,假设这个模型是 UserModel。它处理多种方法,例如允许用户注册或登录。

并且当用户登录时,会调用fetch来获取用户的数据。因此,模型无法在其 initialize 方法中使用 this.fetch() 获取自身。

应该从哪里获取?怎么样?

这是我们的简单 UserModel:

const UserModel = Backbone.Model.extend({
    // get url for 'me' (ie connected user)
    url() {
        return app.endpoint + '/users/me/' + app.conToken;
    },

    login(email, password, rememberMe, callback) {
        …
    },

    signup(email, password, firstname, lastname, callback) {
        …
    }
});

现在假设它由两个人共享:

HomeView & CartView

app.HomeView = Backbone.View.extend({
    template: app.tpl.home,

    initialize() {
        // model is passed @ instanciation
        this.model.fetch();
        this.listenTo(this.model, 'sync', this.render);
    },

    render() {
       …
    }
});


app.CartView = Backbone.View.extend({
    template: app.tpl.cart,

    initialize() {
        // model is passed @ instanciation
        this.model.fetch();
        this.listenTo(this.model, 'sync', this.render);
    },

    render() {
       …
    }
});

现在,如果我实例化 HomeView,将获取 userModel。但是如果稍后我实例化 CartView,将再次获取相同的模型。这会产生无用的 http 请求。

基本上,模型可以在成功调用其 login 方法后自行获取,但用户可以到达页面或重新加载已经登录的浏览器。此外,用户可以登陆任何页面,没有办法说他会在 CartView 之前 HomeView

我看到了两个选项。 UserModel 可以像这样巧妙地处理多个 fetch 调用:

const UserModel = Backbone.Model.extend({
    // get url for 'me' (ie connected user)
    url() {
        return app.endpoint + '/users/me/' + app.conToken;
    },

    isSync() {
        // an hour ago
        let hourAgo = Date.now() - 3600000;

        // data has been fetched less than an hour ago
        if (this.fetchTime && hourAgo > this.fetchTime) return true;
        return false;
    },

    fetch() {
        // has not already fetched data or data is older than an hour
        if (!this.isSync()) {
            this.fetchTime = Date.now();
            this.fetch();

            return;
        }

        // trigger sync without issuing any http call
        this.trigger('sync');
    },

    …
});

这样,我可以根据需要多次调用 this.model.fetch(),在视图中是无状态的。

或者,我可以在视图层上处理:

app.HomeView = Backbone.View.extend({
    template: app.tpl.home,

    initialize() {
        // model is passed @ instanciation

        // fetch model if empty
        if (_.isEmpty(this.model.changed)) this.fetch();

        // render directly if already populated
        else this.render();

        // render on model sync
        this.listenTo(this.model, 'sync', this.render);
    },

    render() {
       …
    }
});

如有需要,Backbone's model.changed doc reference & Underscore's _.isEmpty's.

哪种方式更干净?还有其他我可能错过的方法吗?

个人偏好不会覆盖 fetch 而是实现包装函数,例如 customFetch

const UserModel = Backbone.Model.extend({
// get url for 'me' (ie connected user)
url() {
    return app.endpoint + '/users/me/' + app.conToken;
},

isSync() {
    // an hour ago
    let hourAgo = Date.now() - 3600000;

    // data has been fetched less than an hour ago
    if (this.fetchTime && hourAgo > this.fetchTime) return true;
    return false;
},

customFetch() {
    // has not already fetched data or data is older than an hour
    if (!this.isSync()) {
        this.fetchTime = Date.now();
        this.fetch();

        return;
    }

    // trigger sync without issuing any http call
    this.trigger('sync');
},

…
});

您提供的代码示例将以循环结束(this.fetch 调用自身...),因此我个人的偏好是将核心 backbone 功能包装在另一个函数中。

我什至会拥有自己的自定义 Model,它由我使用的所有模型扩展。例如:

const MyModel = Backbone.Model.extend({
    isSync() {
        // an hour ago
       let hourAgo = Date.now() - 3600000;

       // data has been fetched less than an hour ago
       return (this.fetchTime && hourAgo > this.fetchTime);
    },
    customFetch() {
        this.fetch();
    }, 
});

然后 UserModel 将覆盖 customFetch 并且看起来像这样:

const UserModel = MyModel.extend({
    customFetch() {
        // has not already fetched data or data is older than an hour
        if (!this.isSync()) {
            this.fetchTime = Date.now();
            this.fetch();
            return;
        }

        // trigger sync without issuing any http call
       this.trigger('sync');
    },
});

可能不是最好的方法。就我个人而言,这将是阅读然后扩展的简单方法。我想这个 customFetch 会用在 some/all 模型中,所以它可以适当修改。