Các thực tiễn tốt nhất được chấp nhận phổ biến xung quanh tổ chức mã trong JavaScript [đã đóng]


561

Khi các khung JavaScript như jQuery làm cho các ứng dụng web phía máy khách phong phú hơn và nhiều chức năng hơn, tôi đã bắt đầu nhận thấy một vấn đề ...

Làm thế nào trên thế giới bạn giữ tổ chức này?

  • Đặt tất cả các trình xử lý của bạn vào một vị trí và viết các hàm cho tất cả các sự kiện?
  • Tạo chức năng / lớp để bọc tất cả chức năng của bạn?
  • Viết như điên và chỉ hy vọng nó hoạt động tốt nhất?
  • Từ bỏ và có được một sự nghiệp mới?

Tôi đề cập đến jQuery, nhưng nó thực sự là bất kỳ mã JavaScript nào nói chung. Tôi thấy rằng khi các dòng trên các dòng bắt đầu chồng chất, việc quản lý các tệp tập lệnh hoặc tìm thấy những gì bạn đang tìm kiếm sẽ khó khăn hơn. Rất có thể những biểu tượng lớn nhất mà tôi tìm thấy là có rất nhiều cách để làm điều tương tự, thật khó để biết cách nào là cách thực hành tốt nhất thường được chấp nhận hiện nay.

Có bất kỳ khuyến nghị chung nào về cách tốt nhất để giữ các tệp .js của bạn đẹp và gọn gàng như phần còn lại của ứng dụng của bạn không? Hay đây chỉ là vấn đề của IDE? Có một lựa chọn tốt hơn ngoài đó?


BIÊN TẬP

Câu hỏi này nhằm mục đích nhiều hơn về tổ chức mã chứ không phải tổ chức tệp. Đã có một số ví dụ thực sự tốt về việc hợp nhất các tệp hoặc chia nội dung xung quanh.

Câu hỏi của tôi là: cách thực hành tốt nhất thường được chấp nhận hiện nay để tổ chức mã thực tế của bạn là gì? Cách của bạn là gì, hoặc thậm chí là một cách được đề xuất để tương tác với các thành phần trang và tạo mã có thể sử dụng lại mà không xung đột với nhau?

Một số người đã liệt kê các không gian tên đó là một ý tưởng tốt. Một số cách khác, cụ thể hơn là xử lý các yếu tố trên trang và giữ cho mã được tổ chức và gọn gàng?


ai đó thực sự đã dành thời gian để nói về chính tổ chức mã, chứ không phải "chỉ" công cụ nào anh ta sử dụng để ghép và nén các tệp JS của mình: stackoverflow.com/questions/16736483/ trộm
Adrien Be

Câu trả lời:


183

Sẽ tốt hơn rất nhiều nếu javascript có không gian tên được tích hợp, nhưng tôi thấy rằng việc tổ chức những thứ như Dustin Diaz mô tả ở đây giúp tôi rất nhiều.

var DED = (function() {

    var private_var;

    function private_method()
    {
        // do stuff here
    }

    return {
        method_1 : function()
            {
                // do stuff here
            },
        method_2 : function()
            {
                // do stuff here
            }
    };
})();

Tôi đặt các "không gian tên" khác nhau và đôi khi các lớp riêng lẻ trong các tệp riêng biệt. Thông thường tôi bắt đầu với một tệp và khi một lớp hoặc không gian tên đủ lớn để bảo đảm nó, tôi tách nó ra thành tệp riêng. Sử dụng một công cụ để kết hợp tất cả các tập tin của bạn để sản xuất là một ý tưởng tuyệt vời là tốt.


24
Tôi thường gọi đó là "cách crockford". +1 từ tôi
Matt Briggs

4
Bạn thậm chí có thể đi xa hơn một chút. Xem liên kết này: Wait-till-i.com/2007/08/22/ Mạnh
MKroehnert

4
@MattBriggs hay còn gọi là module patternvà nó dựa trên IIFE pattern.
Adrien Be

Bạn không cần phải xuất các lớp bằng cách nào đó? Làm thế nào để một đối tượng được tạo ra từ bên ngoài của một mô-đun như vậy? Hoặc nên có một phương thức createNewSomething()trong đối tượng trả về, vì vậy việc tạo đối tượng chỉ xảy ra trong mô-đun? Hừm ... Tôi mong rằng các lớp (nhà xây dựng) có thể nhìn thấy từ bên ngoài.
robsch

@robsch Ví dụ của anh ấy không có bất kỳ tham số nào, nhưng hầu hết sẽ. Xem ví dụ của tôi ở đây để biết cách này thường được thực hiện (TypeScript, nhưng giống nhau 99%): repl.it/@fatso83/Module-Potype-in-TypeScript
oligofren

88

Tôi cố gắng tránh bao gồm bất kỳ javascript nào với HTML. Tất cả các mã được gói gọn trong các lớp và mỗi lớp nằm trong tệp riêng của nó. Để phát triển, tôi có các thẻ <script> riêng để bao gồm mỗi tệp js, nhưng chúng được hợp nhất vào một gói lớn hơn để sản xuất để giảm chi phí cho các yêu cầu HTTP.

Thông thường, tôi sẽ có một tệp js 'chính' cho mỗi ứng dụng. Vì vậy, nếu tôi đang viết một ứng dụng "khảo sát", tôi sẽ có một tệp js có tên là "Survey.js". Điều này sẽ chứa điểm nhập vào mã jQuery. Tôi tạo các tham chiếu jQuery trong thời gian khởi tạo và sau đó chuyển chúng vào các đối tượng của tôi dưới dạng tham số. Điều này có nghĩa là các lớp javascript là 'thuần' và không chứa bất kỳ tham chiếu nào đến các id hoặc tên lớp CSS.

