ประสบการณ์การแก้ไขข้อบกพร่องที่ได้รับการปรับปรุง
ในช่วง 2-3 เดือนที่ผ่านมา ทีม Chrome DevTools ได้ทำงานร่วมกับทีม Angular เพื่อเริ่มการปรับปรุงประสบการณ์การแก้ไขข้อบกพร่องในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ผู้คนจากทั้ง 2 ทีมได้ทำงานร่วมกันและดำเนินการตามขั้นตอนเพื่อช่วยให้นักพัฒนาซอฟต์แวร์แก้ไขข้อบกพร่องและสร้างโปรไฟล์เว็บแอปพลิเคชันจากมุมมองการเขียนได้ โดยใช้ภาษาต้นทางและโครงสร้างโปรเจ็กต์ ด้วยการเข้าถึงข้อมูลที่คุ้นเคยและมีความเกี่ยวข้องกับตน
โพสต์นี้จะอธิบายเบื้องหลังว่าต้องทำการเปลี่ยนแปลงใดบ้างใน Angular และเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome แม้ว่าการเปลี่ยนแปลงบางส่วนจะแสดงให้เห็นผ่าน Angular แต่ก็สามารถนำไปใช้กับเฟรมเวิร์กอื่นๆ ได้เช่นกัน ทีม Chrome DevTools ส่งเสริมให้เฟรมเวิร์กอื่นๆ ใช้ API ใหม่ของคอนโซลและจุดเข้าใช้งานแผนที่แหล่งที่มาเพื่อให้สามารถมอบประสบการณ์การแก้ไขข้อบกพร่องที่ดียิ่งขึ้นแก่ผู้ใช้ได้
รหัสรายชื่อที่ละเว้น
เมื่อแก้ไขข้อบกพร่องของแอปพลิเคชันโดยใช้เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ผู้เขียนต้องการดูเฉพาะเฉพาะโค้ดของตน ไม่ใช่ของเฟรมเวิร์กที่อยู่ด้านล่างหรือทรัพยากร Dependency ที่ซ่อนอยู่ในโฟลเดอร์ node_modules
เพื่อให้บรรลุเป้าหมายนี้ ทีมเครื่องมือสำหรับนักพัฒนาเว็บได้เปิดตัวส่วนขยายสำหรับการแมปแหล่งที่มาซึ่งเรียกว่า x_google_ignoreList
ส่วนขยายนี้ใช้เพื่อระบุแหล่งที่มาของบุคคลที่สาม เช่น โค้ดเฟรมเวิร์กหรือโค้ดที่สร้างโดย Bundler เมื่อเฟรมเวิร์กใช้ส่วนขยายนี้ ผู้เขียนจะหลีกเลี่ยงโค้ดที่ไม่ต้องการเห็นหรือเข้าไปผ่านโดยไม่ต้องกำหนดค่าด้วยตนเองล่วงหน้าโดยอัตโนมัติ
ในทางปฏิบัติ เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome สามารถซ่อนโค้ดที่ระบุในสแต็กเทรซ โครงสร้างซอร์ส กล่องโต้ตอบ Quick Open โดยอัตโนมัติ รวมถึงปรับปรุงลักษณะการทำงานของขั้นตอนและการกลับมาทำงานอีกครั้งในโปรแกรมแก้ไขข้อบกพร่อง
ส่วนขยายการแมปแหล่งที่มา x_google_ignoreList
ในการแมปแหล่งที่มา ช่อง x_google_ignoreList
ใหม่จะหมายถึงอาร์เรย์ sources
และแสดงรายการดัชนีแหล่งที่มาของบุคคลที่สามที่รู้จักทั้งหมดในแผนที่แหล่งที่มานั้น เมื่อแยกวิเคราะห์การแมปแหล่งที่มา เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จะใช้ส่วนนี้เพื่อตัดสินว่าส่วนใดของโค้ดที่ควรละเว้นอยู่ในรายการ
ด้านล่างนี้เป็นการแมปแหล่งที่มาของไฟล์ out.js
ที่สร้างขึ้น มี sources
ต้นฉบับ 2 รายการที่มีส่วนในการสร้างไฟล์เอาต์พุตคือ foo.js
และ lib.js
โซลูชันแรกคือสิ่งที่นักพัฒนาเว็บไซต์เขียนขึ้น และรายหลังคือเฟรมเวิร์กที่พวกเขาใช้
{
"version" : 3,
"file": "out.js",
"sourceRoot": "",
"sources": ["foo.js", "lib.js"],
"sourcesContent": ["...", "..."],
"names": ["src", "maps", "are", "fun"],
"mappings": "A,AAAB;;ABCDE;"
}
มี sourcesContent
สำหรับแหล่งที่มาดั้งเดิมทั้ง 2 แหล่งเหล่านี้ และเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จะแสดงไฟล์เหล่านี้โดยค่าเริ่มต้นในโปรแกรมแก้ไขข้อบกพร่อง
- เป็นไฟล์ในโครงสร้างแหล่งที่มา
- ตามผลลัพธ์ในกล่องโต้ตอบ Quick Open
- เป็นตำแหน่งเฟรมการเรียกใช้ที่แมปในสแต็กเทรซข้อผิดพลาดขณะหยุดชั่วคราวในเบรกพอยท์และขณะดำเนินการ
มีข้อมูลเพิ่มเติมอีก 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],
...
}
หากคุณเป็นนักพัฒนาเฟรมเวิร์กหรือ Bundler ให้ตรวจสอบว่าแมปแหล่งที่มาที่สร้างขึ้นระหว่างขั้นตอนการสร้างมีช่องนี้รวมอยู่ด้วยเพื่อทำความเข้าใจความสามารถใหม่ๆ เหล่านี้ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
x_google_ignoreList
ใน Angular
ตั้งแต่ Angular v14.1.0 เนื้อหาของโฟลเดอร์ node_modules
และ webpack
มีการทำเครื่องหมายเป็น "to ignore"
การดำเนินการดังกล่าวเกิดจากการเปลี่ยนแปลงใน angular-cli
โดยการสร้างปลั๊กอินที่เชื่อมต่อเข้ากับโมดูล Compiler
ของ 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 Caption API" ในออบเจ็กต์ console
ซึ่งช่วยให้นักพัฒนาเฟรมเวิร์กสามารถแนะนำทั้งตำแหน่งที่มีการกำหนดเวลาการดำเนินการและตำแหน่งการดำเนินการเหล่านี้
API การติดแท็กสแต็กแบบไม่พร้อมกัน
หากไม่มีการติดแท็กสแต็กแบบไม่พร้อมกัน สแต็กเทรซของโค้ดที่เรียกใช้แบบไม่พร้อมกันด้วยวิธีที่ซับซ้อนตามเฟรมเวิร์กจะปรากฏขึ้นโดยไม่มีการเชื่อมต่อกับโค้ดที่ตั้งเวลาไว้
เมื่อใช้การติดแท็กสแต็กแบบไม่พร้อมกัน คุณสามารถให้บริบทนี้ได้และสแต็กเทรซจะมีลักษณะดังนี้
ซึ่งทำได้โดยใช้เมธอด console
ใหม่ชื่อ console.createTask()
ซึ่งมี Async Stack Scheduling 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);
การดำเนินการแบบไม่พร้อมกันอาจมีการซ้อนกันและ "สาเหตุหลัก" จะปรากฏในสแต็กเทรซตามลำดับ
คุณเรียกใช้งานกี่ครั้งก็ได้ และเพย์โหลดงานอาจแตกต่างกันไปในการเรียกใช้แต่ละครั้ง ระบบจะจดจำสแต็กการเรียกใช้ที่เว็บไซต์การจัดตารางเวลาจนกว่าออบเจ็กต์งานจะเป็นขยะที่รวบรวม
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 DevTools รองรับการเปลี่ยนชื่อฟังก์ชันเหล่านี้ผ่านการแมปแหล่งที่มา หากการแมปแหล่งที่มามีรายการชื่อสําหรับจุดเริ่มต้นของขอบเขตฟังก์ชัน (กล่าวคือ วงเล็บด้านซ้ายของรายการพารามิเตอร์) เฟรมการเรียกใช้ควรแสดงชื่อนั้นในสแต็กเทรซ
เฟรมการโทรที่เป็นกันเองใน Angular
การเปลี่ยนชื่อเฟรมการเรียกใช้ใน Angular คือความพยายามอย่างต่อเนื่อง เราคาดว่าการปรับปรุงเหล่านี้จะค่อยๆ มีผลเมื่อเวลาผ่านไป
ขณะแยกวิเคราะห์เทมเพลต HTML ที่ผู้เขียนได้เขียนไว้ คอมไพเลอร์ Angular จะสร้างโค้ด TypeScript ซึ่งในที่สุดแล้วจะถูกแปลงเป็นโค้ด JavaScript ที่เบราว์เซอร์โหลดและเรียกใช้
ระบบจะสร้างการแมปแหล่งที่มาซึ่งเป็นส่วนหนึ่งของกระบวนการสร้างโค้ดนี้ด้วย เรากำลังสำรวจวิธีใส่ชื่อฟังก์ชันในช่อง "ชื่อ" ของการแมปแหล่งที่มา และอ้างอิงชื่อเหล่านั้นในการจับคู่ระหว่างโค้ดที่สร้างขึ้นกับโค้ดต้นฉบับ
ตัวอย่างเช่น หากมีการสร้างฟังก์ชันสำหรับ Listener เหตุการณ์และชื่อฟังก์ชันไม่เป็นมิตรหรือถูกนำออกระหว่างการลดขนาด ตอนนี้การแมปแหล่งที่มาสามารถรวมชื่อที่จำง่ายยิ่งขึ้นสำหรับฟังก์ชันนี้ในช่อง "ชื่อ" และการแมปสำหรับจุดเริ่มต้นของขอบเขตฟังก์ชันจะอ้างถึงชื่อนี้ (กล่าวคือ วงเล็บด้านซ้ายของรายการพารามิเตอร์) จากนั้นเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จะใช้ชื่อเหล่านี้เพื่อเปลี่ยนชื่อเฟรมการเรียกใช้ในสแต็กเทรซ
มองไปข้างหน้า
การใช้ Angular เป็นโครงการนำร่องเพื่อยืนยันว่างานของเราได้รับประสบการณ์ที่ดี เรายินดีรับฟังความคิดเห็นจากนักพัฒนาเฟรมเวิร์ก และให้ความคิดเห็นเกี่ยวกับประเด็นการขยายเวลาเหล่านี้
ยังมีพื้นที่อื่นๆ ที่เราอยากสำรวจอีก โดยเฉพาะอย่างยิ่งวิธีปรับปรุงประสบการณ์การทำโปรไฟล์ในเครื่องมือสำหรับนักพัฒนาเว็บ