ค้นหาเว็บไซต์

jquery - แนวปฏิบัติที่ยอมรับโดยทั่วไปเกี่ยวกับการจัดระเบียบโค้ดใน JavaScript


เมื่อเฟรมเวิร์กของ JavaScript อย่าง jQuery ทำให้แอปพลิเคชันเว็บฝั่งไคลเอนต์มีความสมบูรณ์และมีฟังก์ชันมากขึ้น ฉันเริ่มสังเกตเห็นปัญหาอย่างหนึ่ง…

คุณทำให้สิ่งนี้เป็นระเบียบได้อย่างไรกัน?

  • รวบรวมตัวจัดการทั้งหมดของคุณไว้ในที่เดียวและเขียนฟังก์ชันสำหรับเหตุการณ์ทั้งหมดหรือไม่?
  • สร้างฟังก์ชัน/คลาสเพื่อห่อหุ้มฟังก์ชันการทำงานทั้งหมดของคุณหรือไม่?
  • เขียนอย่างบ้าคลั่งและแค่หวังว่ามันจะออกมาดีที่สุด?
  • ยอมแพ้และหางานใหม่?

ผมพูดถึง jQuery แต่จริงๆ แล้วมันคือโค้ด JavaScript โดยทั่วไป ผมพบว่าเมื่อบรรทัดต่อบรรทัดเริ่มสะสมขึ้น มันจะยากขึ้นที่จะจัดการไฟล์สคริปต์หรือหาสิ่งที่คุณกำลังมองหา ปัญหาใหญ่ที่สุดที่ผมพบคือมีหลายวิธีมากในการทำสิ่งเดียวกัน ทำให้ยากที่จะรู้ว่าแบบใดเป็นแนวทางปฏิบัติที่ยอมรับกันโดยทั่วไปในปัจจุบัน

มีคำแนะนำทั่วไปเกี่ยวกับวิธีที่ดีที่สุดในการเก็บไฟล์ .js ของคุณให้เรียบร้อยเหมือนส่วนอื่น ๆ ของแอปพลิเคชันของคุณหรือไม่? หรือว่านี่เป็นเรื่องของ IDE เพียงเท่านั้น? มีตัวเลือกที่ดีกว่านี้ไหม?

แก้ไข

คำถามนี้ตั้งใจที่จะเกี่ยวกับการจัดระเบียบโค้ดมากกว่าการจัดระเบียบไฟล์ มีตัวอย่างที่ดีจริงๆ บางตัวอย่างเกี่ยวกับการรวมไฟล์หรือแยกเนื้อหาออกมา

คำถามของฉันคือ: วิธีปฏิบัติที่ยอมรับโดยทั่วไปในปัจจุบันในการจัดระเบียบโค้ดของคุณคืออะไร? วิธีของคุณคืออะไร หรือแม้กระทั่งวิธีที่แนะนำในการโต้ตอบกับองค์ประกอบหน้าและสร้างโค้ดที่สามารถนำกลับมาใช้ซ้ำได้โดยไม่ขัดแย้งกัน?

บางคนได้ระบุ namespaces ซึ่งเป็นความคิดที่ดี มีวิธีอื่น ๆ อะไรอีกบ้าง โดยเฉพาะเกี่ยวกับการจัดการองค์ประกอบบนหน้าเว็บและการทำให้โค้ดเป็นระเบียบเรียบร้อย?

คำตอบ #1

มันคงจะดีมากถ้า JavaScript มี namespace ติดมาด้วย แต่ฉันพบว่าการจัดระเบียบสิ่งต่าง ๆ เหมือนที่ Dustin Diaz อธิบายไว้ที่นี่ช่วยฉันได้มาก

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

ฉันวาง "เนมสเปซ" ต่าง ๆ และบางครั้งคลาสแต่ละคลาสในไฟล์แยกกัน ปกติฉันเริ่มด้วยไฟล์เดียว และเมื่อคลาสหรือเนมสเปซมีขนาดใหญ่พอที่จะควรแยกออก ฉันก็จะแยกมันออกมาเป็นไฟล์ของตัวเอง การใช้เครื่องมือในการรวมไฟล์ทั้งหมดของคุณสำหรับการใช้งานจริงก็เป็นไอเดียที่ดีเช่นกัน