// file: survey.js
$(document).ready(function() {
  var jS = $('#surveycontainer');
  var jB = $('#dimscreencontainer');
  var d = new DimScreen({container: jB});
  var s = new Survey({container: jS, DimScreen: d});
  s.show();
});

Tôi cũng thấy quy ước đặt tên là quan trọng cho khả năng đọc. Ví dụ: Tôi thêm 'j' cho tất cả các phiên bản jQuery.

Trong ví dụ trên, có một lớp được gọi là DimScreen. (Giả sử điều này làm mờ màn hình và bật lên một hộp cảnh báo.) Nó cần một phần tử div có thể phóng to để che màn hình, sau đó thêm một hộp cảnh báo, vì vậy tôi chuyển vào một đối tượng jQuery. jQuery có một khái niệm trình cắm thêm, nhưng nó có vẻ hạn chế (ví dụ: các trường hợp không liên tục và không thể truy cập được) không có mặt trái thực sự. Vì vậy, lớp DimScreen sẽ là một lớp javascript tiêu chuẩn tình cờ sử dụng jQuery.

// file: dimscreen.js
function DimScreen(opts) { 
   this.jB = opts.container;
   // ...
}; // need the semi-colon for minimizing!


DimScreen.prototype.draw = function(msg) {
  var me = this;
  me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
  //...
};

Tôi đã xây dựng một số ứng dụng khá phức tạp bằng cách sử dụng phương pháp này.


15
Tôi thấy rằng sử dụng $như một tiền tố tên biến là một thực tế phổ biến hơn, nhưng tôi có thể sai. Vì vậy, $s = $('...')thay vì jS = $('...'), chỉ là một vấn đề ưu tiên tôi đoán. Thật thú vị, vì ký hiệu Hungary được cho là mùi mã. Thật kỳ lạ khi một số quy ước / sở thích mã JavaScript của tôi khác với các quy ước mã hóa C # / Java của tôi.
jamiebarrow

9
@jamie Đây không phải là mùi mã trong trường hợp này, nó chính xác là một trong số ít trường hợp tiếng Hungary tốt . Bạn có thể muốn đọc .
Dan Abramov

3
@DanAbramov cảm ơn bạn đã liên kết. Tôi thực sự phải đọc tất cả các blog của Joel, anh ấy giải thích mọi thứ rất tốt. Chắc chắn xứng đáng với danh tiếng / danh tiếng mà anh ấy có. Tôi sẽ đề cập đến Systems Hungariannhư một mùi mã và Apps Hungariannhư là một thực tế từ bây giờ :)
jamiebarrow

Tôi đoán trong thế giới C #, nó cũng có thể là một bài viết tuyệt vời để thúc đẩy việc sử dụng var, bây giờ tôi nghĩ về nó. Hầu hết các đối số chống lại việc sử dụng varlà nơi bạn sẽ không chắc chắn về 'loại' của những gì được trả lại, nhưng tôi đoán rằng các đối số nên chống lại không biết 'lớp' của những gì được trả về. Nếu sử dụng Ứng dụng Hungary, bạn không nên có mối quan tâm đó thì ... thú vị.
jamiebarrow

3
@Marnen: Tôi thấy quan điểm của bạn, nhưng nó không vô dụng như một hướng dẫn cho lập trình viên. Tiền tố $ nhắc nhở tôi khi đọc mã của tôi sau này và do đó giúp hiểu nhanh hơn.
Sean

39

Bạn có thể chia tập lệnh của mình thành các tệp riêng biệt để phát triển, sau đó tạo phiên bản "phát hành" nơi bạn kết hợp tất cả chúng lại với nhau và chạy YUI Compressor hoặc một cái gì đó tương tự trên đó.


Đôi khi có các kịch bản javascript không cần thiết. Thật lãng phí khi gửi chúng cho khách hàng. Tôi nghĩ rằng nó chỉ gửi những gì cần thiết. Tất nhiên, đối với một ứng dụng web được sử dụng cả ngày, chẳng hạn như ứng dụng mạng nội bộ, có thể tốt hơn là gửi toàn bộ lô cùng một lúc, trong lần tải trang đầu tiên.
DOK

2
Biên dịch @DOK nên bao gồm cắt bỏ những thứ không sử dụng.
aehlke

Ngoài ra còn có khái niệm tải lười biếng để thử và giảm nhu cầu băng thông, nơi bạn tải trang ban đầu và sau đó thực hiện tải không đồng bộ các tệp tập lệnh cần thiết (như đã đề cập trong các câu trả lời khác cho câu hỏi này). Mặc dù, điều đó có thể yêu cầu nhiều yêu cầu hơn và thực sự có thể ít sử dụng hơn. @DOK, nếu JS được lưu trữ, một yêu cầu trung bình có thể tốt hơn một vài yêu cầu nhỏ.
jamiebarrow

27

Lấy cảm hứng từ các bài đăng trước đó, tôi đã tạo một bản sao của Rakefile và các thư mục nhà cung cấp được phân phối với WysiHat (một RTE được đề cập bởi changelog) và thực hiện một vài sửa đổi để bao gồm kiểm tra mã với JSLint và thu nhỏ bằng YUI Compressor .

Ý tưởng là sử dụng Sprockets (từ WysiHat) để hợp nhất nhiều JavaScripts vào một tệp, kiểm tra cú pháp của tệp được hợp nhất với JSLint và rút gọn nó với YUI Compressor trước khi phân phối.

Điều kiện tiên quyết

  • thời gian chạy Java
  • đá quý ruby ​​và cào
  • Bạn nên biết cách đặt JAR vào Classpath

