骨干工程组织 [英] Backbone project organization

查看:108
本文介绍了骨干工程组织的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我挣扎了一下与未来与组织我骨干应用清洁,可靠的方法。我使用Requirejs,把手和Requirejs文本插件动态加载HTML视图。为了简化问题,我们只能说该网站有以下页面:

首页:显示产品的集合

关于:静态页面

账户:包含帐户信息。购买的产品,允许各种更新。很多功能。有标签导航到不同的部分。

所以我要为装入新页面变成一个div('.backbone视图')的SPA。我应该有一个大致的APPVIEW与EL:$('骨干视图。)当路由变化,然后加载相应的模板被称为?或者我应该对每一个页面(homeView,aboutView,accountView)视图,所有的埃尔设置为骨干网的看法?

除此之外...我还需要为产品以外的任何一个模式?对于静态页左右,我只是加载HTML模板,仅此而已。但对于产品,我需要调用集合的产品,这使得每一个产品视图,其中每个被与产品模型相关联。这很好......但我在哪里初始化这些产品的结构?当我路线的主页,难道我这样做呢?我有这个伪code:

 路线:{
        '':'家',
        关于:关于,
        我的账户:我的账户,
        *默认:家
    },    家:功能(){
        //获取主页模板抓斗        //加载了产品        //替换$('骨干视图')与主页模板填充产品
    },    关于:功能(){
        //取得有关模板和替换$('骨干视图)及其内容
    },    我的帐户:功能(){
        MIND爆炸
    }

我觉得一个很大的问题是,我不是意见的目的明确的......他们能简单地用于页面过渡,或者他们应该始终重视他们的模型?如果是前者,我至少需要为每个页面的APPVIEW,然后查看,对不对?我迷失在何处我会委托的每一步......所以任何帮助是AP preciated。

感谢您的帮助!


解决方案

下面是工作非常大型骨干应用后的几个技巧。这不是详尽的或最后..

分成两个目录


  1. 服务器目录例如服务器/

  2. 可公开访问的目录如 WWW /

此外,当您运行构建的任务,将构建应用程序到分发版本到编译/ DIST / 目录。可能使用咕嘟咕嘟或咕噜。

扩展骨干

您整个应用程序将包括:


  • 查看和放大器;子的意见

  • 路由器和放大器;子路由器

  • 模式

  • 收藏

您应该延伸主干课程,即使他们是在第一个空。最有用的两个扩展是:


  • 子视图(视图可以有一个的意见对象/更多的意见,这当您删除父视图得到清理功能)。所谓的模型和集合模式都会自动向下传递给子的意见。

  • 子路由器(这是不错的路由逻辑模块文件夹中的每个模块)

使用吊舱架构

作为组织围绕您的应用程序独立模块例如:

WWW /应用/模块/家用/ router.js < - 子路由器,调用modules.js方法结果
WWW /应用/模块/家用/ module.js < - prepares终点 - 改变布置,初始化的看法和放大器;模型等结果
WWW /应用/模块/家用/视图/...所有视图(可以有子文件夹太)结果
WWW /应用/模块/家用/模板/ 结果
WWW /应用/模块/家用/模型/ 结果
WWW /应用/模块/家用/集

开始看到你的应用程序中的观点而言和子的意见

一个页面并不仅仅由一个看法。这将有可能是一个特殊的布局视图,里面这将是很多的观点 - 其中一个分裂的页面一半,其中一个与每个页面数,形式的看法,有很多内部的子观点里面更多的意见分页每个表单元素和消息等等等等

您可以启动的意见思维阴影DOM树和逻辑划分 - 任何你认为是你的页面上重复使用使其成为一个包(它自己的视图和模型/收藏品是否需要它们)。

模型是为任何数据和数据进行的任何的逻辑,如果视图被示出从服务器/ API /数据库它通常会被传递到视图的任何这将通过所有或一些模型的属性的模板。

如果该项目显示的信息是在一个列表,然后收集将管理每个模型为每个项目。

做配车型通信

如果你发现自己想从某种看法传达给另一种观点认为,使用一个共享的模式。视图应脱钩越好(它不应该需要知道它的父)。

有一个应用程序的状态

创建一个名为届时AppState模型使用触发器在整个应用广泛的沟通和倾听。

有一个包文件夹(可选)

每当您在您的应用程序,你认为可能是可重复使用的,即使是在其他未来应用遇到的东西,创建一个包。这些通常会在自己的git回购主持,你可以使用的package.json或命令行拉他们到项目。

有你的地方扩展应用间的东西文件夹

有一个扩展的文件夹,这是由多个应用程序消耗模块 - 例如您骨干扩展可以去这里。或者,如果你创建了一个包的形式,但要为这个应用程序做一些具体,那么在这里扩展它。

例如。
WWW /应用/扩展/ view.js 结果
WWW /应用/扩展/ model.js 结果
WWW /应用/扩展/ collection.js 结果
WWW /应用/扩展/按键/ link.js //从一个按钮包扩展链接视图。

资产

为什么我会在公共 WWW / 文件夹中的应用程序/ 文件夹的原因是这样我就可以也有资产的文件夹中有没有字体和图像等:

WWW /资产/ CSS 结果
WWW /资产/图像

请注意:也许你想尝试,并保持资产的模块文件夹(内嵌POD架构)。我没有做过,但它是值得考虑的。

的index.html

通常情况下,如果你使用的是CommonJS的或AMD您的index.html也只是样板,没有实际的DOM元素,你会在那里有一个入口js文件一个电话。由于CommonJS的已编译这个也只是像<脚本的src =/ app.js>< / SCRIPT> 但AMD会更喜欢

 <! - 如果不建 - >
<脚本数据主要=/应用程序/配置SRC =/包/ require.js>< / SCRIPT>
&所述;! - ELSE
<脚本的src =/ app.js>< / SCRIPT>
- >

因此​​,在开发中运行时(非构建)RequireJS将加载应用程序/ config.js ,但在构建整个应用程序将在 app.js 。有各种步兵/咕嘟咕嘟建设任务,这将做一些像上面给你(显然是有条件的语法只是做了)。

布局

我将创建一个扩展/ layout.js 延伸扩展/ view.js ,这将是一个简单的扩展,可以像正常的子视图(如页眉和页脚),而且,我可以附加任何视图(对身体子视图)的特殊子视图如像的setContentView方法(视图)

我想也许创建一个模块调用的布局,并在那里有一个目录模块/布局/默认有具有页眉和页脚子视图的视图。然后到达指数的路线会流是这样的:

应用程序/ router.js =>应用程序/模块/家用/ router.js => app/modules/home/module.js@index =>的setContentView(查看从应用程序/模块/家用/视图/ index.js)

路由

我必须位于例如应用路由器 WWW /应用/ router.js 这可能有一些特殊的途径,但将在很大程度上只是一个对象,指向子路由器subroute:

  subRouters:{
    商店定位器:StoreLocatorRouter,
    我的帐户:MyAccountRouter,
    网站地图:SitemapRouter
}

我要通过像您的扩展(注意的东西延长正常的骨干路由器实现这一点,你需要调用 initSubRouters 初始化) -

 定义([
    下划线,
    '骨干'
]
功能(_,骨干){    使用严格的;    / **
     *扩展骨干样板路由器
     * @class扩展/路由器
     * @extends骨干/视图
     * /
    VAR路由器= Backbone.Router.extend(
        / ** @lends扩展/ router.prototype * /
        {        / **
         *存放参照子路由器
         * @type {}对象
         * /
        subRouters:{},        / **
         *添加子路由
         *基于https://gist.github.com/1235317
         * @参数{}字符串preFIX的字符串,pfixed的路由值$ P $
         * /
        构造:功能(选件){
            如果(!期权){
                选项​​= {};
            }            VAR路线= {} preFIX =选项preFIX。            如果(preFIX){
                //确保prefixes恰好有一个结尾的斜线
                prefix.replace(/ \\ / * $ /,'/');
            }其他{
                // preFIX是可选的,设置为空字符串,如果不通过
                preFIX ='';
            }            如果(preFIX){
                //每路线必须是prefixed
                _.each(this.routes,函数(回调,路径){
                    如果(路径){
                        路线[preFIX +'/'+路径] =回调;
                    }其他{
                        //如果路径是只是设置为preFIX,这是符合
                        //与骨干期望如何基路径看画廊VS库/
                        路线[preFIX +'(/)'] =回调;
                    }
                });                //必须与prefixed航线覆盖
                this.routes =航线;
            }            // .navigate需要subrouter preFIX
            这preFIX = preFIX。            //需要有骨干组建路线
            Backbone.Router.prototype.constructor.apply(这一点,参数);
        },        / **
         *设定beforeRoute事件。
         * /
        初始化:功能(){
            //这是一个有关将beforeRoute事件,绝方式轮
            添加其它路由之前//发生。
            Backbone.history.route({
                测试:this.beforeRoute
            },函数(){});
        },        / **
         *路线之前调用。
         * @返回{}布尔假这就保证了'路线'是禁用的。
         * /
        beforeRoute:功能(){
            Backbone.history.trigger('beforeRoute');
            返回false;
        },        / **
         *添加preFIX导航路线
         * @参数{string}里的路线非prefixed路线
         * @参数{}对象选择经过之地Backbone.router.navigate
         * /
        导航:功能(路线选项){
            如果(route.substr(0,1)=='/'和;!&放大器; route.indexOf(此prefix.substr(0,
                这prefix.length! - 1))== 0){
                。路线=这个preFIX +路线;
            }
            Backbone.Router.prototype.navigate.call(这一点,路线,期权);
        },        / **
         *在初始化`this.subRouters`定义的子路由器
         * /
        initSubRouters:功能(){
            _.each(this.subRouters,功能(路由器名称){
                这是[NAME] =新的路由器({
                    preFIX:名称
                });
            }, 这个);
        }    });    返回路由器;
});

I'm struggling a bit with coming up with a clean, solid way to organize my Backbone application. I'm using Requirejs, Handlebars, and the Requirejs Text plugin to dynamically load HTML views. To simplify things, let's just say the site has the following pages:

Home: displays a collection of products

About: static page

Account: contains account information. products purchased, allows for various updates. Lots of functionality. Has tabs to navigate to different sections.

So I'm going for an SPA that loads new pages into a div ('.backbone-view'). Should I have a general AppView with an el: $('.backbone-view') that is called when the route changes and then loads the appropriate template? Or should I have a view for every page (homeView, aboutView, accountView), all with their el set to backbone-view?

Beyond that...do I need a model for anything except Products? For the static about page, I just load in the html template and that's it. But for products, I need to call the products collection, which renders each product view, each of those being associated with a product model. That's fine...but where do I initialize these product constructs? When I route to the home page, do I do it there? I have this pseudo-code:

  routes: {
        '': 'home',
        'about': 'about',
        'my-account': 'myAccount',
        '*default': 'home'
    },

    'home': function() {
        // Grab template for home page

        // Load up products

        // Replace $('.backbone-view') with home page template populated with products
    },

    'about': function() {
        // Grab about template and replace $('.backbone-view') with its contents
    },

    'myAccount': function() {
        MIND EXPLOSION
    }

I think a big issue is that I'm not clear on the purpose of Views...can they be used simply for page transitions, or should they always have a model attached to them? If the former, I would at least need an AppView and then Views for each page, right? I'm lost as to where I would delegate each step...so any help is appreciated.

Thanks for the help!

解决方案

Here are a few tips after working on very large backbone apps. It's not exhaustive or final..

Divide into two directories

  1. The server directory e.g server/
  2. The publicly accessible directory e.g. www/

Also when you run your build task it would build the app into a distributable version into a build/ or dist/ directory. Probably using Gulp or Grunt.

Extend Backbone

Your entire app will consist of:

  • Views & sub views
  • Routers & sub routers
  • Models
  • Collections

You should extend the Backbone classes even if they are empty at first. The most useful two extensions are:

  • Sub views (a view can have a views object/function with more views, which get cleaned up when you remove the parent view). Models and collections called model or collection get automatically passed down to sub views.
  • Sub routers (it's nice to have the routing logic for each module inside the module folder)

Use pod architecture

As in organise your app around self-contained modules e.g.:

www/app/modules/home/router.js <-- sub router, calls methods in modules.js
www/app/modules/home/module.js <-- prepares endpoints - changing layout, initializing views & models etc
www/app/modules/home/views/... all the views (can have subfolders too)
www/app/modules/home/templates/
www/app/modules/home/models/
www/app/modules/home/collections

Start seeing your app in terms of views and sub views

A page doesn't consist of just one view. It would have perhaps a special "layout" view and inside that would be many views - one which splits the page in half, one which has pagination with more views inside for each page number, a view for a form with lots of sub views inside for each form element and message etc etc

You can start thinking of views as shadowing the DOM tree and divide logically - anything which you think is re-useable on your page make it a package (it's own views and models/collections if it needs them).

Models are for any data and any logic performed on data, if a view was showing anything from the server/api/database it would typically be passed to the view which would pass all or some of the model attributes to the template.

If that item displaying information was in a list, then a collection would manage each model for each item.

Do communication with models

If you find yourself wanting to communicate something from a view to another view, use a shared model. A view should be as decoupled as possible (it shouldn't need to be aware of it's parent).

Have an app state

Create a model called AppState to broadly communicate across the app using triggers and listens.

Have a packages folder (optional)

Whenever you come across stuff in your app which you think could be re-useable, even in other future apps, create a package. These would typically be hosted on their own git repos and you could pull them into projects using package.json or the command line.

Have a folder where you extend inter-app stuff

Have an extensions folder for modules which are consumed by multiple apps - e.g. your backbone extensions could go here. Or, if you created a package for forms but want to do something specifically for this app, then extend it here.

e.g. www/app/extensions/view.js
www/app/extensions/model.js
www/app/extensions/collection.js
www/app/extensions/buttons/link.js // Extending the link view from a "buttons" package.

assets

The reason why I would have an app/ folder in the public www/ folder is so that I could also have an assets folder in there for fonts and images etc:

www/assets/css
www/assets/images

Note: Maybe you want to try and keep assets in the module folders (inline with pod architecture). I haven't done this before but it's worth considering.

index.html

Typically if you are using CommonJS or AMD your index.html would just be boilerplate with no actual DOM elements and you would have one call in there to an entry js file. Since CommonJS has to compile this would just be something like <script src="/app.js"></script> but for AMD it would be more like:

<!--IF NOT BUILD-->
<script data-main="/app/config" src="/packages/require.js"></script>
<!--ELSE
<script src="/app.js"></script>
-->

So when running in dev (non-build) RequireJS will load up app/config.js but in build the whole app will be in app.js. There are various Grunt/Gulp build tasks which will do something like the above for you (obviously that conditional syntax is just made up).

Layouts

I would create a extensions/layout.js which extends extensions/view.js and it would be a simple extension that could have sub views like normal (e.g. header and footer), but also a special subview which I could attach any view to (for the body subview) e.g. a method like setContentView(view).

I would maybe create a module called layouts and in there have a directory modules/layout/default which has a view that has a header and footer subviews. Then reaching the index route would flow something like this:

app/router.js => app/modules/home/router.js => app/modules/home/module.js@index => setContentView(view from app/modules/home/views/index.js)"

Routing

I would have a app router located at e.g. www/app/router.js which could have some special routes but would largely just subroute with an object that pointed at sub routers:

subRouters: {
    'store-locator': StoreLocatorRouter,
    myaccount: MyAccountRouter,
    sitemap: SitemapRouter
}

I would make this possible by extending the normal Backbone router with something like (note in your extension you need to call initSubRouters in initialize) -

define([
    'underscore',
    'backbone'
],
function(_, Backbone) {

    'use strict';

    /**
     * Extended Backbone Boilerplate Router
     * @class extensions/router
     * @extends backbone/view
     */
    var Router = Backbone.Router.extend(
        /** @lends extensions/router.prototype */
        {

        /**
         * Holds reference to sub-routers
         * @type {Object}
         */
        subRouters: {},

        /**
         * Adds sub-routing
         * based on https://gist.github.com/1235317
         * @param {String} prefix The string to be prefixed to the route values
         */
        constructor: function(options) {
            if (!options) {
                options = {};
            }

            var routes = {}, prefix = options.prefix;

            if (prefix) {
                // Ensure prefixes have exactly one trailing slash
                prefix.replace(/\/*$/, '/');
            } else {
                // Prefix is optional, set to empty string if not passed
                prefix = '';
            }

            if (prefix) {
                // Every route needs to be prefixed
                _.each(this.routes, function(callback, path) {
                    if (path) {
                        routes[prefix + '/' + path] = callback;
                    } else {
                        // If the path is "" just set to prefix, this is to comply
                        // with how Backbone expects base paths to look gallery vs gallery/
                        routes[prefix + '(/)'] = callback;
                    }
                });

                // Must override with prefixed routes
                this.routes = routes;
            }

            // .navigate needs subrouter prefix
            this.prefix = prefix;

            // Required to have Backbone set up routes
            Backbone.Router.prototype.constructor.apply(this, arguments);
        },

        /**
         * Sets up 'beforeRoute' event.
         */
        initialize: function() {
            // This is a round about way of adding a beforeRoute event and must
            // happen before any other routes are added.
            Backbone.history.route({
                test: this.beforeRoute
            }, function() {});
        },

        /**
         * Called before routes.
         * @return {Boolean} false This ensures the 'route' is disabled.
         */
        beforeRoute: function() {
            Backbone.history.trigger('beforeRoute');
            return false;
        },

        /**
         * Adds prefix to navigation routes
         * @param  {String} route   Non-prefixed route
         * @param  {Object} options Passed through to Backbone.router.navigate
         */
        navigate: function(route, options) {
            if (route.substr(0, 1) !== '/' && route.indexOf(this.prefix.substr(0,
                this.prefix.length - 1)) !== 0) {
                route = this.prefix + route;
            }
            Backbone.Router.prototype.navigate.call(this, route, options);
        },

        /**
         * Initializes sub-routers defined in `this.subRouters`
         */
        initSubRouters: function() {
            _.each(this.subRouters, function(Router, name) {
                this[name] = new Router({
                    prefix: name
                });
            }, this);
        }

    });

    return Router;
});

这篇关于骨干工程组织的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