Javascript代码组织数据驱动的应用程序

时间:2017-05-17 07:19:49

标签: javascript jquery asp.net-mvc design-patterns kendo-ui

我目前正在开发中型/大型数据驱动的 Asp.net MVC应用程序的前端,我对正确的代码组织/设计有些怀疑模式要遵循。 Web应用程序由多个页面组成,其中包含使用Razor模板定义的许多Kendo UI MVC小部件。

对于那些不熟悉Kendo的人,剃刀语法被转换为Javascript,如下面的代码片段所示: the other

我在我的脚本文件夹中定义了两个主要文件夹,我按照以下方式构建了我的js文件:

  • shared //包含共享的js文件 -file1.js -file2.js

  • 页面//每页一个文件

    • page1.js
    • page2.js
    • ...
    • Ticket.js //第4页:)

每个js文件都是使用以下模式定义的单独模块:
注意:内部meet函数会将每个回调函数注册到窗口事件,有时会注册init块。

$(document).ready(function(){})

一旦我需要在模块中定义的Javascript函数,我在页面中包含相关的Javascript文件。 我的对象的一个​​istance存储在一个变量中,最重要的是,一个函数绑定到widget事件(参见:onRequestStart)。

HTML / JAVASCRIPT

;(function () {
    "use strict";

    function Ticket(settings) {
        this.currentPageUrls = settings.currentPageUrls;
        this.currentPageMessages = settings.currentPageMessages;
        this.currentPageEnums = settings.currentPageEnums;
        this.currentPageParameters = settings.currentPageParameters;         


        this.gridManager = new window.gridManager(); //usage of shared modules

        this.init();
    }

    Ticket.prototype.init = function () {           

            $("form").on("submit", function () {
                $(".window-content-sandbox").addClass("k-loading");
            });

            ...
    }    

    Ticket.prototype.onRequestStart = function (e) {

        ...
    }

    //private functions definition
    function private(a, b, c){

    }

    window.Ticket = Ticket;
}());   

我觉得我的设计模式可能和其他前端delevoper一样不友好,主要是因为我选择不在Jquery插件中实现Javascript模块。

首先,我做错了吗? 第二,我的设计模式是否适合Javascript测试框架? 第三,这是Jquery插件的必备方案吗?

更新

通过上述Razor语法添加了Javascript输出。

Kendo server side-wrappers

3 个答案:

答案 0 :(得分:5)

文件夹结构

在功能(共享)和模块(模块化方法)方面,开发或应用程序代码应代表您在HTML中可以遇到的内容。对解决方案进行简单的ctrl + f应该会产生所有可能的更改。根据多年来的经验,我个人更喜欢将其划分为:

  • app (应用程序代码)
    • classes (可重复使用)
    • modules (singleton)
  • lib (包管理员/ grunt / gulp /...)
    • jquery (正确的库名称/未明确的dist文件或根文件)
    • 剑术

文件名

代表什么做了什么,并且能够在眨眼之间重复使用它将会缩短你的开发时间。选择合适的名字具有价值,因为我确信你知道。我的文件名通常以namespace开头,通常是短暂的,然后是可重复使用的"搜索"术语:

  • 应用/原型
    • ns.calendar.js (多个配置)
    • ns.maps.js (组合或单次使用)
    • ns.places.js (表格或地图附加组件)
    • ns.validation.js (多种形式和一般处理)
  • 应用/单身
    • ns.cookiebox.js (单一配置)
    • ns.socialmedia.js (单一配置)
    • ns.dom.js (为dom更正,全局调整大小事件,小部件,...提供了一个位置)

要添加,您所谓的共享,是指全球化的功能。一个很好的例子是使用下划线库。或者自己创建一系列功能(设备检测,油门,帮助器),以便在整个项目中重复使用=> ns.fn.js 由于您只在整个命名空间中添加它们一次,因此它也可以构建为单例,可以添加到模块文件夹中或直接添加到应用程序根目录中。

最后添加一个启动你的控制点的加载文件=>应用程序根目录中的ns.load.js。该文件包含单个DOM ready事件以绑定原型和模块。

所以你可能想重新考虑分成页面的想法。相信我,我一直在那里。在某些时候,您会注意到功能如何变得太大,以便分别配置所有页面,反复配置。

文件结构

老实说,我喜欢@TxRegex的提示1回答最多,只需添加一小部分来绑定命名空间,并在文件加载时将其从文件传递给文件。

核心原则:IIFE绑定到窗口对象

window.NameSpace = (function($, ns){
    'strict'
    function private(){}
    var x;
    ns.SearchTerm = {};
    return ns;
}(window.jQuery, window.NameSpace || {}));

有关更多示例代码,我想指出我的github account

捆绑