Bây giờ làm

  1. Tải về tê giác và đặt JAR ("js.jar") vào đường dẫn lớp của bạn
  2. Tải xuống máy nén YUI và đặt JAR (build / yuicompressor-xyz.jar) vào đường dẫn lớp của bạn
  3. Tải xuống WysiHat và sao chép thư mục "nhà cung cấp" vào thư mục gốc của dự án JavaScript của bạn
  4. Tải xuống JSLint cho Rhino và đặt nó vào thư mục "nhà cung cấp"

Bây giờ hãy tạo một tệp có tên "Rakefile" trong thư mục gốc của dự án JavaScript và thêm nội dung sau vào đó:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

Nếu bạn thực hiện mọi thứ chính xác, bạn sẽ có thể sử dụng các lệnh sau trong bảng điều khiển của mình:

  • rake merge - để hợp nhất các tệp JavaScript khác nhau thành một
  • rake check- để kiểm tra cú pháp mã của bạn (đây là tác vụ mặc định , vì vậy bạn chỉ cần gõ rake)
  • rake minify - để chuẩn bị phiên bản rút gọn của mã JS của bạn

Về việc hợp nhất nguồn

Sử dụng Sprockets, bộ xử lý trước JavaScript bạn có thể bao gồm (hoặc require) các tệp JavaScript khác. Sử dụng cú pháp sau để bao gồm các tập lệnh khác từ tệp ban đầu (được đặt tên là "main.js", nhưng bạn có thể thay đổi tập lệnh đó trong Rakefile):

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

Và sau đó...

Hãy xem Rakefile được cung cấp với WysiHat để thiết lập thử nghiệm đơn vị tự động. Dụng cụ tốt :)

Và bây giờ cho câu trả lời

Điều này không trả lời tốt câu hỏi ban đầu. Tôi biết và tôi xin lỗi về điều đó, nhưng tôi đã đăng nó ở đây vì tôi hy vọng nó có thể hữu ích cho người khác để tổ chức mớ hỗn độn của họ.

Cách tiếp cận vấn đề của tôi là thực hiện càng nhiều mô hình hướng đối tượng mà tôi có thể và tách các triển khai thành các tệp khác nhau. Sau đó, các xử lý nên càng ngắn càng tốt. Ví dụ với Listsingleton cũng tốt.

Và không gian tên ... chúng có thể được mô phỏng theo cấu trúc đối tượng sâu hơn.

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

Tôi không phải là fan hâm mộ lớn của hàng nhái, nhưng điều này có thể hữu ích nếu bạn có nhiều đối tượng mà bạn muốn chuyển ra khỏi phạm vi toàn cầu.


18

Tổ chức mã yêu cầu áp dụng các quy ước và tiêu chuẩn tài liệu:
1. Mã không gian tên cho một tệp vật lý;

Exc = {};


2. Các lớp nhóm trong các không gian tên javascript;
3. Đặt nguyên mẫu hoặc các hàm hoặc lớp liên quan để biểu diễn các đối tượng trong thế giới thực;

Exc = {};
Exc.ui = {};
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};
Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    ...
};


4. Đặt quy ước để cải thiện mã. Ví dụ, nhóm tất cả các hàm hoặc phương thức bên trong của nó trong thuộc tính lớp của một loại đối tượng.

Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    this.internal = {
        widthEstimates: function (tips) {
            ...
        }
        formatTips: function () {
            ...
        }
    };
    ...
};


5. Làm tài liệu về không gian tên, lớp, phương thức và biến. Khi cần thiết cũng thảo luận về một số mã (một số FI và Fors, họ thường triển khai logic quan trọng của mã).

/**
  * Namespace <i> Example </i> created to group other namespaces of the "Example".  
  */
Exc = {};
/**
  * Namespace <i> ui </i> created with the aim of grouping namespaces user interface.
  */
Exc.ui = {};

/**
  * Class <i> maskdInput </i> used to add an input HTML formatting capabilities and validation of data and information.
  * @ Param {String} mask - mask validation of input data.
  */
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};

/**
  * Class <i> domTips </i> used to add an HTML element the ability to present tips and information about its function or rule input etc..
  * @ Param {String} id - id of the HTML element.
  * @ Param {String} tips - tips on the element that will appear when the mouse is over the element whose identifier is id <i> </i>.
  */
  Exc.ui.domTips = function (id, tips) {
    this.domID = id;
    this.tips = tips;
    ...
};


Đây chỉ là một số mẹo, nhưng điều đó đã giúp rất nhiều trong việc tổ chức mã. Hãy nhớ bạn phải có kỷ luật để thành công!


13

Theo các nguyên tắc thiết kế OO tốt và các mẫu thiết kế sẽ đi một chặng đường dài để làm cho mã của bạn dễ duy trì và dễ hiểu. Nhưng một trong những điều tốt nhất tôi phát hiện ra gần đây là tín hiệu và vị trí hay còn gọi là xuất bản / đăng ký. Hãy xem http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html để thực hiện jQuery đơn giản.

Ý tưởng này được sử dụng tốt trong các ngôn ngữ khác để phát triển GUI. Khi một cái gì đó quan trọng xảy ra ở đâu đó trong mã của bạn, bạn xuất bản một sự kiện tổng hợp toàn cầu mà các phương thức khác trong các đối tượng khác có thể đăng ký. Điều này cho phép phân tách tuyệt vời của các đối tượng.

Tôi nghĩ Dojo (và Prototype?) Có phiên bản dựng sẵn của kỹ thuật này.

xem thêm Tín hiệu và khe cắm là gì?


Tôi đã thực hiện điều này trong jQuery. JS có một mô hình sự kiện tích hợp, vì vậy bạn không thực sự cần nhiều hỗ trợ khung.
Marnen Laibow-Koser

12

