นับตั้งแต่เปิดตัว React ได้เปลี่ยนวิธีที่นักพัฒนาส่วนหน้าคิดในการสร้างเว็บแอป ด้วย DOM เสมือน React ทำให้การอัปเดต UI มีประสิทธิภาพเท่าที่เคยเป็นมาทำให้เว็บแอปพลิเคชันของคุณมีประสิทธิภาพมากขึ้น แต่เหตุใดเว็บแอป React ที่มีขนาดปานกลางจึงยังมีแนวโน้มที่จะทำงานได้ไม่ดี
เบาะแสอยู่ในวิธีที่คุณใช้ React
ห้องสมุดส่วนหน้าที่ทันสมัยเช่น ตอบสนอง ไม่ได้ทำให้แอปของคุณเร็วขึ้นอย่างน่าอัศจรรย์ ผู้พัฒนาต้องทำความเข้าใจว่า React ทำงานอย่างไรและส่วนประกอบมีชีวิตอย่างไรในช่วงต่างๆของวงจรชีวิตส่วนประกอบ
ด้วย React คุณจะได้รับการปรับปรุงประสิทธิภาพมากมายที่มีให้โดยการวัดและปรับแต่งว่าส่วนประกอบของคุณแสดงผลอย่างไรและเมื่อใด และ React มีเพียงเครื่องมือและฟังก์ชันที่จำเป็นเพื่อให้ง่ายขึ้น
ในบทช่วยสอน React นี้คุณจะได้เรียนรู้วิธีวัดประสิทธิภาพของส่วนประกอบ React ของคุณและปรับแต่งให้เหมาะสมเพื่อสร้างเว็บแอป React ที่มีประสิทธิภาพมากขึ้น คุณจะได้เรียนรู้ว่าแนวทางปฏิบัติที่ดีที่สุดของ JavaScript ช่วยในการสร้าง React web app ของคุณมอบประสบการณ์การใช้งานที่คล่องแคล่วมากขึ้นได้อย่างไร
ก่อนที่เราจะดำดิ่งสู่เทคนิคการเพิ่มประสิทธิภาพเราจำเป็นต้องมีความเข้าใจมากขึ้นว่า React ทำงานอย่างไร
ที่แกนกลางของ การพัฒนาปฏิกิริยา คุณมีไวยากรณ์ JSX ที่เรียบง่ายและชัดเจนและความสามารถของ React ในการสร้างและเปรียบเทียบ DOM เสมือน . นับตั้งแต่เปิดตัว React มีอิทธิพลต่อไลบรารีฟรอนต์เอนด์อื่น ๆ อีกมากมาย ไลบรารีเช่น Vue.js ยังอาศัยแนวคิดของ DOM เสมือน
นี่คือวิธีการทำงานของ React:
แอปพลิเคชัน React แต่ละตัวเริ่มต้นด้วยส่วนประกอบของรูทและประกอบด้วยส่วนประกอบต่างๆมากมายในการสร้างต้นไม้ ส่วนประกอบใน React คือ“ ฟังก์ชัน” ที่แสดงผล UI ตามข้อมูล (อุปกรณ์ประกอบฉากและสถานะ) ที่ได้รับ
เราสามารถใช้สัญลักษณ์นี้เป็น F
UI = F(data)
ผู้ใช้โต้ตอบกับ UI และทำให้ข้อมูลเปลี่ยนแปลง ไม่ว่าการโต้ตอบจะเกี่ยวข้องกับการคลิกปุ่มแตะที่รูปภาพลากรายการไปรอบ ๆ คำขอ AJAX ที่เรียกใช้ API ฯลฯ การโต้ตอบทั้งหมดจะเปลี่ยนข้อมูลเท่านั้น พวกเขาไม่เคยทำให้ UI เปลี่ยนแปลงโดยตรง
ที่นี่ข้อมูลคือทุกสิ่งที่กำหนดสถานะของเว็บแอปพลิเคชันไม่ใช่เฉพาะสิ่งที่คุณเก็บไว้ในฐานข้อมูลของคุณ แม้แต่บิตของสถานะส่วนหน้า (เช่นแท็บใดที่ถูกเลือกในปัจจุบันหรือว่ามีการเลือกช่องทำเครื่องหมายอยู่หรือไม่) ก็เป็นส่วนหนึ่งของข้อมูลนี้
เมื่อใดก็ตามที่มีการเปลี่ยนแปลงในข้อมูลนี้ React จะใช้ฟังก์ชันคอมโพเนนต์เพื่อเรนเดอร์ UI อีกครั้ง แต่แทบจะ:
UI1 = F(data1) UI2 = F(data2)
React จะคำนวณความแตกต่างระหว่าง UI ปัจจุบันและ UI ใหม่โดยใช้อัลกอริทึมการเปรียบเทียบกับ DOM เสมือนทั้งสองเวอร์ชัน
Changes = Diff(UI1, UI2)
ตอบสนองจากนั้นดำเนินการต่อเพื่อใช้เฉพาะการเปลี่ยนแปลง UI กับ UI จริงบนเบราว์เซอร์
เมื่อข้อมูลที่เกี่ยวข้องกับการเปลี่ยนแปลงส่วนประกอบ React จะพิจารณาว่าจำเป็นต้องมีการอัปเดต DOM จริงหรือไม่ สิ่งนี้ช่วยให้ React หลีกเลี่ยงการดำเนินการจัดการ DOM ที่อาจมีราคาแพงในเบราว์เซอร์เช่นการสร้างโหนด DOM และการเข้าถึงโหนดที่มีอยู่เกินความจำเป็น
การแสดงผลและการแสดงผลส่วนประกอบที่แตกต่างกันซ้ำ ๆ นี้อาจเป็นหนึ่งในแหล่งที่มาหลักของปัญหาประสิทธิภาพการตอบสนองในแอป React ใด ๆ การสร้างแอป React โดยที่อัลกอริทึมที่แตกต่างกันล้มเหลวในการกระทบยอดอย่างมีประสิทธิภาพทำให้แอปทั้งหมดแสดงผลซ้ำ ๆ กันอาจส่งผลให้ประสบการณ์การใช้งานช้าลงอย่างน่าผิดหวัง
แต่อะไรคือสิ่งที่เราเพิ่มประสิทธิภาพ?
คุณจะเห็นว่าในระหว่างขั้นตอนการเรนเดอร์เริ่มต้น React จะสร้างโครงสร้าง DOM ดังนี้:
เนื่องจากส่วนหนึ่งของการเปลี่ยนแปลงข้อมูลสิ่งที่เราต้องการให้ React ทำคือการแสดงผลใหม่เฉพาะส่วนประกอบที่ได้รับผลกระทบโดยตรงจากการเปลี่ยนแปลง (และอาจข้ามแม้กระทั่งกระบวนการที่แตกต่างสำหรับส่วนประกอบที่เหลือ):
อย่างไรก็ตามสิ่งที่ React ทำคือ:
ในภาพด้านบนโหนดสีเหลืองทั้งหมดจะแสดงผลและแตกต่างกันทำให้เสียเวลา / ทรัพยากรในการคำนวณ นี่คือจุดที่เราจะใช้ความพยายามในการเพิ่มประสิทธิภาพเป็นหลักการกำหนดค่าแต่ละองค์ประกอบให้แสดงผล - ความแตกต่างเมื่อจำเป็นเท่านั้นจะช่วยให้เราสามารถเรียกคืนรอบ CPU ที่สูญเปล่าเหล่านี้ได้
นักพัฒนาของไลบรารี React ได้คำนึงถึงสิ่งนี้และจัดเตรียมเบ็ดให้เราทำสิ่งนั้น: ฟังก์ชันที่ช่วยให้เราบอก React ได้เมื่อสามารถข้ามการแสดงผลองค์ประกอบได้
อย่างที่ Rob Pike ทำให้มันดูหรูหราเป็นหนึ่งในเขา กฎของการเขียนโปรแกรม :
วัด. อย่าปรับความเร็วจนกว่าคุณจะวัดได้และถึงแม้ว่าส่วนหนึ่งของโค้ดจะล้นเกินส่วนที่เหลือ
อย่าเริ่มเพิ่มประสิทธิภาพโค้ดที่คุณคิดว่าอาจทำให้แอปของคุณทำงานช้าลง แต่ให้เครื่องมือวัดประสิทธิภาพ React ช่วยแนะนำคุณตลอดเส้นทาง
React มี เครื่องมือที่มีประสิทธิภาพ เพียงเพื่อสิ่งนี้ การใช้ react-addons-perf
ห้องสมุดคุณสามารถดูภาพรวมของประสิทธิภาพโดยรวมของแอปของคุณ
การใช้งานนั้นง่ายมาก:
Import Perf from 'react-addons-perf' Perf.start(); // use the app Perf.stop(); Perf.printWasted();
สิ่งนี้จะพิมพ์ตารางพร้อมกับจำนวนเวลาที่เสียไปในการแสดงผล
ไลบรารีมีฟังก์ชันอื่น ๆ ที่ช่วยให้คุณสามารถพิมพ์แง่มุมต่างๆของเวลาที่เสียไปแยกกัน (เช่นการใช้ฟังก์ชัน printInclusive()
หรือ printExclusive()
) หรือแม้แต่พิมพ์การดำเนินการจัดการ DOM (โดยใช้ printOperations()
ฟังก์ชัน).
หากคุณเป็นคนที่มองเห็นภาพแล้วล่ะก็ react-perf-tool
เป็นเพียงสิ่งที่คุณต้องการ
react-perf-tool
ขึ้นอยู่กับ react-addons-perf
ห้องสมุด. ช่วยให้คุณเห็นภาพมากขึ้นในการแก้ไขข้อบกพร่องของแอป React ของคุณ ใช้ไลบรารีพื้นฐานเพื่อรับการวัดจากนั้นแสดงภาพเป็นกราฟ
บ่อยกว่านั้นนี่เป็นวิธีที่สะดวกกว่ามากในการระบุปัญหาคอขวด คุณสามารถใช้งานได้อย่างง่ายดายโดยเพิ่มเป็นส่วนประกอบในแอปพลิเคชันของคุณ
โดยค่าเริ่มต้น React จะทำงานแสดงผล DOM เสมือนและเปรียบเทียบความแตกต่างของทุกองค์ประกอบในโครงสร้างสำหรับการเปลี่ยนแปลงในอุปกรณ์ประกอบฉากหรือสถานะ แต่เห็นได้ชัดว่าไม่สมเหตุสมผล
เมื่อแอปของคุณเติบโตขึ้นการพยายามแสดงผลซ้ำและเปรียบเทียบ DOM เสมือนทั้งหมดในทุกการกระทำจะช้าลงในที่สุด
React เป็นวิธีง่ายๆสำหรับนักพัฒนาในการระบุว่าคอมโพเนนต์ต้องการการเรนเดอร์หรือไม่ นี่คือที่ shouldComponentUpdate
วิธีการเข้ามาเล่น
function shouldComponentUpdate(nextProps, nextState) { return true; }
เมื่อฟังก์ชันนี้คืนค่าเป็นจริงสำหรับส่วนประกอบใด ๆ ฟังก์ชันนี้จะอนุญาตให้ทริกเกอร์กระบวนการ render-diff
สิ่งนี้ช่วยให้คุณมีวิธีง่ายๆในการควบคุมกระบวนการ render-diff เมื่อใดก็ตามที่คุณต้องการป้องกันไม่ให้คอมโพเนนต์แสดงซ้ำเลยเพียงแค่ส่งคืน false
จากฟังก์ชัน ภายในฟังก์ชันนี้คุณสามารถเปรียบเทียบอุปกรณ์ประกอบฉากและสถานะชุดปัจจุบันและชุดถัดไปเพื่อพิจารณาว่าจำเป็นต้องมีการเรนเดอร์ซ้ำหรือไม่:
function shouldComponentUpdate(nextProps, nextState) { return nextProps.id !== this.props.id; }
เพื่อให้เทคนิคการเพิ่มประสิทธิภาพนี้ง่ายขึ้นและเป็นอัตโนมัติ React จึงให้สิ่งที่เรียกว่าองค์ประกอบ 'บริสุทธิ์' ก React.PureComponent
เหมือนกับ React.Component
ที่ใช้ shouldComponentUpdate()
ฟังก์ชันที่มีการเปรียบเทียบเสาและสถานะตื้น
ก React.PureComponent
มากหรือน้อยเทียบเท่ากับสิ่งนี้:
class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this.props, nextProps) && shallowCompare(this.state, nextState); } … }
เนื่องจากทำการเปรียบเทียบแบบตื้น ๆ เท่านั้นคุณอาจพบว่ามีประโยชน์ก็ต่อเมื่อ:
forceUpdate()
เพื่ออัปเดตส่วนประกอบของคุณจะเป็นอย่างไรถ้าคุณสามารถใช้ React.PureComponent
แต่ยังมีวิธีที่มีประสิทธิภาพในการบอกเมื่ออุปกรณ์หรือสถานะที่ซับซ้อนมีการเปลี่ยนแปลงโดยอัตโนมัติ? นี่คือที่โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปทำให้ชีวิตง่ายขึ้น
แนวคิดเบื้องหลังการใช้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปเป็นเรื่องง่าย เมื่อใดก็ตามที่วัตถุที่มีการเปลี่ยนแปลงข้อมูลที่ซับซ้อนแทนที่จะทำการเปลี่ยนแปลงในออบเจ็กต์นั้นให้สร้างสำเนาของออบเจ็กต์นั้นพร้อมกับการเปลี่ยนแปลง สิ่งนี้ทำให้การตรวจจับการเปลี่ยนแปลงข้อมูลทำได้ง่ายเพียงแค่เปรียบเทียบการอ้างอิงของวัตถุทั้งสอง
คุณสามารถใช้ Object.assign
หรือ _.extend
(จาก Underscore.js หรือ Lodash):
const newValue2 = Object.assign({}, oldValue); const newValue2 = _.extend({}, oldValue);
ยิ่งไปกว่านั้นคุณสามารถใช้ไลบรารีที่จัดเตรียมโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปได้:
var map1 = Immutable.Map({a:1, b:2, c:3}); var map2 = map1.set('b', 2); assert(map1.equals(map2) === true); var map3 = map1.set('b', 50); assert(map1.equals(map3) === false);
ที่นี่ Immutable.Map
จัดทำโดยห้องสมุด ไม่เปลี่ยนรูป js .
ทุกครั้งที่มีการอัปเดตแผนที่ด้วยวิธีการ set
แผนที่ใหม่จะถูกส่งกลับก็ต่อเมื่อการดำเนินการตั้งค่าเปลี่ยนค่าพื้นฐาน มิฉะนั้นแผนที่เดิมจะถูกส่งกลับ
คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการใช้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูป ที่นี่ .
เมื่อพัฒนาแอป React คุณจะได้รับคำเตือนและข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์จริงๆ สิ่งเหล่านี้ทำให้การระบุจุดบกพร่องและปัญหาระหว่างการพัฒนาเป็นความสุข แต่พวกเขามาพร้อมกับต้นทุนของประสิทธิภาพ
หากคุณดูซอร์สโค้ดของ React คุณจะเห็น if (process.env.NODE_ENV != 'production')
จำนวนมาก ตรวจสอบ โค้ดเหล่านี้ที่ React ทำงานอยู่ในสภาพแวดล้อมการพัฒนาของคุณไม่ใช่สิ่งที่ผู้ใช้ต้องการ สำหรับสภาพแวดล้อมการใช้งานจริงรหัสที่ไม่จำเป็นทั้งหมดนี้สามารถทิ้งได้
หากคุณบูตโปรเจ็กต์โดยใช้ create-react-app
คุณสามารถเรียกใช้ npm run build
เพื่อสร้างงานสร้างโดยไม่มีรหัสพิเศษนี้ หากคุณใช้ Webpack โดยตรงคุณสามารถเรียกใช้ webpack -p
(ซึ่งเทียบเท่ากับ webpack --optimize-minimize --define process.env.NODE_ENV=''production''
.
เป็นเรื่องปกติมากที่จะเห็นฟังก์ชันที่เชื่อมโยงกับบริบทของส่วนประกอบภายในฟังก์ชันการแสดงผล กรณีนี้มักเกิดขึ้นเมื่อเราใช้ฟังก์ชันเหล่านี้เพื่อจัดการกับเหตุการณ์ของส่วนประกอบย่อย
// Creates a new `handleUpload` function during each render() // ...as do inlined arrow functions this.handleUpload(files)} />
ซึ่งจะทำให้เกิด render()
ฟังก์ชันเพื่อสร้างฟังก์ชันใหม่ในทุกการเรนเดอร์ วิธีที่ดีกว่ามากในการทำเช่นเดียวกันคือ:
class App extends React.Component { constructor(props) { super(props); this.handleUpload = this.handleUpload.bind(this); } render() { … … } }
สำหรับเว็บแอป React แบบหน้าเดียวเรามักจะรวมโค้ด JavaScript ส่วนหน้าทั้งหมดไว้ในไฟล์ที่ย่อขนาดไฟล์เดียว วิธีนี้ใช้ได้ดีกับเว็บแอปขนาดเล็กถึงขนาดปานกลาง แต่เมื่อแอปเริ่มเติบโตการส่งไฟล์ JavaScript ที่รวมมานี้ไปยังเบราว์เซอร์ในตัวอาจกลายเป็นกระบวนการที่ใช้เวลานาน
หากคุณใช้ Webpack เพื่อสร้างแอป React ของคุณคุณสามารถใช้ประโยชน์จากความสามารถในการแยกโค้ดเพื่อแยกโค้ดแอพที่สร้างขึ้นของคุณออกเป็น 'กลุ่ม' หลาย ๆ อันและส่งไปยังเบราว์เซอร์ตามความจำเป็น
การแบ่งมีสองประเภท ได้แก่ การแบ่งทรัพยากรและการแยกรหัสตามความต้องการ
ด้วยการแยกทรัพยากรคุณสามารถแบ่งเนื้อหาทรัพยากรออกเป็นไฟล์หลายไฟล์ ตัวอย่างเช่นการใช้ CommonsChunkPlugin คุณสามารถแยกโค้ดทั่วไป (เช่นไลบรารีภายนอกทั้งหมด) ลงในไฟล์ 'chunk' ของตัวเองได้ การใช้ ExtractTextWebpackPlugin คุณสามารถแยกโค้ด CSS ทั้งหมดลงในไฟล์ CSS แยกกันได้
การแยกแบบนี้จะช่วยได้สองทาง ช่วยให้เบราว์เซอร์แคชทรัพยากรที่มีการเปลี่ยนแปลงไม่บ่อยเหล่านั้น นอกจากนี้ยังช่วยให้เบราว์เซอร์ใช้ประโยชน์จากการดาวน์โหลดแบบขนานเพื่อลดเวลาในการโหลดลง
คุณสมบัติที่โดดเด่นกว่าของ Webpack คือการแยกรหัสตามความต้องการ คุณสามารถใช้เพื่อแบ่งโค้ดออกเป็นกลุ่มที่โหลดได้ตามต้องการ วิธีนี้สามารถทำให้การดาวน์โหลดครั้งแรกมีขนาดเล็กและลดเวลาในการโหลดแอป จากนั้นเบราว์เซอร์สามารถดาวน์โหลดโค้ดอื่น ๆ ได้ตามต้องการเมื่อแอปพลิเคชันต้องการ
คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการแบ่งรหัส Webpack ที่นี่ .
โดยทั่วไปแล้วไฟล์ React ของแอป React จะมีขนาดใหญ่มากดังนั้นเพื่อให้หน้าเว็บโหลดเร็วขึ้นเราสามารถเปิดใช้งาน Gzip บนเว็บเซิร์ฟเวอร์ (Apache, Nginx และอื่น ๆ )
เบราว์เซอร์สมัยใหม่ทั้งหมดสนับสนุนและเจรจาการบีบอัด Gzip สำหรับคำขอ HTTP โดยอัตโนมัติ การเปิดใช้งานการบีบอัด Gzip สามารถลดขนาดของการตอบกลับที่โอนได้ถึง 90% ซึ่งสามารถลดระยะเวลาในการดาวน์โหลดทรัพยากรได้อย่างมากลดการใช้ข้อมูลสำหรับไคลเอ็นต์และปรับปรุงเวลาในการแสดงผลหน้าเว็บครั้งแรกของคุณ
ตรวจสอบเอกสารสำหรับเว็บเซิร์ฟเวอร์ของคุณเกี่ยวกับวิธีเปิดใช้งานการบีบอัด:
คุณควรใช้ ESLint สำหรับเกือบทุกโปรเจ็กต์ JavaScript รีแอคไม่ต่างกัน
ด้วย eslint-plugin-react
คุณจะต้องบังคับตัวเองให้ปรับตัวให้เข้ากับกฎมากมายในการเขียนโปรแกรม React ซึ่งจะเป็นประโยชน์ต่อโค้ดของคุณในระยะยาวและหลีกเลี่ยงปัญหาและปัญหาทั่วไปมากมายที่เกิดขึ้นเนื่องจากโค้ดที่เขียนไม่ดี
วิธีทำโทเค็น
ในการใช้ React ให้เกิดประโยชน์สูงสุดคุณต้องใช้ประโยชน์จากเครื่องมือและเทคนิคต่างๆ ประสิทธิภาพของเว็บแอป React อยู่ที่ความเรียบง่ายของส่วนประกอบ การใช้อัลกอริทึมการแสดงผลที่แตกต่างกันมากเกินไปอาจทำให้แอปของคุณทำงานได้ไม่ดีในรูปแบบที่น่าหงุดหงิด
ก่อนที่คุณจะสามารถเพิ่มประสิทธิภาพแอปของคุณคุณจะต้องเข้าใจวิธีการ ตอบสนอง ส่วนประกอบทำงานและวิธีแสดงผลในเบราว์เซอร์ วิธี React lifecycle ให้วิธีป้องกันไม่ให้คอมโพเนนต์ของคุณแสดงผลซ้ำโดยไม่จำเป็น ขจัดปัญหาคอขวดเหล่านั้นและคุณจะมีประสิทธิภาพของแอปที่ผู้ใช้ของคุณสมควรได้รับ
แม้ว่าจะมีหลายวิธีในการเพิ่มประสิทธิภาพเว็บแอป React แต่การปรับแต่งคอมโพเนนต์อย่างละเอียดเพื่ออัปเดตเมื่อจำเป็นเท่านั้นที่ให้การปรับปรุงประสิทธิภาพที่ดีที่สุด
คุณวัดและเพิ่มประสิทธิภาพ React web app ได้อย่างไร แบ่งปันในความคิดเห็นด้านล่าง
ที่เกี่ยวข้อง: การดึงข้อมูลเก่าขณะตรวจสอบความถูกต้องด้วย React Hooks: คำแนะนำคุณควรเริ่มการวัดประสิทธิภาพของรหัสของคุณเสมอ บางครั้งโค้ดที่ดูเหมือน“ ง่าย” อาจเป็นสาเหตุที่ทำให้แอปของคุณทำงานช้าลง วัดประสิทธิภาพของรหัสการตอบสนองของคุณด้วยเครื่องมือที่มีให้ก่อนที่จะดำน้ำในการเพิ่มประสิทธิภาพ
ซึ่งแตกต่างจาก React.Component คือ React.PureComponent จะแสดงผลอีกครั้งเมื่อมีการเปลี่ยนแปลงสถานะหรืออุปกรณ์ประกอบฉากของส่วนประกอบเท่านั้น ทำการเปรียบเทียบแบบตื้นเพื่อตรวจจับการเปลี่ยนแปลง คุณสามารถใช้ประโยชน์จากพฤติกรรมนี้โดยใช้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปในสถานะที่เปลี่ยนแปลงการอ้างอิงเมื่อใดก็ตามที่เนื้อหามีการเปลี่ยนแปลง