คำตอบ #2

ฉันพยายามหลีกเลี่ยงการใส่ JavaScript ใด ๆ กับ HTML โค้ดทั้งหมดถูกห่อหุ้มอยู่ในคลาส และแต่ละคลาสอยู่ในไฟล์ของตัวเอง สำหรับการพัฒนา ฉันมีแท็ก <script> แยกต่างหากเพื่อรวมแต่ละไฟล์ js แต่ไฟล์เหล่านี้จะถูกรวมเป็นแพ็กเกจใหญ่เดียวสำหรับการใช้งานจริงเพื่อลดภาระของคำขอ HTTP

โดยปกติแล้ว ฉันจะมีไฟล์ js 'หลัก' เพียงไฟล์เดียวสำหรับแต่ละแอปพลิเคชัน ดังนั้น ถ้าฉันกำลังเขียนแอปพลิเคชัน 'สำรวจ' ฉันจะมีไฟล์ js ที่ชื่อว่า 'survey.js' ซึ่งไฟล์นี้จะมีจุดเริ่มต้นในการใช้โค้ด jQuery ฉันสร้างการอ้างอิง jQuery ในระหว่างการสร้างอินสแตนซ์ แล้วจึงส่งพวกมันเข้าไปยังอ็อบเจ็กต์ของฉันเป็นพารามิเตอร์ ซึ่งหมายความว่าคลาส JavaScript จะเป็น 'บริสุทธิ์' และจะไม่มีการอ้างอิงใดๆ ไปยัง id หรือชื่อคลาสของ 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();
});

ฉันยังพบว่าการตั้งชื่อนั้นสำคัญต่อการอ่านง่าย ตัวอย่างเช่น: ฉันเติม 'j' ไว้ข้างหน้าตัวแปร jQuery ทุกตัว

ในตัวอย่างข้างต้น มีคลาสที่ชื่อว่า DimScreen (สมมติว่านี่สามารถลดความสว่างหน้าจอและแสดงกล่องแจ้งเตือนได้) มันต้องการองค์ประกอบ div ที่สามารถขยายเพื่อครอบคลุมหน้าจอ จากนั้นเพิ่มกล่องแจ้งเตือน ดังนั้นฉันจึงส่งผ่านอ็อบเจ็กต์ jQuery เข้าไป jQuery มีแนวคิดปลั๊กอิน แต่ดูเหมือนว่าจะจำกัด (เช่น อินสแตนซ์ไม่คงอยู่และไม่สามารถเข้าถึงได้) โดยไม่มีข้อดีที่แท้จริง ดังนั้นคลาส DimScreen จะเป็นคลาส javascript มาตรฐานที่บังเอิญใช้ 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>');
  //...
};

ฉันได้สร้างแอปพลิเคชันที่ค่อนข้างซับซ้อนโดยใช้แนวทางนี้

คำตอบ #3

คุณสามารถแบ่งสคริปต์ของคุณออกเป็นไฟล์แยกสำหรับการพัฒนา จากนั้นสร้างเวอร์ชัน 'ปล่อย' ที่คุณรวมทั้งหมดเข้าด้วยกันและรัน YUI Compressor หรือสิ่งที่คล้ายกันกับมันได้

คำตอบ #4

ได้รับแรงบันดาลใจจากโพสต์ก่อนหน้านี้ของฉัน ฉันก็ทำสำเนาของไดเรกทอรี Rakefile และ vendor ที่แจกจ่ายกับ WysiHat (RTE ที่กล่าวถึงใน changelog) และทำการปรับเปลี่ยนเล็กน้อยเพื่อรวมการตรวจสอบโค้ดด้วย JSLint และการย่อโค้ดด้วย YUI Compressor.

แนวคิดคือการใช้ Sprockets (จาก WysiHat) เพื่อรวมหลายไฟล์ JavaScript ให้เป็นไฟล์เดียว ตรวจสอบไวยากรณ์ของไฟล์ที่รวมแล้วด้วย JSLint และบีบอัดมันด้วย YUI Compressor ก่อนการแจกจ่าย

คุณสมบัติเบื้องต้น

  • รันไทม์ของจาวา
  • รูบี้และเก็มเรก
  • คุณควรรู้วิธีใส่ JAR ลงใน Classpath

