寻求有关如何将 JS 代码与视图解耦的建议

发布于 2024-12-14 02:49:01 字数 2986 浏览 4 评论 0原文

JavaScript 在大多数 Web 解决方案中扮演着越来越重要的角色,但我发现将 JS 代码与视图细节分离比服务器端代码困难得多。

人们使用什么技术来解耦 JS 代码,以减少维护负担并使其尽可能适应较小的视图更改?

为了提供一个具体的例子,我有一个看起来有点像下面这样的视图:

<div id="signin" class="auth">
    <h2>Sign in</h2>
    <div id="resultbox" class="box" style="display: none;"></div>

    <div class="form">
        <p>Required fields are marked <span class="yellow">*</span></p>
        <form action="@Url.Action( MVC.Account.Authenticate() )" method="post" id="authform">
            <label for="Identification">Alias or email <span class="yellow">*</span></label></p>
            @Html.TextBoxFor( m => m.Identification, new { size="22", tabindex="1" } )

            <label for="Password">Password <span class="yellow">*</span></label></p>
            @Html.PasswordFor( m => m.Password, new { size="22", tabindex="2" } )

            <a href="#" class="but_styled" id="btn_signin" tabindex="3">Sign in</a>
        </form>
    </div>
</div>

JS 部分分为两个文件 - 第一个是我的“业务逻辑”类,第二个主要用于接线:

(function (pp, $, undefined) {
    pp.account = {};

    function hideResultBox() {
        var box = $('#resultbox');
        box.hide();
        box.removeClass('info_box').removeClass('error_box');
    }
    function showResultBox(result) {
        var box = $('#resultbox');
        if (result.Success === true) {
            $('#resultbox_content').html(result.Message);
            box.addClass('info_box').show();
        } else {
            $('#resultbox_content').html(result.Message);
            box.addClass('error_box').show();
            var messages = '';
            for (var error in result.Errors) {
                messages += '<li>' + result.Errors[error] + '</li>';
            }
            if (messages !== '')
                $('#resultbox_content').append('<br /><ul>' + messages + '</ul>');
        }
    }

    pp.account.authenticate = function (data) {
        hideResultBox();
        $.post('/account/authenticate', data, function (result) {
            showResultBox(result);
            if (result.Success === true) {
                setTimeout(function () { window.location = result.Url; }, 1000);
            }
        });
    };
})(window.pressplay = window.pressplay || {}, jQuery);

接线部分:

$(document).ready(function () {
    $('#signin a').live('click', function () {
        var form = $(this).closest('form').serialize();
        pressplay.account.authenticate(form);
        return false;
    });
});

上面代码的问题在于它与视图的外观(必须存在的元素 id、结构等)紧密相关,但我对如何改进它没有好主意。

在我看来,如果我继续沿着这条路走下去,JS 文件最终会变成一堆正确封装的逻辑与各种特定于视图的东西相结合。

这是我能做的最好的事情吗?或者我可以应用任何技术来避免这种混乱吗?

我自己关于如何改进这一点的想法围绕着构建某种服务器端生成的“元素选择器”JS 类,这样我就可以编写 JS,而无需使用对类和元素 id 的大量字符串引用。但我不确定如何生成它,或者最终维护起来是否会更糟糕。

JavaScript plays an increasingly important role in most web solutions, but I find that it is much harder to decouple the JS code from the view specifics than it is for the server-side code.

What techniques are people using to decouple the JS code, in order to reduce the maintenance burden and to make it as resilient to minor view changes as possible?

To provide a concrete example, I have a view that looks sort of like the following:

<div id="signin" class="auth">
    <h2>Sign in</h2>
    <div id="resultbox" class="box" style="display: none;"></div>

    <div class="form">
        <p>Required fields are marked <span class="yellow">*</span></p>
        <form action="@Url.Action( MVC.Account.Authenticate() )" method="post" id="authform">
            <label for="Identification">Alias or email <span class="yellow">*</span></label></p>
            @Html.TextBoxFor( m => m.Identification, new { size="22", tabindex="1" } )

            <label for="Password">Password <span class="yellow">*</span></label></p>
            @Html.PasswordFor( m => m.Password, new { size="22", tabindex="2" } )

            <a href="#" class="but_styled" id="btn_signin" tabindex="3">Sign in</a>
        </form>
    </div>