Tôi đã có thể áp dụng thành công Mẫu Mô-đun Javascript cho ứng dụng Ext JS trong công việc trước đây của mình. Nó cung cấp một cách đơn giản để tạo mã được đóng gói độc đáo.


11

Dojo đã có hệ thống mô-đun từ ngày đầu tiên. Trong thực tế, nó được coi là nền tảng của Dojo, chất keo kết dính tất cả lại với nhau:

Sử dụng các mô-đun Dojo đạt được các mục tiêu sau:

  • Không gian tên cho mã Dojo và mã tùy chỉnh (dojo.declare() ) - không gây ô nhiễm không gian toàn cầu, cùng tồn tại với các thư viện khác và mã không nhận biết Dojo của người dùng.
  • Đang tải các mô-đun đồng bộ hoặc không đồng bộ theo tên (dojo.require() ).
  • Xây dựng tùy chỉnh bằng cách phân tích các phụ thuộc mô-đun để tạo một tệp hoặc một nhóm các tệp phụ thuộc lẫn nhau (được gọi là các lớp) để chỉ bao gồm những gì ứng dụng web của bạn cần. Bản dựng tùy chỉnh có thể bao gồm các mô-đun Dojo và mô-đun do khách hàng cung cấp.
  • Truy cập dựa trên CDN trong suốt vào mã Dojo và mã người dùng. Cả AOL và Google đều mang Dojo theo cách này, nhưng một số khách hàng cũng làm điều đó cho các ứng dụng web tùy chỉnh của họ.

9

Kiểm tra JavasciptMVC .

Bạn có thể :

  • chia mã của bạn thành các lớp mô hình, khung nhìn và bộ điều khiển.

  • nén tất cả mã vào một tệp sản xuất

  • mã tự động tạo

  • tạo và chạy thử nghiệm đơn vị

  • và nhiều hơn nữa...

Trên hết, nó sử dụng jQuery, vì vậy bạn cũng có thể tận dụng các plugin jQuery khác.


Vâng, tôi đã sử dụng jmvc và nó khá tốt - mặc dù tài liệu có thể tốt hơn
meouw

9

Ông chủ của tôi vẫn nói về những lần họ viết mã mô-đun (ngôn ngữ C) và phàn nàn về việc mã ngày nay tệ đến mức nào! Người ta nói rằng các lập trình viên có thể viết lắp ráp trong bất kỳ khuôn khổ nào. Luôn có một chiến lược để vượt qua tổ chức mã. Vấn đề cơ bản là với những kẻ coi kịch bản java như một món đồ chơi và không bao giờ cố gắng học nó.

Trong trường hợp của tôi, tôi viết các tệp js trên một chủ đề giao diện người dùng hoặc màn hình ứng dụng, với init_screen () thích hợp. Sử dụng quy ước đặt tên id thích hợp, tôi đảm bảo rằng không có xung đột không gian tên ở cấp độ phần tử gốc. Trong cửa sổ unobstrusive.load (), tôi kết nối mọi thứ dựa trên id cấp cao nhất.

Tôi nghiêm túc sử dụng các bao đóng và tập lệnh java để ẩn tất cả các phương thức riêng tư. Sau khi làm điều này, không bao giờ phải đối mặt với một vấn đề xung đột giữa các thuộc tính / định nghĩa hàm / định nghĩa biến. Tuy nhiên, khi làm việc với một nhóm thường khó thực thi cùng một sự nghiêm ngặt.


9

Tôi ngạc nhiên không ai đề cập đến các khung MVC. Tôi đã sử dụng Backbone.js để mô đun hóa và tách mã của tôi và nó là vô giá.

Có khá nhiều loại khung công tác ngoài kia, và hầu hết trong số chúng cũng khá nhỏ. Ý kiến ​​cá nhân của tôi là nếu bạn sẽ viết nhiều hơn chỉ một vài dòng jQuery cho các công cụ UI hào nhoáng hoặc muốn có một ứng dụng Ajax phong phú, một khung công tác MVC sẽ giúp cuộc sống của bạn dễ dàng hơn nhiều.


8