尝试从lib到app实现单个捆绑和缩小的文件,加载到head async上用于生产版本。在defer上使用分离和未分解的脚本文件进行开发和调试。如果执行此操作,则必须避免整个项目中具有全局依赖性的内联脚本。

  • js / lib / ** / *。js的路径(通常分开以保持顺序)
  • js / app / ns.load.js的路径
  • js / app / ns.fn.js的路径
  • js / app / ** / *。js的路径(自动更新捆绑包)

输出=> ns.bundle.js        => ns.bundle.min.js

通过这种方式,您可以避免JavaScript中的渲染阻塞问题并加快加载过程,从而加快搜索引擎优化。此外,您还可以动态组合移动布局和桌面布局的功能,而不会出现内存问题或不稳定的行为。从加载器文件调用实例时,实际上很好地缩小并产生很少的开销。由于单个包将在整个页面中缓存,因此所有这些都取决于您可以从包中删除多少依赖项或库。理想情况下适用于可以共享代码并插入不同项目的中型和大型项目。

another post中的更多信息。

结论

首先,我做错了吗?

  • 完全没有,你的模块化方法似乎没问题......
  • 它缺少一个全局命名空间,如果没有至少一个,这是很难避免的。您为每个模块创建一个,但最好将它们全部分组到一个名称空间下,这样您就可以将库代码与窗口对象中的应用程序代码区分开来。
  • Kendo似乎在创建内联脚本?你不能反击放置服务器端吗?

其次,我的设计模式是否适合Javascript测试框架?

  • 除了Kendo实例,您可以添加一个图层用于测试目的。请记住,如果jQuery是您的内联依赖项,那么您必须对其进行加载。否则=> jQuery is undefined
  • 如果您无法控制内联脚本,则从捆绑包中排除Kendo依赖项。转到</body>捆绑解决方案。

第三,哪些是Jquery插件的必备方案?

  • 模块化方法
  • 多个实例的可配置方法(提示:从逻辑中移动所有字符串,查看Kendo如何使用对象文字)
  • 包管理员将&#34;垃圾&#34;分开来自&#34; gold&#34;
  • grunt / gulp / ...设置将scss和css与js
  • 分开
  • 尝试实现数据属性绑定,因此一旦编写完所有内容,就可以通过HTML配置新实例。

写一次,在必要时轻松适应并配置充足!

答案 1 :(得分:1)

组织和模式似乎很好,但我有一些提示:

提示1:

您可以返回对象,而不是在模块中设置特定的全局变量。所以不要这样做:

;(function () {
"use strict";

    function Ticket(settings) {
        console.log("ticket created", settings);
    }
    ...
    window.Ticket = Ticket;
}()); 

你会这样做:

;window.Ticket = (function () {
"use strict";

    function Ticket(settings) {
        console.log("ticket created", settings);
    }
    ...
    return Ticket;
}()); 

这样做的原因是能够获取模块代码并在需要时为其提供不同的全局变量名称。如果存在名称冲突,您可以将其重命名为MyTicket或其他任何内容,而无需实际更改模块的内部代码。

提示2:

忘记提示1,全局变量发臭。为什么不创建对象管理器并使用单个全局变量来管理所有对象,而不是为每个对象类型创建单独的全局变量:

window.myCompany = (function () {
    function ObjectManager(modules) {
        this.modules = modules || {};
    }

    ObjectManager.prototype.getInstance = function(type, settings) {
        if (!type || !this.modules.hasOwnProperty(type)) {
            throw "Unrecognized object type:";
        }
        return new this.modules[type](settings);
    };

    ObjectManager.prototype.addObjectType = function(type, object) {
        if (!type) {
            throw "Type is required";
        }
        if(!object) {
            throw "Object is required";
        }
        this.modules[type] = object;
    };

    return new ObjectManager();
}());

现在,您可以使用附加了公司名称的单个全局对象管理每个模块。

;(function () {
"use strict";

    function Ticket(settings) {
        console.log("ticket created", settings);
    }
    ...
    window.myCompany.addObjectType("Ticket", Ticket);
}()); 

现在,您可以轻松地为每个对象类型获取一个实例,如下所示:

var settings = {test: true};
var ticket = window.myCompany.getInstance("Ticket", settings);

你只需担心一个全局变量。

答案 2 :(得分:-1)

您可以尝试在不同的组件中分隔文件,因为每个组件都有一个文件夹。

例如:第1页是关于矩形的,所以你在该文件夹中创建一个文件夹调用矩形,你创建了3个文件rectangle.component.html,rectangle.component.css,rectangle.component.js(可选的rectangle.spec.js for测试)。

app
└───rectangle
        rectangle.component.css
        rectangle.component.html
        rectangle.component.js

所以,如果有任何不好的事情发生在矩形上,你知道问题在哪里

隔离变量并在正确的位置执行的一种好方法是使用路由器基本上它在URL处检查并执行您指向该页面的代码部分

希望如果您需要更多帮助,请告知我们。