</div>

The JS part is split into two files - the first is my "business logic" class and the second is used mostly for wiring:

(function (pp, $, undefined) {
    pp.account = {};

    function hideResultBox() {
        var box = $('#resultbox');
        box.hide();
        box.removeClass('info_box').removeClass('error_box');
    }
    function showResultBox(result) {
        var box = $('#resultbox');
        if (result.Success === true) {
            $('#resultbox_content').html(result.Message);
            box.addClass('info_box').show();
        } else {
            $('#resultbox_content').html(result.Message);
            box.addClass('error_box').show();
            var messages = '';
            for (var error in result.Errors) {
                messages += '<li>' + result.Errors[error] + '</li>';
            }
            if (messages !== '')
                $('#resultbox_content').append('<br /><ul>' + messages + '</ul>');
        }
    }

    pp.account.authenticate = function (data) {
        hideResultBox();
        $.post('/account/authenticate', data, function (result) {
            showResultBox(result);
            if (result.Success === true) {
                setTimeout(function () { window.location = result.Url; }, 1000);
            }
        });
    };
})(window.pressplay = window.pressplay || {}, jQuery);

And the wiring part:

$(document).ready(function () {
    $('#signin a').live('click', function () {
        var form = $(this).closest('form').serialize();
        pressplay.account.authenticate(form);
        return false;
    });
});

The problem with the code above is how closely it is tied to how the view looks (element ids that must be present, structure, etc.), but I have no good ideas on how it could be improved.

It just seems to me that, if I continue down this path, the JS files end up being a mess of properly encapsulated logic combined with all sorts of view-specific stuff.

Is this the best I can hope to do or are there any techniques I can apply to avoid some of this mess?

My own ideas on how to improve this revolve around building some sort of server-side generated "element selectors" JS class, such that I could write the JS without using quite as many string references to classes and element ids. But I am not sure how one would go about generating that or whether it'll just be worse to maintain in the end.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清风夜微凉 2024-12-21 02:49:01

一些想法:

  • 不要将功能基于元素“id”值,除非视图功能确实涉及“自然”唯一的元素。尽可能使用类。
  • 使用“data-”属性将功能配置信息从 HTML 传递到 JavaScript。 (但是不要将代码放入“data-”属性中;在我看来,这是一个非常糟糕的主意。对此表示抱歉,Knockout 的粉丝。)
  • 使用事件发布/订阅系统在功能“包”之间进行通信,以控制相互依赖性。
  • 使用 类来描述不同类型的页面,以提高“连接”的效率。这样,功能代码可以非常快速地判断给定页面是否需要考虑它。

编辑 - 澄清最后一个:假设您有一些与

页面相关的功能,例如日期选择器或自动完成输入,或其他类似的东西。有时,有些功能仅对某些种类的表单有意义。在这种情况下,使用主体类可以轻松地确定某个功能是否应该在 DOM 中查找要影响的元素:

if ($('body').is('.password-form')) { 
  // perform specific initializations
}

我当前涉及的 Web 应用程序不是太大,但也不是微不足道的。我发现我可以为整个网站保留一个大的 JavaScript 集合,并将其相同地包含在每个页面上,并且我没有任何性能问题(目前)(IE7 很慢,但这种技术并不是导致那)。

Some thoughts:

  • Don't base functionality on element "id" values, except for view features that really do involve elements that are "naturally" unique. Use classes instead whenever possible.
  • Use "data-" attributes to communicate feature configuration information from HTML to JavaScript. (But don't put code in your "data-" attributes; in my opinion that's a really terrible idea. Sorry about that, fans of Knockout. To each his own.)
  • Use an event pub/sub system to communicate between feature "bundles" to keep interdependencies under control.
  • Use <body> classes to characterize different sorts of pages in order to increase the efficiency of your "wiring". That way, feature code can tell very quickly whether it needs to be even considered for a given page.

edit — to clarify the last one: let's say you've got some features that have to do with <form> pages, like date pickers, or auto-complete inputs, or other things like that. Sometimes there are features that only make sense for certain kinds of forms. In such cases, using a body class can make it simple to figure out whether a feature should bother to look around the DOM for elements to affect:

