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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (67)

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

对于那些不熟悉剑道的人来说,剃刀的语法会被翻译成Javascript,如下所示:

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

  • 共享//包含共享的js文件-file1.js -file2.js
  • 页面//每页一个文件
    • page1.js
    • page2.js
    • ...
    • Ticket.js //第4页:)

每个js文件都是用以下模式定义的单独模块: 注意:内部init函数将每个回调函数注册到窗口事件并偶尔会注册一个$(document).ready(function(){})块。

;(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;
}());   

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

HTML / JAVASCRIPT

@(Html.Kendo().DropDownList()
      .Name("Users")
      .DataValueField("Id")
      .DataTextField("Username")
      .DataSource(d => d.Read(r => r.Action("UsersAsJson", "User"))
                        .Events(e => e.RequestStart("onRequestStart"))))



var settings = {};

var ticket = new window.Ticket(settings);

function onRequestStart(e){
    ticket.onRequestStart(e);
}

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

首先,我做错了什么? 其次,我的设计模式适合Javascript测试框架吗? 第三,哪些是Jquery插件的必备场景?

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

提问于
用户回答回答于

文件夹结构

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

  • app (应用程序代码)
    • classes (reusable)
  • modules (singleton)

  • lib (软件包管理器/ grunt / gulp / ...)
    • jquery (正确的库名称/未分类的dist文件或根文件)
    • kendo

文件名称

代表什么事情,并能够在瞬间重用它,会减少你的开发时间。选择正确的名字是有价值的,因为我相信你知道。我的文件名通常始于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就绪事件来绑定原型和模块。

所以你可能想重新考虑你分页的想法。相信我,我一直在那里。在某些时候,您会注意到功能增长过大以便为所有页面单独配置和重复配置。

文件结构

说实话,我喜欢@TxRegex的Tip 1回答最多,除了绑定命名空间并在文件加载时将其从文件传递到文件时,我们还有一点小小的补充。

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

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

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

捆绑

努力实现从LIB单一的捆绑和精缩文件的应用程序,在装headasync的产品发布。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中的渲染阻塞问题,并加速加载过程,从而提高搜索引擎优化。此外,您还可以即时将移动设备布局和桌面布局的功能组合起来,而不会出现内存问题或不稳定行为。很好地完成缩减,并且在从加载程序文件调用实例时产生很小的开销。由于单个软件包将缓存在整个页面中,这取决于您可以从软件包中删除多少依赖项或库。理想情况下适用于代码可以共享并插入不同项目的大中型项目。

更多信息在另一篇文章中

结论

首先,我做错了什么?

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

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

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

第三,哪些是Jquery插件的必备场景?

  • 模块化方法
  • 针对多个实例的可配置方法(提示:移动逻辑中的所有字符串,查看Kendo如何使用对象文字)
  • 包管理者将“垃圾”从“黄金”
  • grunt / gulp / ...安装程序将scss和css从js中分离出来
  • 尝试实现数据属性绑定,因此一旦写入完成,就可以通过HTML配置新实例。

写一次,在需要的地方轻松调整,并配置很多!

用户回答回答于

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

技巧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);

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

扫码关注云+社区