"Viết như điên và chỉ hy vọng nó hoạt động tốt nhất?", Tôi đã thấy một dự án như thế này được phát triển và duy trì bởi chỉ 2 nhà phát triển, một ứng dụng khổng lồ với rất nhiều mã javascript. Trên hết, có các phím tắt khác nhau cho mọi chức năng jquery có thể bạn có thể nghĩ đến. Tôi đề nghị họ tổ chức mã dưới dạng các plugin, vì đó là tương đương jquery của lớp, mô-đun, không gian tên ... và toàn bộ vũ trụ. Nhưng mọi thứ trở nên tồi tệ hơn, giờ họ bắt đầu viết các plugin thay thế mọi sự kết hợp của 3 dòng mã được sử dụng trong dự án. Cá nhân tôi nghĩ rằng jQuery là ma quỷ và nó không nên được sử dụng cho các dự án có nhiều javascript vì nó khuyến khích bạn lười biếng và không nghĩ đến việc tổ chức mã theo bất kỳ cách nào. Tôi thà đọc 100 dòng javascript hơn một dòng với 40 hàm jQuery được xâu chuỗi (I ' m không đùa đâu). Trái với suy nghĩ phổ biến, việc tổ chức mã javascript tương đương với không gian tên và các lớp rất dễ dàng. Đó là những gì YUI và Dojo làm. Bạn có thể dễ dàng cuộn của riêng bạn nếu bạn muốn. Tôi thấy cách tiếp cận của YUI tốt hơn và hiệu quả hơn nhiều. Nhưng bạn thường cần một trình soạn thảo đẹp với sự hỗ trợ cho đoạn trích để bù cho các quy ước đặt tên YUI nếu bạn muốn viết bất cứ điều gì hữu ích.


3
Tôi đồng ý với bạn về các lệnh thực sự dài, bị xiềng xích, nhưng một trong những phần hay nhất của jQuery là nó giữ cho tất cả Javascript không có HTML. Bạn có thể thiết lập trình xử lý sự kiện cho tất cả các thành phần của mình mà không cần "thêm" để thêm ID hoặc vào các sự kiện <anything> trên các thành phần của bạn. Như mọi khi, việc sử dụng bất kỳ công cụ nào đều xấu ...
Hugoware

Tôi đã làm việc với các dự án lớn, được tổ chức tốt trong jQuery. Tôi không biết tại sao bạn nghĩ rằng nó cản trở tổ chức.
Marnen Laibow-Koser

7

Tôi tạo ra các singletons cho mọi thứ mà tôi thực sự không cần phải khởi tạo nhiều lần trên màn hình, một lớp cho mọi thứ khác. Và tất cả chúng được đặt trong cùng một không gian tên trong cùng một tệp. Mọi thứ đều được nhận xét và được thiết kế với UML, sơ đồ trạng thái. Mã javascript không có html nên không có javascript nội tuyến và tôi có xu hướng sử dụng jquery để giảm thiểu các vấn đề về trình duyệt chéo.


3
bình luận tốt là KHÓA - Tôi rất vui vì bạn đã nói vậy nên tôi không phải làm thế. Tôi sẽ thêm các quy ước đặt tên nhất quán, một số loại chiến lược tổ chức dễ hiểu cho các biến & amp; các chức năng, và như bạn đã đề cập, sử dụng hợp lý các lớp so với singletons.
matt lohkamp

Không. Nếu bạn cần bình luận, mã của bạn thường không thể đọc được. Phấn đấu để viết mã mà không cần bình luận.
Marnen Laibow-Koser

Ngoài ra, nếu bạn cần UML và sơ đồ trạng thái, điều đó có thể có nghĩa là kiến ​​trúc của bạn không đủ rõ ràng từ mã. Hạ cấp.
Marnen Laibow-Koser

1
@Marnen Các dự án được viết tốt bao gồm các ý kiến ​​để mô tả TẠI SAO, không nhất thiết là GÌ. Mã đã mô tả CÁI GÌ, nhưng thường thì bạn cần một cái gì đó để mô tả TẠI SAO. Nâng cao.
Cypher

@Cypher Các dự án được viết tốt có mã đủ rõ ràng mà bạn thường có thể nói "tại sao", không chỉ là "cái gì". Tôi sẽ không tin một bình luận để cho tôi biết "tại sao", vì tôi không đảm bảo rằng nó đồng bộ với mã. Hãy để tài liệu mã chính nó.
Marnen Laibow-Koser

6

Trong dự án cuối cùng của tôi -Viajeros.com- Tôi đã sử dụng kết hợp nhiều kỹ thuật. Tôi sẽ không biết cách tổ chức một ứng dụng web - Viajeros là một trang web mạng xã hội dành cho khách du lịch với các phần được xác định rõ ràng, vì vậy thật dễ dàng để phân tách mã cho từng khu vực.

Tôi sử dụng mô phỏng không gian tên và lười tải các mô-đun theo phần trang web. Trên mỗi lần tải trang, tôi khai báo một đối tượng "vjr" và luôn tải một tập hợp các hàm phổ biến cho nó (vjr.base.js). Sau đó, mỗi trang HTML quyết định các mô-đun cần với một cách đơn giản:

vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];

Vjr.base.js lấy từng cái được nén từ máy chủ và thực thi chúng.

vjr.include(vjr.Required);
vjr.include = function(moduleList) {
  if (!moduleList) return false;
  for (var i = 0; i < moduleList.length; i++) {
    if (moduleList[i]) {
      $.ajax({
        type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
      });
    }
  }
};

Mỗi "mô-đun" có cấu trúc này:

vjr.comments = {}

vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }

// Handlers
vjr.comments.setUpUI = function() {
    // Assign handlers to screen elements
}

vjr.comments.init = function () {
  // initialize stuff
    vjr.comments.setUpUI();
}

$(document).ready(vjr.comments.init);

Với kiến ​​thức Javascript hạn chế của mình, tôi biết phải có cách tốt hơn để quản lý việc này, nhưng cho đến bây giờ nó vẫn hoạt động rất tốt với chúng tôi.


6

Sắp xếp mã của bạn theo cách NameSpace trung tâm của Jquery có thể trông như sau ... và sẽ không xung đột với các API Javascript khác như Prototype, Ext.

<script src="jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script type="text/javascript">

var AcmeJQ = jQuery.noConflict(true);
var Acme = {fn: function(){}};

(function($){

    Acme.sayHi = function()
    {
        console.log('Hello');
    };

    Acme.sayBye = function()
    {
        console.log('Good Bye');
    };
})(AcmeJQ);

// Usage
//          Acme.sayHi();
// or
// <a href="#" onclick="Acme.sayHi();">Say Hello</a>


</script>

Hi vọng điêu nay co ich.


Điều này gây ấn tượng với tôi như một chút văn hóa hàng hóa. jQuery.fnlà một con trỏ tới jQuery.prototype, bởi vì $()thực sự trả về một thể hiện mới của hàm tạo hàm jQuery. Thêm một 'plugin' vào jQuery có nghĩa là chỉ cần mở rộng nguyên mẫu của nó. Nhưng những gì bạn đang làm không phải là điều đó, và có những cách sạch hơn để hoàn thành điều tương tự.
Adam Lassek

Tôi tin rằng anh ta chỉ đơn giản là tạo ra các chức năng tĩnh. Tôi nhớ đã thấy nó trong các tài liệu của jQuery rằng cách khai báo các hàm tĩnh này có thể chấp nhận được
Alex Heyd

