กรณีศึกษา: การแก้ไขข้อบกพร่องของ Angular ที่ดียิ่งขึ้นด้วยเครื่องมือสำหรับนักพัฒนาเว็บ

ประสบการณ์การแก้ไขข้อบกพร่องที่ดีขึ้น

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

โพสต์นี้จะกล่าวถึงการเปลี่ยนแปลงใน Angular และเครื่องมือสำหรับนักพัฒนาเว็บของ Chrome ที่จำเป็นเพื่อให้บรรลุเป้าหมายนี้ แม้ว่าการเปลี่ยนแปลงบางอย่างจะแสดงผ่าน Angular แต่ก็สามารถนําไปใช้กับเฟรมเวิร์กอื่นๆ ได้ด้วย ทีมเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของ Chrome สนับสนุนให้เฟรมเวิร์กอื่นๆ ใช้ Console API และจุดขยายสําเนาที่มาแบบใหม่ เพื่อให้ผู้ใช้ได้รับประสบการณ์การแก้ไขข้อบกพร่องที่ดียิ่งขึ้นด้วย

รหัสการละเว้นข้อมูล

เมื่อแก้ไขข้อบกพร่องแอปพลิเคชันโดยใช้เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome นักเขียนมักจะต้องการเห็นเฉพาะโค้ดของตนเอง ไม่ใช่โค้ดของเฟรมเวิร์กที่อยู่เบื้องหลังหรือไลบรารีบางอย่างที่ซ่อนอยู่ในโฟลเดอร์ node_modules

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

ในทางปฏิบัติ เครื่องมือสำหรับนักพัฒนาเว็บของ Chrome สามารถซ่อนโค้ดที่ระบุไว้ดังกล่าวโดยอัตโนมัติในสแต็กเทรซ ต้นไม้แหล่งที่มา กล่องโต้ตอบเปิดด่วน และปรับปรุงลักษณะการแยกขั้นตอนและการดําเนินการต่อในโปรแกรมแก้ไขข้อบกพร่อง

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

ส่วนขยายการแมปแหล่งที่มา x_google_ignoreList

ในการแมปแหล่งที่มา ช่อง x_google_ignoreList ใหม่จะอ้างอิงอาร์เรย์ sources และแสดงดัชนีของแหล่งที่มาของบุคคลที่สามทั้งหมดที่รู้จักในการแมปแหล่งที่มานั้น เมื่อแยกวิเคราะห์แผนที่ซอร์สโค้ด เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จะใช้ข้อมูลนี้เพื่อพิจารณาว่าควรเพิ่มส่วนใดของโค้ดลงในรายการที่ควรละเว้น

ด้านล่างนี้คือสําเนาที่มาของไฟล์ out.js ที่สร้างขึ้น sources เดิม 2 รายการที่มีส่วนร่วมในการสร้างไฟล์เอาต์พุต ได้แก่ foo.js และ lib.js รายการแรกคือสิ่งที่นักพัฒนาเว็บไซต์เขียนขึ้น ส่วนรายการที่ 2 คือเฟรมเวิร์กที่ใช้

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "lib.js"],
  "sourcesContent": ["...", "..."],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE;"
}

sourcesContent จะรวมอยู่ในทั้ง 2 แหล่งที่มาเดิมนี้ และ Chrome DevTools จะแสดงไฟล์เหล่านี้โดยค่าเริ่มต้นในโปรแกรมแก้ไขข้อบกพร่อง

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

ขณะนี้มีข้อมูลเพิ่มเติมอีก 1 รายการที่รวมไว้ในแผนที่แหล่งที่มาเพื่อระบุว่าแหล่งที่มาใดเป็นโค้ดของบุคคลที่สามหรือโค้ดแรก

{
  ...
  "sources": ["foo.js", "lib.js"],
  "x_google_ignoreList": [1],
  ...
}

ฟิลด์ x_google_ignoreList ใหม่มีดัชนีเดียวที่อ้างอิงอาร์เรย์ sources: 1 ซึ่งระบุว่าภูมิภาคที่แมปกับ lib.js คือโค้ดของบุคคลที่สามที่ควรเพิ่มลงในรายการละเว้นโดยอัตโนมัติ