ตอนนี้ทำ

  1. ดาวน์โหลด Rhino แล้ววางไฟล์ JAR ("js.jar") ลงใน classpath ของคุณ
  2. ดาวน์โหลด YUI Compressor และวาง JAR (build/yuicompressor-xyz.jar) ลงใน classpath ของคุณ
  3. ดาวน์โหลด WysiHat และคัดลอกโฟลเดอร์ "vendor" ไปยังรากของโปรเจกต์ JavaScript ของคุณ
  4. ดาวน์โหลด JSLint สำหรับ Rhino และวางมันไว้ในโฟลเดอร์ "vendor"

ตอนนี้สร้างไฟล์ชื่อ "Rakefile" ในไดเรกทอรีรากของโปรเจกต์ JavaScript และเพิ่มเนื้อหาต่อไปนี้ลงไป:

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

ถ้าคุณทำทุกอย่างถูกต้อง คุณควรจะสามารถใช้คำสั่งต่อไปนี้ในคอนโซลของคุณได้:

  • rake merge -- เพื่อรวมไฟล์ JavaScript หลายไฟล์เข้าด้วยกัน
  • rake check -- เพื่อตรวจสอบไวยากรณ์ของโค้ดของคุณ (นี่คืองาน เริ่มต้น ดังนั้นคุณสามารถพิมพ์เพียง rake)
  • rake minify -- เพื่อเตรียมเวอร์ชันย่อของโค้ด JS ของคุณ

เกี่ยวกับการรวมแหล่งที่มา

การใช้ Sprockets ซึ่งเป็นโปรเซสเซอร์ล่วงหน้าของ JavaScript คุณสามารถรวม (หรือ require) ไฟล์ JavaScript อื่น ๆ ใช้ไวยากรณ์ดังต่อไปนี้เพื่อรวมสคริปต์อื่นจากไฟล์เริ่มต้น (ชื่อว่า "main.js" แต่คุณสามารถเปลี่ยนชื่อนั้นใน 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
})();

และแล้ว...

ลองดู Rakefile ที่มาพร้อมกับ WysiHat เพื่อจัดการการทดสอบหน่วยอัตโนมัติ สิ่งที่ดี :)

และตอนนี้สำหรับคำตอบ

นี่ไม่ได้ตอบคำถามเดิมได้ดีนัก ผม/ดิฉันรู้และขอโทษเกี่ยวกับเรื่องนั้น แต่ผม/ดิฉันโพสต์มันที่นี่เพราะหวังว่ามันอาจเป็นประโยชน์กับคนอื่นในการจัดระเบียบความยุ่งเหยิงของพวกเขา

วิธีการของฉันต่อปัญหาคือทำการสร้างแบบจำลองเชิงวัตถุให้มากที่สุดเท่าที่จะทำได้และแยกการใช้งานออกเป็นไฟล์ต่าง ๆ จากนั้นตัวจัดการควรจะสั้นที่สุดเท่าที่จะเป็นไปได้ ตัวอย่างกับ List singleton ก็ถือว่าน่าประทับใจเช่นกัน

และ namespace... ก็สามารถเลียนแบบได้ด้วยโครงสร้างวัตถุที่ลึกกว่า

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

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

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

ฉันไม่ใช่แฟนพันธุ์แท้ของของเลียนแบบ แต่สิ่งนี้สามารถช่วยได้หากคุณมีวัตถุหลายชิ้นที่คุณต้องการย้ายออกจากสโคปทั่วโลก

คำตอบ #5

การจัดระเบียบโค้ดต้องมีการนำข้อปฏิบัติและมาตรฐานเอกสารมาใช้:
1. ใช้โค้ด Namespace สำหรับไฟล์ทางกายภาพ;

Exc = {};

2. จัดกลุ่มคลาสใน namespace เหล่านี้ javascript;
3. ตั้งค่า Prototypes หรือฟังก์ชันหรือคลาสที่เกี่ยวข้องเพื่อแทนวัตถุในโลกจริง;

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

4. กำหนดข้อปฏิบัติเพื่อปรับปรุงโค้ด ตัวอย่างเช่น รวมฟังก์ชันภายในหรือเมธอดทั้งหมดของมันไว้ในแอตทริบิวต์คลาสของประเภทอ็อบเจ็กต์

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