6

Hiệu trưởng tốt của OO + MVC chắc chắn sẽ đi một chặng đường dài để quản lý một ứng dụng javascript phức tạp.

Về cơ bản, tôi đang tổ chức ứng dụng và javascript của mình theo thiết kế quen thuộc sau (tồn tại suốt từ thời lập trình máy tính để bàn của tôi cho Web 2.0)

JS OO và MVC

Mô tả cho các giá trị số trên hình ảnh:

  1. Widgets đại diện cho quan điểm của ứng dụng của tôi. Điều này cần được mở rộng và tách ra một cách gọn gàng dẫn đến sự phân tách tốt mà MVC cố gắng đạt được thay vì biến widget của tôi thành mã spaghetti (tương đương trong ứng dụng web đặt một khối lớn Javascript trực tiếp vào HTML). Mỗi tiện ích giao tiếp với nhau bằng cách lắng nghe sự kiện được tạo bởi các tiện ích khác, do đó làm giảm sự khớp nối mạnh mẽ giữa các tiện ích có thể dẫn đến mã không thể quản lý được (hãy nhớ ngày thêm onclick ở mọi nơi chỉ vào một hàm toàn cầu trong thẻ script? Urgh ...)
  2. Các mô hình đối tượng đại diện cho dữ liệu mà tôi muốn đưa vào các widget và chuyển qua lại với máy chủ. Bằng cách đóng gói dữ liệu vào mô hình của nó, ứng dụng sẽ trở thành định dạng dữ liệu. Ví dụ: trong khi tự nhiên trong Javascript, các mô hình đối tượng này hầu hết được tuần tự hóa và được giải tuần tự hóa thành JSON, nếu bằng cách nào đó, máy chủ đang sử dụng XML để liên lạc, tất cả những gì tôi cần thay đổi là thay đổi lớp tuần tự hóa / giải tuần tự hóa và không nhất thiết phải thay đổi tất cả các lớp widget .
  3. Các lớp điều khiển quản lý logic nghiệp vụ và giao tiếp với máy chủ + lớp bộ đệm. Lớp này điều khiển giao thức truyền thông đến máy chủ và đưa dữ liệu cần thiết vào các mô hình đối tượng
  4. Các lớp được gói gọn gàng trong không gian tên tương ứng của họ. Tôi chắc chắn tất cả chúng ta đều biết không gian tên toàn cầu khó chịu như thế nào trong Javascript.

Trước đây, tôi sẽ tách các tệp thành js của riêng nó và sử dụng thông lệ chung để tạo các nguyên tắc OO trong Javascript. Vấn đề mà tôi sớm nhận thấy rằng có nhiều cách để viết JS OO và không nhất thiết là tất cả các thành viên trong nhóm có cùng một cách tiếp cận. Khi nhóm trở nên lớn hơn (trong trường hợp của tôi là hơn 15 người), điều này trở nên phức tạp vì không có cách tiếp cận tiêu chuẩn cho Javascript hướng đối tượng. Đồng thời tôi không muốn viết khuôn khổ của riêng mình và lặp lại một số công việc mà tôi chắc chắn rằng những người thông minh hơn tôi đã giải quyết.

