แนวทางปฏิบัติแนะนำสำหรับองค์ประกอบที่กำหนดเอง

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

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

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

เช็กลิสต์

Shadow DOM

สร้างรากเงาเพื่อสรุปรูปแบบ

เพราะเหตุใด รูปแบบการห่อหุ้มในรากเงาขององค์ประกอบจะช่วยให้มั่นใจได้ว่ารูปแบบนั้นจะทำงาน ไม่ว่าจะใช้งานอยู่ที่ใด ซึ่งสำคัญมากหากนักพัฒนาแอป ต้องการวางองค์ประกอบของคุณไว้ภายในรากเงาขององค์ประกอบอื่น ช่วงเวลานี้ ใช้ได้กับองค์ประกอบง่ายๆ เช่น ช่องทำเครื่องหมายหรือปุ่มตัวเลือก อาจจะเป็น ในกรณีนี้ เนื้อหาเดียวภายในรากเงาของคุณจะเป็นรูปแบบ ด้วยตนเอง
ตัวอย่าง <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จะไม่มีผลใดๆ บ่อยครั้ง เป็นเรื่องน่าประหลาดใจสำหรับนักพัฒนาแอปและอาจทำให้เกิดปัญหาเกี่ยวกับ การจัดวางหน้าเว็บ หากคุณไม่ต้องการจอแสดงผลแบบ inline ควรกำหนดค่า display เริ่มต้นเสมอ
ตัวอย่าง <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() หากทุกแอตทริบิวต์มี ที่สอดคล้องกันและทั้งสองแบบ จะช่วยให้คุณสามารถ ทำงานกับองค์ประกอบของคุณได้ กล่าวคือ การโทร setAttribute('foo', value) ควรตั้งค่า foo พร็อพเพอร์ตี้และในทางกลับกันด้วย แต่แน่นอนว่ามีข้อยกเว้น ด้วยกฎนี้ คุณไม่ควรแสดงพร็อพเพอร์ตี้ความถี่สูง เช่น currentTime ในวิดีโอเพลเยอร์ โปรดใช้วิจารณญาณที่ดีที่สุด หาก ดูเหมือนว่าผู้ใช้จะโต้ตอบกับพร็อพเพอร์ตี้หรือแอตทริบิวต์ และ การสะท้อนความคิดนั้นไม่ใช่เรื่องยุ่งยาก ดังนั้น การทำเช่นนั้นจึงไม่ใช่เรื่องยุ่งยาก
ตัวอย่าง <howto-checkbox> ซึ่งอธิบายเพิ่มเติมใน หลีกเลี่ยงปัญหาการใช้งานซ้ำ

พยายามยอมรับเฉพาะข้อมูลแบบสมบูรณ์ (ออบเจ็กต์ อาร์เรย์) เป็นพร็อพเพอร์ตี้

เพราะเหตุใด กล่าวโดยทั่วไปคือ ไม่มีตัวอย่างองค์ประกอบ HTML ในตัวที่ ยอมรับข้อมูลสมบูรณ์ (ออบเจ็กต์และอาร์เรย์ JavaScript ธรรมดา) ผ่าน แต่จะยอมรับข้อมูลแบบสมบูรณ์แทนผ่านการเรียกเมธอดหรือ พร็อพเพอร์ตี้ มีข้อเสียที่เห็นได้ชัดอยู่ 2-3 ข้อในการยอมรับข้อมูลอย่างละเอียด แอตทริบิวต์: การจัดชุดวัตถุขนาดใหญ่ไปยังสตริงอาจมีค่าใช้จ่ายสูง และ การอ้างอิงออบเจ็กต์จะหายไปในกระบวนการกำหนดสตริงนี้ สำหรับ ตัวอย่างเช่น หากคุณกำหนดเป็นสตริงให้กับออบเจ็กต์ที่มีการอ้างอิงไปยังออบเจ็กต์อื่น หรือโหนด 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;
    ...
  }
}