ในตัวอย่างที่ซับซ้อนมากขึ้นซึ่งแสดงด้านล่าง ดัชนี 2, 4 และ 5 จะระบุว่าภูมิภาคที่แมปกับ lib1.ts, lib2.coffee และ hmr.js เป็นโค้ดของบุคคลที่สามทั้งหมดที่ควรเพิ่มลงในรายการละเว้นโดยอัตโนมัติ

{
  ...
  "sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
  "x_google_ignoreList": [2, 4, 5],
  ...
}

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

x_google_ignoreList ใน Angular

ตั้งแต่ Angular v14.1.0 เนื้อหาในโฟลเดอร์ node_modules และ webpack ได้รับการทําเครื่องหมายเป็น"to ignore"

ซึ่งทำได้ผ่านการเปลี่ยนแปลงใน angular-cli โดยสร้างปลั๊กอินที่เชื่อมต่อกับCompilerโมดูลของ webpack

ปลั๊กอิน webpack ที่วิศวกรของเราสร้างจะฮุกเข้ากับระยะ PROCESS_ASSETS_STAGE_DEV_TOOLING และป้อนข้อมูลในช่อง x_google_ignoreList ในแผนที่ซอร์สโค้ดสําหรับชิ้นงานขั้นสุดท้ายที่ webpack สร้างขึ้นและเบราว์เซอร์โหลด

const map = JSON.parse(mapContent) as SourceMap;
const ignoreList = [];

for (const [index, path] of map.sources.entries()) {
  if (path.includes('/node_modules/') || path.startsWith('webpack/')) {
    ignoreList.push(index);
  }
}

map[`x_google_ignoreList`] = ignoreList;
compilation.updateAsset(name, new RawSource(JSON.stringify(map)));

สแต็กเทรซที่ลิงก์

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

V8 มีกลไกในการติดตามงานแบบไม่สอดคล้องกันดังกล่าวภายในเมื่อใช้พรอมิเตอการตั้งเวลาเบราว์เซอร์มาตรฐาน เช่น setTimeout ซึ่งระบบจะดำเนินการโดยค่าเริ่มต้นในกรณีดังกล่าว นักพัฒนาแอปจึงตรวจสอบรายการเหล่านี้ได้แล้ว แต่สำหรับโปรเจ็กต์ที่ซับซ้อนมากขึ้น การดำเนินการนี้อาจไม่ง่ายอย่างที่คิด โดยเฉพาะเมื่อใช้เฟรมเวิร์กที่มีกลไกการกําหนดเวลาขั้นสูงมากขึ้น เช่น เฟรมเวิร์กที่ใช้การติดตามโซน การจัดคิวงานที่กำหนดเอง หรือแยกการอัปเดตออกเป็นหลายหน่วยงานที่จะทํางานเมื่อเวลาผ่านไป

ในการแก้ปัญหานี้ DevTools จึงแสดงกลไกที่เรียกว่า "Async Stack Tagging API" ในออบเจ็กต์ console ซึ่งช่วยให้นักพัฒนาเฟรมเวิร์กสามารถบอกทั้งตำแหน่งที่กำหนดเวลาการดำเนินการและตำแหน่งที่ดำเนินการเหล่านี้

Async Stack Tagging API

หากไม่มีการติดแท็กสแต็กแบบแอซิงโครนัส สแต็กเทรซสําหรับโค้ดที่เฟรมเวิร์กเรียกใช้แบบแอซิงโครนัสด้วยวิธีที่ซับซ้อนจะปรากฏขึ้นโดยไม่มีการเชื่อมต่อกับโค้ดที่ตั้งเวลาไว้

สแต็กเทรซของโค้ดที่ดำเนินการแบบแอสซิงค์บางรายการที่ไม่มีข้อมูลเกี่ยวกับเวลาที่กําหนดเวลาไว้ โดยจะแสดงเฉพาะสแต็กเทรซที่เริ่มต้นจาก `requestAnimationFrame` แต่ไม่มีข้อมูลจากเวลาที่กําหนดเวลาไว้

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

สแต็กเทรซของโค้ดที่เรียกใช้แบบแอสซิงค์บางส่วนพร้อมข้อมูลเกี่ยวกับเวลาที่กําหนดเวลาไว้ โปรดสังเกตว่าตอนนี้มี "businessLogic" และ "schedule" ในสแต็กเทรซ ซึ่งต่างจากก่อนหน้านี้

โดยให้ใช้เมธอด console ใหม่ชื่อ console.createTask() ซึ่ง Async Stack Tagging API มีให้ ลายเซ็นมีดังนี้

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