5. จัดทำเอกสารของ namespaces, classes, methods และ variables เมื่อจำเป็น ให้พูดคุยเกี่ยวกับโค้ดบางส่วนด้วย (บาง FIs และ Fors พวกมันมักจะใช้ตรรกะสำคัญของโค้ด)

/**
  * 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;
    ...
};

นี่เป็นเพียงเคล็ดลับบางอย่าง แต่สิ่งนี้ได้ช่วยในการจัดระเบียบโค้ดอย่างมาก จำไว้ว่าคุณต้องมีวินัยเพื่อที่จะประสบความสำเร็จ!

คำตอบ #6

การปฏิบัติตามหลักการออกแบบเชิงวัตถุ (OO) ที่ดีและรูปแบบการออกแบบต่างๆ ช่วยให้โค้ดของคุณง่ายต่อการบำรุงรักษาและเข้าใจ แต่หนึ่งในสิ่งที่ดีที่สุดที่ฉันค้นพบเมื่อเร็วๆ นี้คือ signals และ slots หรือที่เรียกว่า publish/subscribe ลองดูที่ http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html สำหรับการใช้งาน jQuery แบบง่าย ๆ

แนวคิดนี้ถูกนำไปใช้ดีในภาษาอื่น ๆ สำหรับการพัฒนา GUI เมื่อมีเหตุการณ์สำคัญเกิดขึ้นที่ไหนสักแห่งในโค้ดของคุณ คุณจะเผยแพร่เหตุการณ์สังเคราะห์ระดับโลกซึ่งวิธีการอื่น ๆ ในวัตถุอื่นอาจสมัครรับ ซึ่งสิ่งนี้ทำให้เกิดการแยกวัตถุได้อย่างยอดเยี่ยม

ฉันคิดว่า Dojo (และ Prototype?) มีเวอร์ชันในตัวของเทคนิคนี้

ดูเพิ่มเติม สัญญาณและสล็อตคืออะไร?

คำตอบ #7

ฉันสามารถใช้รูปแบบโมดูลของ Javascript กับแอปพลิเคชัน Ext JS ในงานก่อนหน้าของฉันได้สำเร็จ ซึ่งมันให้วิธีที่ง่ายในการสร้างโค้ดที่แยกออกมาอย่างสวยงาม

คำตอบ #8

Dojo มีระบบโมดูลตั้งแต่วันแรก แท้จริงแล้วมันถือเป็นมุมหลักของ Dojo คือกาวที่เชื่อมทุกสิ่งเข้าด้วยกัน:

  • dojo.require — เอกสารอย่างเป็นทางการ
  • การทำความเข้าใจ dojo.declare, dojo.require, และ dojo.provide
  • แนะนำโดโจ

การใช้โมดูล Dojo บรรลุวัตถุประสงค์ดังต่อไปนี้:

  • Namespaces สำหรับโค้ด Dojo และโค้ดที่กำหนดเอง (dojo.declare()) — ไม่ทำให้พื้นที่สากลสกปรก อยู่ร่วมกับไลบรารีอื่น ๆ และโค้ดของผู้ใช้ที่ไม่รู้จัก Dojo
  • การโหลดโมดูลแบบซิงโครนัสหรือแอสิงโครนัสตามชื่อ (dojo.require())
  • การสร้างแบบกำหนดเองโดยการวิเคราะห์การพึ่งพาของโมดูลเพื่อสร้างไฟล์เดียวหรือกลุ่มของไฟล์ที่พึ่งพาซึ่งกันและกัน (เรียกว่าชั้น) เพื่อรวมเฉพาะสิ่งที่แอปพลิเคชันเว็บของคุณต้องการ การสร้างแบบกำหนดเองสามารถรวมโมดูล Dojo และโมดูลที่ลูกค้าให้มาได้เช่นกัน
  • การเข้าถึง Dojo และโค้ดของผู้ใช้ผ่าน CDN แบบโปร่งใส ทั้ง AOL และ Google มี Dojo ในลักษณะนี้ แต่ลูกค้าบางรายก็ทำแบบนั้นสำหรับแอปพลิเคชันเว็บที่กำหนดเองของพวกเขาด้วย

บทความที่เกี่ยวข้อง