องค์ประกอบที่กำหนดเองให้คุณสร้างแท็ก HTML ของคุณเองได้ รายการตรวจสอบนี้ครอบคลุมแนวทางปฏิบัติแนะนำที่จะช่วยคุณสร้างองค์ประกอบที่มีคุณภาพสูง
องค์ประกอบที่กำหนดเองช่วยให้คุณสามารถขยาย HTML และกำหนดแท็กของคุณเอง ฟีเจอร์เหล่านี้เป็นฟีเจอร์ที่มีประสิทธิภาพอย่างเหลือเชื่อ แต่ก็อยู่ในระดับต่ำเช่นกัน ซึ่งหมายความว่าไม่มีความชัดเจนว่าจะใช้องค์ประกอบของตัวเองอย่างไรจึงจะดีที่สุดเสมอไป
เราได้จัดทำรายการตรวจสอบนี้ขึ้นมาเพื่อให้คุณสร้างประสบการณ์การใช้งานที่ดีที่สุด โดยจะแจกแจงทุกสิ่งที่เราคิดว่าจำเป็นต้องใช้ในการ
เป็นองค์ประกอบที่กำหนดเองที่มีลักษณะการทำงานที่ดี
เช็กลิสต์
Shadow DOM
สร้างเงามืดเพื่อห่อหุ้มสไตล์ |
เพราะเหตุใด |
การห่อหุ้มรูปแบบใน Shadow รูทขององค์ประกอบจะช่วยให้มั่นใจได้ว่ารูปแบบจะทำงานได้ไม่ว่าจะใช้งานที่ใด การดำเนินการนี้สำคัญอย่างยิ่งหากนักพัฒนาซอฟต์แวร์ต้องการวางองค์ประกอบของคุณไว้ในรากเงาขององค์ประกอบอื่น ซึ่งมีผลกับองค์ประกอบง่ายๆ เช่น ช่องทำเครื่องหมายหรือปุ่มตัวเลือก ในบางกรณี อาจมีเฉพาะเนื้อหาที่อยู่ในรากเงาของเงาเองเท่านั้น
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
สร้างเงามืดในตัวสร้าง
|
เพราะเหตุใด |
เครื่องมือสร้างคือเมื่อคุณมีความรู้เฉพาะตัวเกี่ยวกับองค์ประกอบ
ตอนนี้เป็นเวลาที่เหมาะแก่การตั้งค่ารายละเอียดการใช้งานซึ่งคุณไม่ต้องการให้องค์ประกอบอื่นๆ เลอะเทอะ การดำเนินการนี้ในโค้ดเรียกกลับในภายหลัง เช่น connectedCallback หมายความว่าคุณจะต้องป้องกันกรณีที่องค์ประกอบถูกถอดออกและแนบเข้ากับเอกสารอีกครั้ง
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
วางองค์ประกอบย่อยที่องค์ประกอบสร้างขึ้นลงในรากที่แสงเงา
|
เพราะเหตุใด |
องค์ประกอบย่อยที่สร้างโดยองค์ประกอบของคุณเป็นส่วนหนึ่งของการใช้งานและควรเป็นแบบส่วนตัว หากไม่มีการปกป้องรูทที่แสงเงา JavaScript ภายนอกอาจรบกวนการทำงานของเด็กเหล่านี้โดยไม่ตั้งใจ
|
ตัวอย่าง |
องค์ประกอบ
<howto-tabs>
|
ใช้ <slot> เพื่อฉายภาพ Light DOM ไปยัง Shadow DOM
|
เพราะเหตุใด |
อนุญาตให้ผู้ใช้คอมโพเนนต์ระบุเนื้อหาในคอมโพเนนต์เป็น HTML ย่อยเพื่อให้เขียนคอมโพเนนต์ได้ง่ายขึ้น เมื่อเบราว์เซอร์ไม่รองรับองค์ประกอบที่กำหนดเอง เนื้อหาที่ซ้อนอยู่จะยังคงอยู่ มองเห็น และเข้าถึงได้
|
ตัวอย่าง |
องค์ประกอบ
<howto-tabs>
|
ตั้งค่ารูปแบบการแสดงผล :host (เช่น block , inline-block , flex ) เว้นแต่ว่าคุณต้องการใช้ค่าเริ่มต้นเป็น inline
|
เพราะเหตุใด |
องค์ประกอบที่กำหนดเองจะมีค่าเป็น display: inline โดยค่าเริ่มต้น ดังนั้นการตั้งค่า width หรือ height จะไม่มีผล เรื่องนี้มักเป็นเรื่องน่าประหลาดใจสำหรับนักพัฒนาซอฟต์แวร์และอาจทำให้เกิดปัญหาเกี่ยวกับการจัดเลย์เอาต์หน้าเว็บ คุณควรกำหนดค่า display เริ่มต้นเสมอหากไม่ต้องการใช้จอแสดงผล inline
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
เพิ่มรูปแบบการแสดงผล :host ตามแอตทริบิวต์ที่ซ่อนไว้
|
เพราะเหตุใด |
องค์ประกอบที่กำหนดเองซึ่งมีรูปแบบ display เริ่มต้น เช่น :host { display: block } จะลบล้าง
แอตทริบิวต์ hidden ในตัวที่เฉพาะเจาะจงที่ต่ำกว่า
ซึ่งอาจทำให้คุณประหลาดใจหากต้องการตั้งค่าแอตทริบิวต์ hidden ในองค์ประกอบให้แสดงผล display: none นอกเหนือจากรูปแบบ display เริ่มต้นแล้ว ให้เพิ่มการรองรับ hidden ด้วย :host([hidden]) { display: none } ด้วย
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
แอตทริบิวต์และพร็อพเพอร์ตี้
อย่าลบล้างแอตทริบิวต์ส่วนกลางที่ผู้เขียนกำหนด
|
เพราะเหตุใด |
แอตทริบิวต์ร่วมคือแอตทริบิวต์ที่มีอยู่ในองค์ประกอบ HTML ทั้งหมด ตัวอย่างเช่น tabindex และ role องค์ประกอบที่กำหนดเองอาจต้องการตั้งค่า tabindex เริ่มต้นเป็น 0 เพื่อให้โฟกัสแป้นพิมพ์ได้ แต่คุณควรตรวจสอบก่อนเสมอเพื่อดูว่านักพัฒนาซอฟต์แวร์ที่ใช้องค์ประกอบของคุณได้กำหนดค่านี้เป็นค่าอื่นหรือไม่ เช่น หากตั้งค่า tabindex เป็น -1 แสดงว่าเป็นสัญญาณว่าไม่ต้องการให้องค์ประกอบมีการโต้ตอบ
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox> ซึ่งจะอธิบายเพิ่มเติมในอย่าลบล้างผู้เขียนหน้าเว็บ
|
ยอมรับข้อมูลพื้นฐาน (สตริง ตัวเลข บูลีน) เป็นแอตทริบิวต์หรือพร็อพเพอร์ตี้เสมอ
|
เพราะเหตุใด |
องค์ประกอบที่กำหนดเอง เช่น องค์ประกอบที่มีในตัว ควรกำหนดค่าได้
คุณจะส่งการกำหนดค่าแบบประกาศผ่านแอตทริบิวต์ หรือผ่านพร็อพเพอร์ตี้ JavaScript ก็ได้ โดยหลักการแล้ว ทุกแอตทริบิวต์ควรลิงก์กับพร็อพเพอร์ตี้ที่เกี่ยวข้องด้วย
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
พยายามทำให้แอตทริบิวต์และพร็อพเพอร์ตี้ของข้อมูลพื้นฐานซิงค์กัน โดยสะท้อนจากพร็อพเพอร์ตี้หนึ่งไปยังแอตทริบิวต์ และในทางกลับกัน
|
เพราะเหตุใด |
คุณไม่มีทางทราบว่าผู้ใช้จะโต้ตอบกับองค์ประกอบของคุณอย่างไร โดยอาจตั้งค่าพร็อพเพอร์ตี้ใน JavaScript จากนั้นอ่านค่าดังกล่าวโดยใช้ API เช่น getAttribute() หากทุกแอตทริบิวต์มีพร็อพเพอร์ตี้ที่สัมพันธ์กันและแสดงทั้ง 2 แอตทริบิวต์ จะทำให้ผู้ใช้ทำงานร่วมกับองค์ประกอบได้ง่ายขึ้น กล่าวคือ การเรียกใช้ setAttribute('foo', value) ควรตั้งค่าพร็อพเพอร์ตี้ foo ที่เกี่ยวข้องด้วย และในทางกลับกันด้วย แน่นอนว่ามีข้อยกเว้นสำหรับกฎนี้ คุณไม่ควรแสดงพร็อพเพอร์ตี้ความถี่สูง เช่น currentTime ในโปรแกรมเล่นวิดีโอ โปรดใช้วิจารณญาณที่ดีที่สุด หากดูเหมือนว่าผู้ใช้จะโต้ตอบกับพร็อพเพอร์ตี้หรือแอตทริบิวต์ และตัวแปรก็ไม่ยุ่งยากในการแสดงให้เห็นถึงการโต้ตอบดังกล่าว
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox> ซึ่งดูเพิ่มเติมได้ในหัวข้อหลีกเลี่ยงปัญหาการกลับมาใช้ซ้ำ
|
มุ่งเน้นการรับเฉพาะข้อมูลแบบสมบูรณ์ (วัตถุ อาร์เรย์) เป็นคุณสมบัติ
|
เพราะเหตุใด |
กล่าวโดยทั่วไปคือไม่มีตัวอย่างขององค์ประกอบ HTML ในตัวที่ยอมรับ Rich Data (ออบเจ็กต์และอาร์เรย์ JavaScript แบบธรรมดา) ผ่านแอตทริบิวต์ ระบบจะยอมรับข้อมูลแบบสมบูรณ์ผ่านการเรียกใช้เมธอดหรือพร็อพเพอร์ตี้แทน มีข้อเสียที่ชัดเจน 2 ประการในการยอมรับข้อมูลแบบสมบูรณ์เป็นแอตทริบิวต์ กล่าวคือ การทำให้ออบเจ็กต์ขนาดใหญ่เป็นต่อเนื่องเป็นสตริงอาจมีค่าใช้จ่ายสูง และการอ้างอิงออบเจ็กต์จะสูญหายไปในกระบวนการสตริงนี้ ตัวอย่างเช่น หากคุณสตริงออบเจ็กต์ที่มีการอ้างอิงไปยังออบเจ็กต์อื่นหรือโหนด DOM การอ้างอิงเหล่านั้นจะหายไป
|
ไม่แสดงพร็อพเพอร์ตี้ข้อมูลแบบสมบูรณ์ในแอตทริบิวต์
|
เพราะเหตุใด |
การแสดงพร็อพเพอร์ตี้ข้อมูลสื่อสมบูรณ์กับแอตทริบิวต์มีค่าใช้จ่ายสูงโดยไม่จำเป็น ซึ่งทำให้ออบเจ็กต์ JavaScript เดียวกันเป็นอนุกรมและดีซีเรียลไลซ์ หากไม่มีกรณีการใช้งานที่สามารถแก้ไขได้ด้วยฟีเจอร์นี้เท่านั้น เราขอแนะนำให้หลีกเลี่ยง
|
ลองตรวจสอบพร็อพเพอร์ตี้ที่อาจตั้งค่าไว้ก่อนการอัปเกรดองค์ประกอบ
|
เพราะเหตุใด |
นักพัฒนาซอฟต์แวร์ที่ใช้องค์ประกอบของคุณอาจพยายามตั้งค่าพร็อพเพอร์ตี้ในองค์ประกอบก่อนที่จะโหลดคำจำกัดความ โดยเฉพาะอย่างยิ่งหากนักพัฒนาซอฟต์แวร์ใช้เฟรมเวิร์กที่จัดการคอมโพเนนต์การโหลด ประทับกับหน้าเว็บ และเชื่อมโยงพร็อพเพอร์ตี้กับโมเดล
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox> อธิบายเพิ่มเติมในหัวข้อทำให้พร็อพเพอร์ตี้เป็นแบบ Lazy Loading
|
โปรดอย่าสมัครชั้นเรียนด้วยตนเอง
|
เพราะเหตุใด |
องค์ประกอบที่ต้องแสดงสถานะไว้ด้วยแอตทริบิวต์ โดยทั่วไปแอตทริบิวต์ class จะถือว่าเป็นของนักพัฒนาซอฟต์แวร์ที่ใช้องค์ประกอบของคุณ และการเขียนแอตทริบิวต์ไปยังแอตทริบิวต์ดังกล่าวด้วยตนเองอาจเหยียบชั้นเรียนนักพัฒนาซอฟต์แวร์โดยไม่ได้ตั้งใจ
|
กิจกรรม
ส่งเหตุการณ์เพื่อตอบสนองต่อกิจกรรมของคอมโพเนนต์ภายใน
|
เพราะเหตุใด |
คอมโพเนนต์อาจมีพร็อพเพอร์ตี้ที่เปลี่ยนแปลงไปตามกิจกรรมที่คอมโพเนนต์ของคุณเท่านั้นที่ทราบ เช่น เมื่อตัวจับเวลาหรือภาพเคลื่อนไหวเล่นจนจบ หรือโหลดทรัพยากรเสร็จ การส่งเหตุการณ์เพื่อตอบสนองต่อการเปลี่ยนแปลงเหล่านี้จะช่วยแจ้งให้โฮสต์ทราบว่าสถานะของคอมโพเนนต์นั้นแตกต่างออกไป
|
อย่าส่งเหตุการณ์เพื่อตอบสนองต่อการตั้งค่าพร็อพเพอร์ตี้ของโฮสต์ (โฟลว์ข้อมูลขาลง)
|
เพราะเหตุใด |
การส่งเหตุการณ์เพื่อตอบสนองต่อการตั้งค่าพร็อพเพอร์ตี้ของโฮสต์นั้นไม่จำเป็น
(โฮสต์จะทราบสถานะปัจจุบันเพราะเพิ่งตั้งค่า) การส่งเหตุการณ์เพื่อตอบสนองต่อการตั้งค่าพร็อพเพอร์ตี้ของโฮสต์อาจทำให้เกิดการวนซ้ำแบบไม่สิ้นสุดกับระบบการเชื่อมโยงข้อมูล
|
ตัวอย่าง |
องค์ประกอบ
<howto-checkbox>
|
วิดีโออธิบาย
อย่าลบล้างผู้เขียนหน้าเว็บ
เป็นไปได้ว่านักพัฒนาซอฟต์แวร์ที่ใช้องค์ประกอบของคุณอาจต้องการลบล้างสถานะเริ่มต้นบางสถานะ ตัวอย่างเช่น การเปลี่ยน ARIA role
หรือความสามารถในการโฟกัสด้วย tabindex
ตรวจสอบว่ามีการตั้งค่าเหล่านี้และแอตทริบิวต์ร่วมอื่นๆ หรือไม่ ก่อนที่จะใช้ค่าของคุณเอง
connectedCallback() {
if (!this.hasAttribute('role'))
this.setAttribute('role', 'checkbox');
if (!this.hasAttribute('tabindex'))
this.setAttribute('tabindex', 0);
ทำให้ที่พักเป็นแบบ Lazy Loading
นักพัฒนาซอฟต์แวร์อาจพยายามตั้งค่าพร็อพเพอร์ตี้ในองค์ประกอบของคุณก่อนที่จะมีการโหลดคำจำกัดความ โดยเฉพาะอย่างยิ่งหากนักพัฒนาซอฟต์แวร์ใช้เฟรมเวิร์กที่จัดการคอมโพเนนต์การโหลด การแทรกคอมโพเนนต์ลงในหน้าเว็บ และเชื่อมโยงพร็อพเพอร์ตี้กับโมเดล
ในตัวอย่างต่อไปนี้ Angular จะเชื่อมโยงพร็อพเพอร์ตี้ isChecked
ของโมเดลกับพร็อพเพอร์ตี้ checked
ของช่องทำเครื่องหมายอย่างชัดเจน หากการกำหนดช่องทำเครื่องหมายวิธีการโหลดแบบ Lazy Loading ก็เป็นไปได้ว่า Angular อาจพยายามตั้งค่าคุณสมบัติที่ตรวจสอบแล้วก่อนที่องค์ประกอบจะอัปเกรด
<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>
องค์ประกอบที่กำหนดเองควรจัดการกับสถานการณ์นี้ด้วยการตรวจสอบว่ามีการตั้งค่าพร็อพเพอร์ตี้ในอินสแตนซ์ของตนแล้วหรือยัง <howto-checkbox>
จะแสดงรูปแบบนี้โดยใช้เมธอดชื่อ _upgradeProperty()
connectedCallback() {
...
this._upgradeProperty('checked');
}
_upgradeProperty(prop) {
if (this.hasOwnProperty(prop)) {
let value = this[prop];
delete this[prop];
this[prop] = value;
}
}
_upgradeProperty()
จะบันทึกค่าจากอินสแตนซ์ที่ไม่ได้อัปเกรดและลบพร็อพเพอร์ตี้เพื่อไม่ให้ดูตัวตั้งค่าพร็อพเพอร์ตี้ขององค์ประกอบที่กำหนดเอง
ด้วยวิธีนี้ เมื่อคําจํากัดความขององค์ประกอบโหลดขึ้นมาในท้ายที่สุด จะแสดงสถานะที่ถูกต้องได้ทันที
หลีกเลี่ยงปัญหาการกลับมาใช้งานซ้ำ
คุณอยากใช้ attributeChangedCallback()
เพื่อแสดงถึงสถานะในพร็อพเพอร์ตี้ที่แสดงอยู่ เช่น
// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'checked')
this.checked = newValue;
}
แต่การดำเนินการนี้อาจสร้างการวนซ้ำที่ไม่สิ้นสุดหากตัวตั้งค่าพร็อพเพอร์ตี้แสดงถึงแอตทริบิวต์ด้วย
set checked(value) {
const isChecked = Boolean(value);
if (isChecked)
// OOPS! This will cause an infinite loop because it triggers the
// attributeChangedCallback() which then sets this property again.
this.setAttribute('checked', '');
else
this.removeAttribute('checked');
}
อีกวิธีหนึ่งคือการอนุญาตให้ตัวตั้งค่าพร็อพเพอร์ตี้แสดงถึงแอตทริบิวต์ และให้ Getter กำหนดค่าตามแอตทริบิวต์
set checked(value) {
const isChecked = Boolean(value);
if (isChecked)
this.setAttribute('checked', '');
else
this.removeAttribute('checked');
}
get checked() {
return this.hasAttribute('checked');
}
ในตัวอย่างนี้ การเพิ่มหรือนำแอตทริบิวต์ออกจะเป็นการตั้งค่าพร็อพเพอร์ตี้ด้วย
และสุดท้าย attributeChangedCallback()
สามารถใช้เพื่อจัดการกับผลข้างเคียง
เช่น การใช้สถานะ ARIA
attributeChangedCallback(name, oldValue, newValue) {
const hasValue = newValue !== null;
switch (name) {
case 'checked':
// Note the attributeChangedCallback is only handling the *side effects*
// of setting the attribute.
this.setAttribute('aria-checked', hasValue);
break;
...
}
}