การเรียกใช้ console.createTask() จะแสดงผลอินสแตนซ์ Task ซึ่งคุณจะนำไปใช้เรียกใช้โค้ดแบบแอ็กซิงโครนัสในภายหลังได้

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

นอกจากนี้ การดำเนินการแบบไม่พร้อมกันยังซ้อนกันได้ และ "สาเหตุที่แท้จริง" จะแสดงในสแต็กเทรซตามลำดับ

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

Async Stack Tagging API ใน Angular

ใน Angular มีการเปลี่ยนแปลง NgZone ซึ่งเป็นบริบทการดําเนินการของ Angular ที่ใช้กับงานแบบแอสซิงค์

เมื่อตั้งเวลางาน ระบบจะใช้ console.createTask() หากมี ระบบจะจัดเก็บอินสแตนซ์ Task ที่ได้ไว้เพื่อใช้งานต่อไป เมื่อเรียกใช้งาน NgZone จะใช้อินสแตนซ์ Task ที่เก็บไว้เพื่อเรียกใช้

การเปลี่ยนแปลงเหล่านี้อยู่ใน NgZone 0.11.8 ของ Angular ผ่านคำขอดึงข้อมูล #46693 และ #46958

เฟรมการโทรที่ใช้งานง่าย

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

ใน Angular เป็นเรื่องปกติที่จะเห็นเฟรมการเรียกที่มีชื่อ เช่น AppComponent_Template_app_button_handleClick_1_listener ในสแต็กเทรซ

ภาพหน้าจอของสแต็กเทรซที่มีชื่อฟังก์ชันที่สร้างขึ้นโดยอัตโนมัติ

ด้วยเหตุนี้ เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จึงรองรับการเปลี่ยนชื่อฟังก์ชันเหล่านี้ผ่านสําเนาที่มา หากสําเนาแผนที่ซอร์สโค้ดมีรายการชื่อสําหรับจุดเริ่มต้นของขอบเขตฟังก์ชัน (นั่นคือวงเล็บปีกผี่งด้านซ้ายของรายการพารามิเตอร์) เฟรมการเรียกใช้ควรแสดงชื่อนั้นในสแต็กเทรซ

เฟรมการเรียกใช้ที่เป็นมิตรใน Angular

การเปลี่ยนชื่อเฟรมการเรียกใช้ใน Angular เป็นการดำเนินการอย่างต่อเนื่อง เราคาดว่าการปรับปรุงเหล่านี้จะค่อยๆ เปิดตัวไปเรื่อยๆ

ขณะแยกวิเคราะห์เทมเพลต HTML ที่ผู้เขียนเขียนมา คอมไพเลอร์ Angular จะสร้างโค้ด TypeScript ซึ่งจะเปลี่ยนรูปแบบเป็นโค้ด JavaScript ที่เบราว์เซอร์โหลดและเรียกใช้ในที่สุด

ระบบจะสร้างแผนที่แหล่งที่มาเป็นส่วนหนึ่งของกระบวนการสร้างโค้ดนี้ด้วย ขณะนี้เรากําลังหาวิธีรวมชื่อฟังก์ชันในช่อง "names" ของแผนที่ซอร์สโค้ด และการอ้างอิงชื่อเหล่านั้นในการแมประหว่างโค้ดที่สร้างขึ้นกับโค้ดต้นฉบับ

ตัวอย่างเช่น หากมีการสร้างฟังก์ชันสําหรับโปรแกรมรับเหตุการณ์และชื่อฟังก์ชันนั้นไม่สะดวกสําหรับผู้ใช้หรือถูกนําออกระหว่างการลดขนาด ตอนนี้แผนที่ซอร์สโค้ดจะใส่ชื่อที่สะดวกสําหรับผู้ใช้มากขึ้นสําหรับฟังก์ชันนี้ในช่อง "names" และตอนนี้การแมปสําหรับจุดเริ่มต้นของขอบเขตฟังก์ชันจะอ้างอิงชื่อนี้ได้ (นั่นคือวงเล็บปีกผีเสื้อด้านซ้ายของรายการพารามิเตอร์) จากนั้น Chrome DevTools จะใช้ชื่อเหล่านี้เพื่อเปลี่ยนชื่อเฟรมการเรียกใช้ในสแต็กเทรซ

ในอนาคต

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

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