if ($('body').is('.password-form')) { 
  // perform specific initializations
}

The web application I'm currently involved with isn't too big, but it's not trivial either. I find that I can keep just one big collection of JavaScript for the entire site and include it identically on every page, and I don't have any performance concerns (currently) (well IE7 is slow, but this technique is not the cause of that).

时常饿 2024-12-21 02:49:01

javascript 仍然是视图的一部分,所以我认为让 javascript 与视图交织在一起并不是太糟糕的设计。

至于如何让生活变得更轻松,我认为您确实需要尽一切努力找到常见的 javascript 编码情况,并将它们从视图中重构出来,并放入一个可以使用和重用的通用库中。例如,如果您将 hideResultBox 重写为如下所示:

function hideElement(var jqElementSelector, var cssClass1, var cssClass2 var cssClass3) {
    var element = $(jqElementSelector);
    element.hide();
    element.removeClass(cssClass1).removeClass(cssClass2).removeClass(cssClass3);
}

然后您可以将此视图中的调用替换为:

CommonLib.hideElement('#resultbox', 'info_box', 'error_box');

这样至少您不需要维护太多的 javascript。它还可以更轻松地从服务器生成 javascript 调用点,因为您无需传递逻辑,而只需传递名称和名称。或 ids,并且不必担心在 javascript 逻辑中失去任何类型的 javascript 智能感知能力。

The javascript is still a part of your view, so I don't believe is too bad of a design to have your javascript being intertwined with your view.

As far as what could be done to make life easier, I think you really need to do whatever you can to find common javascript coding situations, and refactor them out of the view, and into a common library where it can be used and reused. For example, if you rewrote your hideResultBox to look like this:

function hideElement(var jqElementSelector, var cssClass1, var cssClass2 var cssClass3) {
    var element = $(jqElementSelector);
    element.hide();
    element.removeClass(cssClass1).removeClass(cssClass2).removeClass(cssClass3);
}

And then you could replace your call in this view to be this:

CommonLib.hideElement('#resultbox', 'info_box', 'error_box');

This way at least you don't have as much javascript to maintain. It would also make it easier to generate the javascript call points from the server, since you're not passing in logic, just names & or ids, and not have to worry about loosing any sort of javascript intellisense abilities in your javascript logic.

烟凡古楼 2024-12-21 02:49:01

我会以更“功能性的方式”来思考,而不是 ID……
也就是首先想一下javascript代码的作用是什么,
然后以您的视图可以使用的方式编写代码。

你看到区别了吗?

您正在做的方式是:首先查看,然后是 javascript 代码...我的建议是:首先是 javascript,然后是视图元素。这样你就可以确信你正在编写一个可重用的 javascript 代码库。

不使用 ID,而使用类。让代码就像是一个通用库,以便您可以重用。不需要固定的结构,例如“.myClass div a”...该 div 正在扼杀可重用性...将其替换为一些内部类:“.myClass .innerClass a”,这样您就可以将类应用到您的视图,并且不要让视图主导代码。

尽可能减少直接放入视图中的 JavaScript 代码。只需使用对您自己的库的方法调用,使用简单且描述性的方法名称。尝试将你的 JavaScript 放在一起。尝试不使用声明性 javascript...因为我看到你已经这样做了。这就是要走的路! =)

不要担心可维护性,因为解耦是提高可维护性的一种行之有效的方法。

Instead of IDs, I'd think in a more "functional way"...
that is, first think about what is the function of the javascript code,
then make the code in a way that it can be used by your view.

Do you see the difference?

The way you are doing is: view first, then javascript code... what I suggest is: javascript first and then the view elements. This way you are quite sure you are coding a reusable javascript code-base.

Instead of using IDs, use classes. Make the code like it was a general purpose lib, that you can reuse. Do not require fixed structure, for example ".myClass div a"... that div is killing reusability... replace it by some inner class: ".myClass .innerClass a", this way you can apply the classes to your view, and not let the view dominate the code.

Minimize as much as you can the javascript code put directly in your views. Just use method calls to your own libraries, use simple and descriptive method names. Try to put your javascript together. Try not using declarative javascript... as I see you already do this. This is the way to go! =)

Don't worry about maintainability, because decoupling is a proved way of increasing maintainability.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文