【问题标题】:Backbone.js app completely rendered in JSBackbone.js 应用完全用 JS 渲染
【发布时间】:2015-08-10 02:00:11
【问题描述】:

我正在尝试在 Backbone.js 中构建一个新项目,我的背景是使用 Java、PHP、Rails、Perl 等开发 web 应用程序。我已经完成了相当多的 JS 工作,包括 JQuery 和一些 Node,但是我在思考在 Backbone 中构建整个应用程序的最佳实践时遇到了一些麻烦。教程中的内容似乎与我看到的实际应用程序的实现方式有所不同。

我一直在阅读 Addy Osmani 的 Backbone Fundamentals。他的示例让您创建一个静态 HTML 文件,将应用程序的骨架放入其中,然后由 JS 对其进行修改。一个例子 sn-p:

  <section id="todoapp">
    <header id="header">
      <h1>todos</h1>
      <input id="new-todo" placeholder="What needs to be done?" autofocus>
    </header>
    <section id="main">
      <input id="toggle-all" type="checkbox">
      <label for="toggle-all">Mark all as complete</label>
      <ul id="todo-list"></ul>
    </section>
    <footer id="footer"></footer>
  </section>

这一切都很好,而且似乎是一种足够体面的方法。但我见过几个应用程序,其中静态 HTML 文件只是少数样式表和脚本标签。看起来整个应用程序都是由 JS 生成的。这对于具有许多不同视图和动态内容的应用程序似乎是有益的。

我真的很想了解这些应用程序是如何构建的,但我一直无法找到涵盖它的文档或教程。我想我可以一起破解一些东西,但我真的更想了解正确的方法、最佳实践等。

有人可以指出一些文档的方向来了解这一点吗?

【问题讨论】:

    标签: javascript backbone.js single-page-application


    【解决方案1】:

    很好的问题,但不幸的是相当开放。有manysuchtutorialsaround,但我宁愿给你一个需要做什么的高级概述来回答。

    您的问题所指的区别是在哪里呈现 HTML:全部在服务器端,全部在客户端,或两者兼而有之。

    另一个(相关)问题是在哪里 路由发生。

    最接近“所有客户端”的方法是有一个服务器端面向用户的 URL 端点,该端点返回一个小的 HTML 响应,该响应本质上是客户端的引导程序,如下例所示:

    获取/

    <html>
    <head>
        <title>Loading</title>
        <script data-main="/client/main" src="/client/libs/require.js"></script>
    </head>
    <body>
    </body>
    </html>
    

    这使用 AMD 模块加载器require.js(还有很多其他选择,但请耐心等待)。反过来,require.js 将自动加载在data-main 中指定的脚本,在本例中为/client/main.js(约定不包括.js)。

    GET /client/main.js

    require.config({
        baseUrl: '/client',       // this tells require to load things relative to this "base" path
        paths: {
             underscore: 'libs/underscore',
             jquery: 'libs/jquery',
             backbone: 'libs/backbone',
             text: 'libs/require-plugins/text',      // this is a "plugin" for require.js that allows you to load textfiles instead of scripts when you precede the path with 'text!'
             // etc for other libs
        },
        shim: {
             underscore: { exports: '_' },
             jquery: { exports: '$' },
             backbone: { deps: ['underscore', 'jquery'], exports: 'Backbone' }
        }
    });
    
    require(['jquery', 'application'], function($, Application) {
    
        var $rootDiv = $("<div>", {id: "app-root", class: ""});
        $('body').prepend($rootDiv);
        var app = new Application({ rootEl: $rootDiv });
        app.start();
    
    });
    

    我不想过多地深入研究require.js,所以我只想指出它的作用与它看起来的一样:它以一种受控的方式异步加载 JavaScript(所以你说 this 脚本依赖于 that 脚本等)。第一个块只是require.js 的配置,有点离题,但我想让它变得现实。

    第二块更有趣。它是这样说的:

    1. require 以下依赖项:jqueryapplication
    2. 在加载它们之后(从HTTP GET 或者如果之前加载则从缓存中),将它们分别别名为$Application
    3. 创建一个新的div 并将其添加到body
    4. 创建一个新的Application,将rootEl指定为div创建
    5. 在应用实例上调用start

    这是 DOM 在这一点上的样子:

    <html>
    <head>
        <title>Loading</title>
        <script data-main="/client/main" src="/client/libs/require.js"</script>
        <script data-requiremodule="main" src="/client/main.js"></script>
        <script data-requiremodule="jquery" src="/client/libs/jquery.js"></script>
         <!-- ... etc ... -->
         <script data-requiremodule="application" src="/client/application.js"></script>
    </head>
    <body>
         <div id="application-root"></div>
    </body>
    </html>
    

    关于最后一个依赖,application.js:

    GET /client/application.js

    define(['underscore', 'jquery', 'backbone', 'text!templates.html'], function(_, $, Backbone, Templates) {
    
        var getStartOptions = function(options) {
            options = options || {};
            _(options).defaults({
                rootEl: $('body'),
                initialRoute: '/'
            });
    
            return options;
        };
    
        return Backbone.View.extend({
            initialize: function(options) {
                this.state = new Backbone.Model(getStartOptions(options));
    
                this.listenToOnce(this.state, 'change:started', function() {
                    this.state.set(getStartOptions(options));
                }, this);
    
                this.listenTo(this.state, 'change:rootEl', this.onChangeRootEl, this);
            },
            onChangeRootEl: function(val) {
                this.setElement(val);
                if (!this.state.previous('rootEl')) {
                    var $templates = $('<div>', {id: 'app-templates'});
                    $templates.html(Templates);
                    $('body').append($templates);
                }
                this.render();
            },
            template: _.template($('#app-templates #app-layout-template').html()),
            render: function() {
                this.$el.html(this.template());
                return this;
            },
            start: function() {
                this.state.set('started', true);
            }
        });
    
    });
    

    define 函数类似于require(它是require.js 的一部分),但它不会自行“运行”——require 可以。所以,无论你define,你必须在其他地方require 运行。

    define 函数参数中的 return 值是您在 require 时返回的值。

    在这种情况下,getStartOptions私有的,但返回的 View 是之前要求 ['application'] 的值。

    大致情况如下:

    1. 从以前开始,我们实例化这里定义的View(我们称之为Application
    2. 我们打电话给start
    3. startstarted 设置为 true
    4. started 为真时,视图(通过listenToOnce 调用)设置初始选项。
    5. 第一次设置rootEl 时,我们将模板注入DOM
    6. When options change, we update the view accordingly and then re-render.

    在这一切之后,结果将是:

    <body>
        <div id="app-root">
            ... content of the template with id "app-layout-template" ...
        </div>
    </body>
    

    布局模板只要是这种形式即可。

    GET /client/templates.html

    <script type="text/whatever-you-want" id="app-layout-template">
    ...
    </script>
    
    <script type="text/x-underscore-template" id="example-of-app-layout-template">
        <header><h2><%= appTitle %></h2></header>
        <section><%- appContent %></section>
        <footer><p><%= appFooterMessage %></footer>
    </script>
    

    Here is some information about underscore templates.还有很多其他选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-17
      • 1970-01-01
      • 2011-09-28
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多