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
ตอนนี้ทำ
- ดาวน์โหลด Rhino แล้ววางไฟล์ JAR ("js.jar") ลงใน classpath ของคุณ
- ดาวน์โหลด YUI Compressor และวาง JAR (build/yuicompressor-xyz.jar) ลงใน classpath ของคุณ
- ดาวน์โหลด WysiHat และคัดลอกโฟลเดอร์ "vendor" ไปยังรากของโปรเจกต์ JavaScript ของคุณ
- ดาวน์โหลด 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 ในลักษณะนี้ แต่ลูกค้าบางรายก็ทำแบบนั้นสำหรับแอปพลิเคชันเว็บที่กำหนดเองของพวกเขาด้วย