jQuery rất đẹp vì Javascript Framework và tôi thích nó, tuy nhiên khi dự án lớn hơn, tôi rõ ràng cần cấu trúc bổ sung cho ứng dụng web của mình đặc biệt là để tạo điều kiện cho việc chuẩn hóa OO. Đối với bản thân mình, sau nhiều thí nghiệm, tôi thấy rằng YUI3 Base và Widget ( http://yuilibrary.com/yui/docs/widget/http://yuilibrary.com/yui/docs/base/index.html ) cơ sở hạ tầng cung cấp chính xác những gì tôi cần. Vài lý do tại sao tôi sử dụng chúng.

  1. Nó cung cấp hỗ trợ Namespace. Nhu cầu thực sự về OO và tổ chức gọn gàng mã của bạn
  2. Nó hỗ trợ khái niệm về các lớp và các đối tượng
  3. Nó cung cấp một phương tiện tiêu chuẩn hóa để thêm các biến thể hiện vào lớp của bạn
  4. Nó hỗ trợ mở rộng lớp gọn gàng
  5. Nó cung cấp hàm tạo và hàm hủy
  6. Nó cung cấp kết xuất và ràng buộc sự kiện
  7. Nó có khung widget cơ sở
  8. Hiện tại mỗi tiện ích có thể liên lạc với nhau bằng mô hình dựa trên sự kiện tiêu chuẩn
  9. Quan trọng nhất, nó cung cấp cho tất cả các kỹ sư một Tiêu chuẩn OO để phát triển Javascript

Trái với nhiều quan điểm, tôi không nhất thiết phải chọn giữa jQuery và YUI3. Hai người này có thể chung sống hòa bình. Mặc dù YUI3 cung cấp mẫu OO cần thiết cho ứng dụng web phức tạp của tôi, jQuery vẫn cung cấp cho nhóm của tôi tính dễ sử dụng Trừu tượng JS mà tất cả chúng ta đều yêu thích và quen thuộc.

Sử dụng YUI3, tôi đã quản lý để tạo mẫu MVC bằng cách tách các lớp mở rộng Cơ sở thành Mô hình, các lớp mở rộng Widget dưới dạng Xem và tất nhiên bạn có các lớp Trình điều khiển đang thực hiện các cuộc gọi logic và máy chủ cần thiết.

Widget có thể giao tiếp với nhau bằng mô hình dựa trên sự kiện và lắng nghe sự kiện và thực hiện các tác vụ cần thiết dựa trên giao diện được xác định trước. Nói một cách đơn giản, việc đưa cấu trúc OO + MVC vào JS là một niềm vui đối với tôi.

Chỉ là từ chối trách nhiệm, tôi không làm việc cho Yahoo! và chỉ đơn giản là một kiến ​​trúc sư đang cố gắng đối phó với cùng một vấn đề được đặt ra bởi câu hỏi ban đầu. Tôi nghĩ rằng nếu bất cứ ai tìm thấy khuôn khổ OO tương đương, điều này cũng sẽ hoạt động. Chủ yếu, câu hỏi này cũng áp dụng cho các công nghệ khác. Cảm ơn Chúa vì tất cả những người đã đưa ra Nguyên tắc OO + MVC để làm cho ngày lập trình của chúng ta dễ quản lý hơn.


5

Tôi sử dụng quản lý gói của Dojo ( dojo.requiredojo.provide) và hệ thống lớp ( dojo.declarecũng cho phép nhiều kế thừa đơn giản) để mô đun hóa tất cả các lớp / widget của tôi thành các tệp riêng biệt. Không chỉ liều này giữ cho mã của bạn được tổ chức, mà nó còn cho phép bạn lười biếng / chỉ trong thời gian tải các lớp / widget.


3

Vài ngày trước, những kẻ ở 37Signals đã phát hành một điều khiển RTE , với một sự thay đổi. Họ đã tạo một thư viện chứa các tệp javascript bằng cách sử dụng một loại lệnh tiền xử lý.

Tôi đã sử dụng nó kể từ khi tách các tệp JS của mình và cuối cùng hợp nhất chúng thành một. Bằng cách đó, tôi có thể tách các mối quan tâm và cuối cùng, chỉ có một tệp đi qua đường ống (được nén, không ít hơn).

Trong các mẫu của bạn, hãy kiểm tra xem bạn có ở chế độ phát triển hay không và bao gồm các tệp riêng biệt và nếu trong sản xuất, hãy bao gồm tệp cuối cùng (bạn sẽ phải tự "xây dựng").


1
gotprockets.org là liên kết trực tiếp
Matt Gardner

3

Tạo các lớp giả và đảm bảo rằng bất kỳ thứ gì có thể được ném vào một hàm riêng biệt có ý nghĩa đều được thực hiện. Cũng đảm bảo bình luận nhiều, và không viết mã spagghetti, thay vì giữ tất cả trong các phần. Ví dụ, một số mã vô nghĩa mô tả lý tưởng của tôi. Rõ ràng trong cuộc sống thực, tôi cũng viết nhiều thư viện về cơ bản bao gồm chức năng của chúng.

$(function(){
    //Preload header images
    $('a.rollover').preload();

    //Create new datagrid
    var dGrid = datagrid.init({width: 5, url: 'datalist.txt', style: 'aero'});
});

var datagrid = {
    init: function(w, url, style){
        //Rendering code goes here for style / width
        //code etc

        //Fetch data in
        $.get(url, {}, function(data){
            data = data.split('\n');
            for(var i=0; i < data.length; i++){
                //fetching data
            }
        })
    },
    refresh: function(deep){
        //more functions etc.
    }
};


2

Tôi nghĩ rằng điều này có liên quan đến, có lẽ, DDD (Thiết kế hướng tên miền). Ứng dụng tôi đang làm việc, mặc dù thiếu API chính thức, sẽ đưa ra gợi ý về cách đó bằng mã phía máy chủ (tên lớp / tên tệp, v.v.). Với điều đó, tôi đã tạo một đối tượng cấp cao nhất như một thùng chứa cho toàn bộ miền vấn đề; Sau đó, tôi đã thêm không gian tên vào nơi cần thiết:

var App;
(function()
{
    App = new Domain( 'test' );

    function Domain( id )
    {
        this.id = id;
        this.echo = function echo( s )
        {
            alert( s );
        }
        return this;
    }
})();

// separate file
(function(Domain)
{
    Domain.Console = new Console();

    function Console()
    {
        this.Log = function Log( s )
        {
            console.log( s );
        }
        return this;
    }
})(App);

// implementation
App.Console.Log('foo');

2

Đối với tổ chức JavaScript đã sử dụng như sau

  1. Thư mục cho tất cả javascript của bạn
  2. Javascript cấp trang có được tập tin riêng của nó với cùng tên của trang. ProductDetail.aspx sẽ là ProductDetail.js
  3. Trong thư mục javascript cho các tệp thư viện Tôi có một thư mục lib
  4. Đặt các chức năng thư viện liên quan trong một thư mục lib mà bạn muốn sử dụng trong suốt ứng dụng của mình.
  5. Ajax là javascript duy nhất mà tôi di chuyển ra ngoài thư mục javascript và nhận được thư mục riêng của nó. Sau đó, tôi thêm hai thư mục con máy khách và máy chủ
  6. Thư mục máy khách nhận được tất cả các tệp .js trong khi thư mục máy chủ nhận được tất cả các tệp phía máy chủ.

Đẹp cho tổ chức tập tin. Tôi làm điều đó với mã. Nhưng cuối cùng, tôi biên dịch mã của mình trong một ... hãy nói dll. Bạn cũng cần điều đó với javascript hoặc cuối cùng bạn sẽ yêu cầu 15 tệp js trên mỗi trang.
liệu

Không có gì sai khi yêu cầu 15 tệp JS trên mỗi trang. Trình duyệt của bạn sẽ lưu trữ chúng cho các yêu cầu tiếp theo.
Marnen Laibow-Koser

@ MarnenLaibow-Koser Vấn đề duy nhất với việc yêu cầu 15 tệp JS trên một trang là có bao nhiêu yêu cầu HTTP mà trình duyệt có thể xử lý cùng một lúc. Vì vậy, việc gói chúng vào một tệp cho phép trình duyệt yêu cầu các tệp cần thiết khác cùng một lúc.
iwasrobbed

Điều đó đúng, nhưng sau vài lần truy cập đầu tiên, chúng sẽ nằm trong bộ đệm của trình duyệt, vì vậy chúng sẽ không yêu cầu kết nối HTTP.
Marnen Laibow-Koser

2

Tôi đang sử dụng thứ nhỏ này. Nó cung cấp cho bạn chỉ thị 'bao gồm' cho cả hai mẫu JS và HTML. Nó làm nổi bật sự lộn xộn hoàn toàn.

https://github.com/gaperton/include.js/

$.include({
    html: "my_template.html" // include template from file...
})
.define( function( _ ){ // define module...
    _.exports = function widget( $this, a_data, a_events ){ // exporting function...
        _.html.renderTo( $this, a_data ); // which expands template inside of $this.

        $this.find( "#ok").click( a_events.on_click ); // throw event up to the caller...
        $this.find( "#refresh").click( function(){
            widget( $this, a_data, a_events ); // ...and update ourself. Yep, in that easy way.
        });
    }
});

2

Bạn có thể sử dụng jquery mx (được sử dụng trong javascriptMVC) là tập hợp các tập lệnh cho phép bạn sử dụng các mô hình, chế độ xem và bộ điều khiển. Tôi đã sử dụng nó trong một dự án và giúp tôi tạo javascript có cấu trúc, với kích thước tập lệnh tối thiểu vì nén. Đây là một ví dụ về bộ điều khiển:

$.Controller.extend('Todos',{
  ".todo mouseover" : function( el, ev ) {
   el.css("backgroundColor","red")
  },
  ".todo mouseout" : function( el, ev ) {
   el.css("backgroundColor","")
  },
  ".create click" : function() {
   this.find("ol").append("<li class='todo'>New Todo</li>"); 
  }
})

new Todos($('#todos'));

Bạn cũng chỉ có thể sử dụng phía bộ điều khiển của jquerymx nếu bạn không quan tâm đến phần xem và mô hình.


1

Câu hỏi của bạn là một câu hỏi làm tôi khó chịu vào cuối năm ngoái. Sự khác biệt - trao mã cho các nhà phát triển mới, những người chưa bao giờ nghe nói về các phương thức riêng tư và công khai. Tôi đã phải xây dựng một cái gì đó đơn giản.

Kết quả cuối cùng là một khung công tác nhỏ (khoảng 1KB) để dịch các đối tượng bằng chữ thành jQuery. Cú pháp trực quan dễ quét hơn và nếu js của bạn phát triển thực sự lớn, bạn có thể viết các truy vấn có thể sử dụng lại để tìm những thứ như bộ chọn được sử dụng, tệp được tải, hàm phụ thuộc, v.v.

Đăng một khung nhỏ ở đây là không thực tế, vì vậy tôi đã viết một bài đăng blog với các ví dụ (Đầu tiên của tôi. Đó là một cuộc phiêu lưu!). Bạn được chào đón để xem.

Đối với bất kỳ ai khác ở đây với một vài phút để kiểm tra, tôi đánh giá rất cao phản hồi!

FireFox được đề xuất vì nó hỗ trợ toSource () cho ví dụ truy vấn đối tượng.

Chúc mừng!

Ađam


0

Tôi sử dụng một tập lệnh tùy chỉnh lấy cảm hứng từ hành vi của Ben Nolan (đáng buồn là tôi không thể tìm thấy một liên kết hiện tại với điều này nữa) để lưu trữ hầu hết các trình xử lý sự kiện của tôi. Các trình xử lý sự kiện này được kích hoạt bởi các phần tử className hoặc Id, ví dụ. Thí dụ:

Behaviour.register({ 
    'a.delete-post': function(element) {
        element.observe('click', function(event) { ... });
    },

    'a.anotherlink': function(element) {
        element.observe('click', function(event) { ... });
    }

});

Tôi muốn bao gồm hầu hết các thư viện Javascript của mình một cách nhanh chóng, ngoại trừ những thư viện chứa hành vi toàn cầu. Tôi sử dụng trình trợ giúp giữ chỗ headScript () của Zend Framework cho việc này, nhưng bạn cũng có thể sử dụng javascript để tải các tập lệnh khác khi đang di chuyển với Ajile chẳng hạn.


Đây có phải là những gì bạn đang tìm kiếm? koders.com/javascript/ từ
DOK

Yup, đó là một trong những! :) Có vẻ như mã đằng sau liên kết này khá mới hơn so với phiên bản mà tôi được truyền cảm hứng. Cảm ơn nỗ lực của bạn!
Aron Rotteveel

0

Bạn không đề cập đến ngôn ngữ phía máy chủ của bạn là gì. Hoặc, thường xuyên hơn, bạn đang sử dụng khung nào - nếu có - ở phía máy chủ.

IME, tôi sắp xếp mọi thứ ở phía máy chủ và để tất cả bắt đầu trên trang web. Khung công tác được giao nhiệm vụ tổ chức không chỉ JS mà mỗi trang phải tải, mà cả các đoạn JS hoạt động với đánh dấu được tạo. Những mảnh như vậy bạn thường không muốn phát ra nhiều lần - đó là lý do tại sao chúng được trừu tượng hóa trong khuôn khổ cho mã đó để chăm sóc vấn đề đó. :-)

Đối với các trang cuối phải phát ra JS của riêng họ, tôi thường thấy rằng có một cấu trúc logic trong đánh dấu được tạo. JS được bản địa hóa như vậy thường có thể được lắp ráp ở đầu và / hoặc cuối của cấu trúc như vậy.

Lưu ý rằng không ai trong số này miễn trừ bạn viết JavaScript hiệu quả! :-)


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.