【问题标题】:One Backbone model for several views, who handles the fetch()?多个视图的一个主干模型,谁处理 fetch()?
【发布时间】:2016-11-15 23:34:58
【问题描述】:

在多个视图之间共享一个 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() {
       …
    }
});

现在如果我实例化HomeViewuserModel 将被获取。但是,如果稍后我实例化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 文档参考和下划线的_.isEmpty's

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

【问题讨论】:

    标签: javascript backbone.js model synchronization


    【解决方案1】:

    个人偏好不会覆盖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 调用自身...),所以我个人的偏好是将核心主干功能包装在另一个函数中。

    我什至会拥有我自己的自定义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 将用于部分/所有模型,因此可以酌情对其进行修改。

    【讨论】:

    • +1 表示无限循环,很明显我没有测试过它:p 有一个自定义的基础模型听起来很棒!为什么要让UserModel 覆盖customFetch?它可以直接在基类中声明。然后我会在我只想确保模型已填充的视图中调用customFetch,或者在我需要处理最新数据时直接调用fetch
    • @Buzut 又是我个人的偏好。让我们设置一个TimeModel,它也扩展了MyModel,这个模型我们每次都想要fetch。我更愿意在我的代码中到处写customFetch,而不是混合使用customFetchfetch。您可以很容易地将customFetch 中的MyModel 设置为您的自定义代码。再说一次,这就是它对我的意义,希望我的推理是有意义的。最终,它是最适合您的,但请尽量与您的方法保持一致。
    • 感谢您阐明这一点,并花时间提出全面的答案。
    • 没关系。 Backbone 是一个非常强大的野兽,纯粹是因为它不需要你以特定的方式做任何事情。所以你会发现创建MyModel 甚至MyView 会减少很多重复的代码。我还强烈建议您查看Marionette 以帮助解决views
    • 我喜欢 Backbone,因为它极简的方法和它为开发人员提供的高度控制(没有什么神奇的东西 à la Angular)。尽管如此,有时我想确保“我的方式”是优雅的。我读过很多Derick Balay's posts on Backbone(木偶创作者)。我使用Marionnette 中的regionManager 之类的东西,但在可能的情况下,我喜欢让Backbone 保持“原始”状态!
    猜你喜欢
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多