โดย มาร์วิน ฟราเชต์
ปีนี้ดูเหมือนจะเป็นปีแห่ง React คุณคงเคยได้ยินเกี่ยวกับฟีเจอร์นักฆ่าใหม่ที่มาพร้อมกับ 16.7-alpha.0 — Hooks คุณคงเคยได้ยินเกี่ยวกับสิ่งที่ยอดเยี่ยมและเจ๋งอื่นๆ เช่น Time Slicing หรือแม้แต่ Suspense
บทความนี้ ไม่ มุ่งเป้าไปที่การอธิบายวิธีใช้คุณสมบัติใหม่บางอย่าง แต่เป็นการพิสูจน์ว่าคุณสมบัติเหล่านั้นถูกสร้างขึ้นมาอย่างไร เพียงเพื่อประโยชน์ในการทำความเข้าใจสิ่งที่เรากำลังเล่นด้วย
มันยังเขียนไว้ในวิธีที่ฉันค้นพบฟีเจอร์นี้ด้วย อาจไม่ใช่วิธีที่คิดไว้ แต่นี่คือวิธีที่ฉันได้คะแนน
สิ่งที่คุณจะพบขณะอ่าน:
- Async JavaScript และลูปเหตุการณ์
- เอฟเฟกต์พีชคณิตใน React พร้อมตัวอย่าง
- เฟสของไฟเบอร์และปฏิกิริยา
ทำไมฉันถึงเขียนโพสต์นี้
สิ่งที่ทำให้ฉันอยากเขียนโพสต์นี้คือคุณลักษณะพิเศษและทดลองที่ช่วยให้สามารถใช้ อะซิงโครนัส ได้ การดำเนินการโดยใช้ ซิงโครนัส เอพีไอ:
04รหัส> ?… อะไรนะ? ซิงโครนัส ?!
ไลบรารีแคชตอบสนองสร้างความสามารถในการใช้การดำเนินการแบบอะซิงโครนัสกับ API แบบซิงโครนัส นี่คือฟีเจอร์ที่ทำให้ฉันต้องการเรียนรู้วิธีการทำงานของ React ภายใต้ประทุน นี่คือการนำเสนอโดย Dan Abramov และ Andrew Clark ในห้องสมุดนี้:
มันเป็นไปได้อย่างไร? เราจะรับข้อมูลระยะไกลโดยใช้การโทรแบบซิงโครนัสได้อย่างไร
มาเจาะลึกในตัวอย่างนี้และพยายามทำความเข้าใจว่า react-cache นำฟังก์ชันดังกล่าวไปใช้อย่างไร และค้นหาว่ามันทำงานอย่างไร เรื่องราวนี้เริ่มต้นด้วยสถาปัตยกรรมไฟเบอร์
การควบคุมการทำงานของ JavaScript
สถาปัตยกรรมไฟเบอร์ช่วยให้ React ควบคุมการดำเนินการงานได้ มันถูกสร้างขึ้นเพื่อแก้ไขปัญหาต่างๆ มากมายที่ React ประสบ นี่คือสองสิ่งที่ดึงดูดความสนใจของฉัน:
- การจัดลำดับความสำคัญเหนือเหตุการณ์เฉพาะ เช่น ข้อมูลของผู้ใช้มากกว่าการดึงข้อมูล
- การแยกการคำนวณ React แบบอะซิงโครนัสเพื่อรักษาความพร้อมใช้งานของเธรดหลักและเพื่อหลีกเลี่ยงการบล็อกในระหว่างกระบวนการเรนเดอร์ที่ยาวนาน
ทุกสิ่งที่ทำให้เกิดการเปลี่ยนแปลงสถานะ ไม่ใช่แค่กับ React เท่านั้น แต่ภายในแอปพลิเคชัน JavaScript นั้นเกิดจากการดำเนินการแบบอะซิงโครนัส ซึ่งรวมถึง 13 , 21รหัส> และผู้ฟังเหตุการณ์
การดำเนินการแบบอะซิงโครนัสได้รับการจัดการผ่านแนวคิดหลัก JavaScript หลายรายการ:
- งาน (ไมโคร มาโคร เรนเดอร์ ฯลฯ...)
- วงเหตุการณ์
- คอลสแต็ก
หากคุณไม่คุ้นเคยกับแนวคิดเหล่านี้ ฉันขอแนะนำให้คุณดูวิดีโอนี้โดย Jake Archibald:
ต้องขอบคุณไฟเบอร์ที่ทำให้อินพุตของผู้ใช้ได้รับการแก้ไข ก่อนการดำเนินการอะซิงโครนัสอื่นๆ เช่น การดึงข้อมูลการโทร
สิ่งนี้เป็นไปได้อย่างไร
การพูดคุยของ Archibald ข้างต้นถือเป็นการปูทางครั้งแรกในเส้นทางการเรียนรู้ของฉันเกี่ยวกับการทำงานของลูปเหตุการณ์ เขาบอกว่างานเล็กๆ ที่สร้างผ่าน Promise API เป็นต้น จะถูกดำเนินการและล้าง ก่อน งานแมโครถัดไป กระบวนการนี้ใช้วิธีการเรียกกลับเช่น 35 .
ดังนั้น หากคุณจำการเปรียบเทียบ "การป้อนข้อมูลของผู้ใช้กับการดึงข้อมูล" ของฉันได้ ทีมงานสร้าง 43 ได้อย่างไร ความละเอียด หลัง 52รหัส> ความละเอียด?
แนวคิดเหล่านี้ไม่ตรงกับข้อกำหนดเดียวกัน นั่นคือ WhatWG / HTML5 / Ecma-262 และได้มาจากที่อื่น เช่น เบราว์เซอร์หรือกลไก JS
ฉันหมายถึงว่าเราควรแก้ไข 64 อย่างไร หลัง 76 ?
มันฟังดูบ้าบอมากสำหรับฉัน และมันยากมากที่จะเข้าใจว่ามันทำงานอย่างไร ความจริงก็คือมันเกิดขึ้นในระดับที่สูงกว่า
ต่อมา ฉันได้ชมการพูดคุยที่น่าทึ่งจาก Brandon Dail ที่ React Rally สิ่งนี้นำเสนอคุณสมบัติ Time Slicing และ Suspense ใหม่ที่มาพร้อมกับสถาปัตยกรรมไฟเบอร์ React:
ตามข้อมูลของ Dail ไฟเบอร์ก็เหมือนกับ JavaScript callstack ปกติ โดยที่แต่ละรายการในสแต็กเรียกว่า ไฟเบอร์ . มันแตกต่างกับ callstack ที่อาศัย เฟรม ซึ่งแสดงถึง ฟังก์ชัน (+ ข้อมูลเมตา) แต่ไฟเบอร์แสดงถึงส่วนประกอบ (+ ข้อมูลเมตา) . เรามาดูกันว่าไฟเบอร์เป็นเหมือนกล่องขนาดใหญ่รอบๆ ส่วนประกอบที่รู้ทุกอย่างเกี่ยวกับมัน
มีความแตกต่างที่สำคัญระหว่างสองแนวคิดนี้
ประการแรก Callstack เป็นฟังก์ชันที่ถูกสร้างขึ้นจาก การขับเคลื่อนส่วนดั้งเดิม โค้ด JavaScript . มีจุดมุ่งหมายเพื่อซ้อนการเรียกใช้ฟังก์ชัน JavaScript ทุกครั้งและเรียกใช้งานด้วยตนเอง แต่ละครั้งที่เราเรียกใช้ฟังก์ชันฟังก์ชันจะถูกเพิ่มลงในสแต็ก หากไม่มี Callstack เราจะไม่สามารถมี StackTrace ข้อผิดพลาดที่ชัดเจนและละเอียดได้ และเนื่องจากคอลสแต็กไม่สามารถเข้าถึงได้ด้วยโค้ด JavaScript จึงเป็นเรื่องยากมากและเป็นไปไม่ได้เลยที่จะควบคุมมัน
ในทางกลับกัน ไฟเบอร์ — เหมือนกองไฟเบอร์ — แสดงถึงแนวคิดเดียวกัน แต่สร้างขึ้นใน โค้ด JavaScript หน่วยที่เล็กที่สุดไม่ใช่ฟังก์ชัน แต่เป็นส่วนประกอบ. จริงๆ แล้วมันทำงานในจักรวาล JavaScript
ความจริงที่ว่าสถาปัตยกรรมไฟเบอร์ถูกสร้างขึ้นอย่างสมบูรณ์ใน JavaScript หมายความว่าเราสามารถใช้ เข้าถึง และแก้ไขมันได้ เราสามารถแก้ไขได้โดยใช้ JavaScript มาตรฐาน
สิ่งที่ผลักดันฉันไปในทิศทางที่ผิดคือฉันคิดว่า React กำลังใช้วิธีแก้ปัญหาเพื่อตัดวิธีการทำงานของ JavaScript ภายใน ไม่เป็นเช่นนั้น . ไฟเบอร์เป็นเพียงอ็อบเจ็กต์ JavaScript ที่เป็นเจ้าของข้อมูลเกี่ยวกับส่วนประกอบ React และสามารถโต้ตอบกับวงจรชีวิตได้ มันสามารถดำเนินการกับฟังก์ชันภายในของ React เท่านั้น
แนวคิดนี้ ไม่ เพื่อกำหนดวิธีการทำงานของ JavaScript ใหม่ เช่น การบอก 83 ควรดำเนินการแก้ไขไมโครทาสก์ก่อนงานโทรกลับ มันขึ้นอยู่กับว่าวิธีการ React ควรถูกเรียกหรือไม่ ในบริบทที่เฉพาะเจาะจง เช่น การขัดจังหวะการเรียกใช้เมธอดวงจรการใช้งานที่แตกต่างกัน
เฮ้รอ! คุณบอกว่าไฟเบอร์สามารถควบคุมทุกอย่างในแอป React ได้ใช่ไหม แต่ส่วนประกอบจะบอก React ให้หยุดทำอะไรได้อย่างไร
เอฟเฟกต์พีชคณิต ใช่ แต่ใน JavaScript ได้โปรด
React สามารถควบคุมส่วนประกอบต่างๆ และรู้ว่าส่วนประกอบกำลังทำงานอยู่หรือไม่ ต้องขอบคุณสถาปัตยกรรมไฟเบอร์ สิ่งที่ขาดหายไปในตอนนี้คือวิธีบอก React ว่ามีการเปลี่ยนแปลงบางอย่างสำหรับส่วนประกอบเฉพาะ ดังนั้นมันจะจัดการกับการเปลี่ยนแปลงนี้
นี่คือจุดที่ผลกระทบเกี่ยวกับพีชคณิต เข้าสู่เกม
เอฟเฟกต์พีชคณิตไม่ใช่สิ่งที่มีอยู่ใน JavaScript ฉันจะพยายามอธิบายว่ามันคืออะไรด้วยคำอธิบายในระดับที่สูงกว่า
เอฟเฟกต์พีชคณิตเป็นแนวคิดที่ช่วยให้สามารถส่งข้อมูลบางอย่างไปที่ไหนสักแห่งได้ เช่นเดียวกับผู้มอบหมายงาน แนวคิดคือการเรียกใช้ฟังก์ชันเฉพาะที่จะ ขัดจังหวะ ฟังก์ชันที่กำลังทำงานอยู่ในตำแหน่งที่แม่นยำเพื่อให้ฟังก์ชันหลักจัดการการคำนวณ เมื่อการคำนวณพาเรนต์เสร็จสิ้น โปรแกรมสามารถกลับมาทำงานต่อที่ตำแหน่งเริ่มต้นที่มีการส่งข้อมูลไปแล้ว
บางภาษา เช่น OCaml หรือ Eff ได้รับประโยชน์จากฟีเจอร์เหล่านี้โดยกำเนิด นี่เป็นนามธรรมที่น่าสนใจมาก เนื่องจากรายละเอียดการใช้งานจะขึ้นอยู่กับพาเรนต์เท่านั้น:
คงจะดีไม่น้อยถ้ามีฟีเจอร์ดังกล่าวใน JavaScript
ทีม React ได้สร้างแนวทางที่คล้ายกันในบริบท React ที่เกี่ยวข้องกับ JavaScript 93 บล็อก ตามข้อมูลของ Dail มันเป็นแนวคิดที่ใกล้เคียงที่สุดใน JavaScript
การขว้างบางสิ่งบางอย่างทำให้สามารถส่งข้อมูลไปยังผู้ปกครองที่ไหนสักแห่งได้ ผู้ปกครองคนแรกที่จับข้อมูลได้สามารถจัดการกับข้อมูลและคำนวณข้อมูลได้
ตัวอย่างดีกว่าคำพูดนับพันคำ
ลองนึกภาพโค้ดต่อไปนี้ที่พยายามดึงข้อมูล Bulbasaur โดยใช้ API แบบซิงโครนัส :
โค้ดชิ้นนี้อาจแปลกเนื่องจากการดึงข้อมูลโดยใช้ API แบบซิงโครนัสไม่ใช่เรื่องปกติ กระโดดเข้าไปใน 100 กัน การใช้งานฟังก์ชัน:
โอ้รอ! นี่มันดูไม่เหมือนการเอาของเลยจริงๆ! ฉันไม่เข้าใจว่าฟังก์ชันนี้ตั้งเป้าไว้ทำอะไรเลย…
ลองจินตนาการถึงบางสิ่งรอบๆ ส่วนประกอบ สมมติว่ามีเส้นใยที่มีลักษณะดังนี้:
ใช้เวลาอ่านโค้ดสักครู่
ตอนนี้ เรามาข้ามไปที่ 118 กันดีกว่า การใช้งาน:
ส่วนสำคัญในตัวอย่างก่อนหน้าคือ 123 บล็อก.
มาสรุปสิ่งที่เกิดขึ้นผ่านโค้ดส่วนต่างๆ เหล่านี้:
137รหัส> ส่วนประกอบเรียก140วิธีการ.- The
152เมธอดพยายามอ่านแคชภายใน แต่มันว่างเปล่า ดังนั้นมันจึงพ่นบางสิ่ง / ที่ไหนสักแห่ง — เอฟเฟกต์พีชคณิต 169รหัส> parent จับข้อมูลนั้น จัดการ และดึงข้อมูล จากนั้นจะเติม172แคชกับข้อมูล- การเรนเดอร์ซ้ำเกิดขึ้นใน
180และตอนนี้190แคชเต็ม ขณะนี้ข้อมูลพร้อมใช้งานในส่วนประกอบโดยใช้ API แบบซิงโครนัส
ลองดูที่ 205 รายละเอียดการใช้งานและตรวจสอบการพ่นที่แตกต่างกัน
บางสิ่งอาจดึงดูดความสนใจของคุณในระหว่างกระบวนการนี้:213 ถูกเรียกสองครั้ง หนึ่งรายการสำหรับการแจ้งข้อผิดพลาด — หยุดชั่วคราว ส่วนประกอบ — และอีกอันสำหรับรับข้อมูล — ดำเนินการต่อ ส่วนประกอบ เป็นเรื่องปกติที่ React จะทริกเกอร์ 223 หลายรายการ โทรออกเนื่องจากเป็นเพียงฟังก์ชันเพียงอย่างเดียว — ไม่มีผลข้างเคียงใดๆ ในตัวเอง
เดี๋ยว…อะไรนะ? 231รหัส> ไม่มีผลข้างเคียงใช่ไหม? แล้ว DOM ล่ะ?
ระยะตอบสนอง
หากคุณทำงานกับ React มาเป็นเวลานาน คุณอาจเคยได้ยินมาว่าการเรนเดอร์ซ้ำหลายๆ ครั้งไม่ใช่แนวปฏิบัติที่ดี ก่อนสถาปัตยกรรมไฟเบอร์ ทุกครั้งที่เราเรียกใช้ฟังก์ชันเรนเดอร์ React จะทำการคำนวณภายใน จากนั้นจึงแก้ไข DOM ตามนั้น ตัวอย่างเช่น สิ่งนี้เกิดขึ้นเมื่อเรียกใช้ฟังก์ชันเรนเดอร์ผ่าน 245 . กระบวนการอยู่ในบรรทัด:
254รหัส> →269รหัส> → เปรียบเทียบโหนดเสมือน → อัปเดตโหนด DOM
การจัดการกับไฟเบอร์ กระบวนการจะแตกต่างออกไปเล็กน้อย โดยได้แนะนำแนวคิดเกี่ยวกับคิวและแบทช์ที่ช่วยให้สามารถแก้ไข DOM ที่มีประสิทธิภาพสูงได้
ความคิดนี้ค่อนข้างง่าย เราถือว่าหน้าจอสามารถทำงานได้ประมาณ 60 เฟรมต่อวินาที จากสมมติฐานนี้ และการใช้ฟังก์ชัน JavaScript ที่มีอยู่ เป็นไปได้ที่จะทำการคำนวณและการแก้ไข DOM ทุกๆ ~16.7ms เท่านั้น เมื่อใช้ไฟเบอร์ React สามารถจัดคิวการแก้ไขได้หลายรายการและดำเนินการประมาณ 60 ครั้งต่อวินาที
การปรับเปลี่ยนประเภทนี้ทำให้ React แบ่งออกเป็นสามระยะโดยมีข้อดีและลักษณะเฉพาะของตัวเอง:
_[Dan Abramov เกี่ยวกับ React เฟส](https://twitter.com/dan_abramov/status/981712092611989509/photo/1?ref_src=twsrc%5Etfw%7Ctwcamp%5Etwitterembed%7Ctwter m%5E981712092611989509&ref_url=https%3A%2F%2Fmedium.com%2Fmedia%2Fbda1c34a16e9f8a8e3eb244716a1da72%3FpostId%3D2a57dc9c2e6d" rel="noopener" target="blank" title=") ป>
- ระยะการเรนเดอร์นั้นบริสุทธิ์และถูกกำหนดไว้แล้ว ไม่มีผลข้างเคียงและฟังก์ชันต่างๆ ที่ประกอบขึ้นสามารถเรียกได้หลายครั้ง ระยะการเรนเดอร์สามารถขัดจังหวะได้ — ไม่ใช่
273ฟังก์ชั่นที่อยู่ในโหมดหยุดชั่วคราว แต่ทั้งเฟส - วลีที่คอมมิตล่วงหน้ามีจุดมุ่งหมายเพื่อให้สามารถเข้าถึงสถานะ DOM จริง เช่น ตำแหน่งแถบเลื่อน ในโหมดอ่าน
- ขั้นตอนการดำเนินการแก้ไข DOM จริง ๆ และ ไม่ถูกขัดจังหวะ . การตอบสนองไม่สามารถหยุดชั่วคราวในระหว่างระยะนั้นได้
ชุดของสามเฟสนี้ได้นำเสนอความสามารถในการแบ่งเวลา React สามารถหยุดชั่วคราวในระหว่างเฟสการเรนเดอร์ ระหว่างการเรียกใช้ฟังก์ชันคอมโพเนนต์สองครั้ง และเพื่อกลับมาดำเนินการเฟสนั้นต่อเมื่อจำเป็น
ในไฟเบอร์ 288 มุ่งหวังที่จะรับการเป็นตัวแทนล่าสุดที่มีอยู่เท่านั้น ของส่วนประกอบตามสถานะภายในเพื่อทำการเปรียบเทียบและทราบว่า React จำเป็นต้องเปลี่ยน DOM หรือไม่ หากจำเป็นต้องมีการแก้ไขการคอมมิต มันจะเพิ่มการแก้ไขลงในคิว "งานอยู่ระหว่างดำเนินการ"
ทีม React ได้ทำการปรับปรุงประสิทธิภาพอย่างมากด้วย React Concurrent (Time Slicing + Suspense) และสถาปัตยกรรมไฟเบอร์ พวกเขาได้สร้างวิธีแก้ปัญหาชั่วคราวเพื่อรับมือกับปัญหาเบราว์เซอร์ต่างๆ เช่น การจัดลำดับความสำคัญของเหตุการณ์และการทำงานพร้อมกัน
ถ้าเราถอยออกไปสักก้าว นั่นเป็นสิ่งที่พวกเขาแสดงให้เห็นไม่ใช่หรือ? การจัดลำดับความสำคัญดูเหมือนจะเป็นความท้าทายใหม่สำหรับเบราว์เซอร์และเฟรมเวิร์กส่วนหน้า
ทีมอื่นๆ กำลังทำงานเพื่อปรับปรุงสถานะที่แท้จริงของศิลปะและเสนอ API ในอนาคตด้วย นี่คือสิ่งที่ Google ต้องการ:
เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น