โหลดเพจถัดไปเร็วขึ้นด้วย Guess.js และ React
หลังจากหลอกล่อให้ผู้คนเข้าใช้งานเว็บไซต์เราได้แล้ว หากเนื้อหามันใช่หรือโปรโมชันมันโดน ย่อมมีแนวโน้มที่ผู้ใช้งานจะท่องไปต่อที่หน้าเพจอื่น
ในยุคที่ Webpack และเครื่องมือ bundler เฟื่องฟู เราคงไม่แพ็ค JavaScript และ assets ทั้งหลายของเราเป็นก้อนเดียว โหลดหน้าโฮมเพจทีเดียว ตู้มมมม ได้ assets ที่จะใช้ทั้งเว็บไปทั้งก้อน แต่เรามักจะซอยย่อย ซอยยิก ๆ ส่วนของโค้ดเช่น JavaScript เป็นก้อน ๆ (chunk) หน้าเพจไหนต้องการใช้อะไรก็โหลดเฉพาะส่วนที่ต้องการ สิ่งนี้คือหลักการของ Code Spliting และ Lazy-Loaded Routes
สมมติว่าเว็บเรามีหน้าโฮมเพจ หน้า a b และ c เราทราบแล้วว่าเมื่อผู้ใช้งานอยู่หน้าโฮมเพจ (/) ก็มีแนวโน้มที่จะเปลี่ยนหน้าไปที่ a b หรือ c แต่การที่ผู้ใช้งานจะเปลี่ยนไปหน้าอื่นมันมีค่าใช้จ่ายอยู่คือการรอโหลดหน้าเพจถัดไป ทำอย่างไรหละจะให้หน้าเพจถัดไปโหลดได้เร็วขึ้น?
ความจริงที่ว่า a b และ c ผู้ใช้งานสามารถคลิกจากลิงก์ของหน้าโฮมเพจเพื่อเข้าถึงหน้าเหล่านี้ได้หมด หลังจากที่โฮมเพจโหลดเสร็จเราจึงต้องทำการ prefetch คือแอบโหลดหน้า a b และ c มาด้วย ด้วยวิธีการนี้พอผู้ใช้งานจิ้มลิงก์ของเพจไหนก็ตาม เขาเหล่านั้นจึงไม่ต้องเสียเวลารอโหลดหน้าเพจใหม่ เพราะเพจเหล่านี้ได้พร้อมเสริฟอยู่ในแคชเป็นที่เรียบร้อยแล้วนั่นเอง
ทว่าถ้าลิงก์ที่ไปต่อได้จากหน้าโฮมเพจมีซัก 100 ลิงก์หละจะเกิดอะไรขึ้น? คือถ้าที่บ้านเป็นโรงงานผลิต 4G แบบ True ก็คงไม่มีใครว่า เมื่อเป็นเช่นนี้การจะมา prefetch หน้าเพจถัด ๆ ไปทุกเพจจึงไม่ใช่ทางออกที่ดี
Google I/O 2018 มีสิ่งหนึ่งที่เป็นทางออกสำหรับปัญหานี้ นั่นคือ Guess.js
พยากรณ์เพจถัดไปด้วย Google Analytics
บริการอย่าง Google Analytics นอกจากช่วยให้เราทราบว่าผู้ใช้งานดูเพจไหนของเรามากเพียงใด มันยังสามารถบอกเราได้ว่าเมื่อผู้ใช้งานอยู่ที่เพจปัจจุบันแล้วมักจะไปที่เพจไหนต่อ เราจึงสามารถหาความน่าจะเป็นที่ผู้ใช้งานจะไปยังเพจถัดไปได้
ย้อนกลับมาที่รูปก่อนหน้า หากรายงานจาก Google Analytics แสดงว่าเมื่อผู้ใช้งานอยู่ที่โฮมเพจ มักจะไปต่อที่เพจ a ด้วยสัดส่วน 0.65 (65%) เพจ b ที่ 0.2 (20%) และ c ที่ 0.15 (15%) เราอาจตัดสินใจได้ว่าควรทำ prefetch เฉพาะเพจ a เมื่อเพจปัจจุบันคือโฮมเพจ
การพิจารณาว่าควรทำการ prefetch เพจไหนมาบ้างนั้น เป็นการพิจารณาโดยอิงกับเพจปัจจุบันที่ผู้ใช้กำลังใช้งานอยู่ เหตุนี้เราจึงต้องมีการสร้างกราฟแบบถ่วงน้ำหนัก (weight graph) ของเพจ เพื่อบอกว่าตามสถิติแล้วผู้ใช้ไปเพจถัดไปจำนวนเท่าไร
1{2 "": {3 "/blog": 15,4 "/login": 2,5 "/courses": 326 },7 "/blog": {8 "/login": 2,9 "/courses": 32,10 "/create-react-app": 12,11 "/css-in-js": 1,12 "/graphql": 3313 },14 ...15 ...16}
สัดส่วนการเข้าถึงเพจตาม chunk
ตามที่เราคุยกันไปแต่ต้น สมัยนี้ไม่มีใครเข้าเพจเดียวโหลด JavaScript มาตู้มเดียวทั้งเว็บแล้วครับ เราทำ Code Spliting เพื่อแบ่งโค้ดออกเป็น chunk ย่อย ๆ โดยส่วนใหญ่เรานิยมใช้เทคนิค Route-based code-splitting หรือการแบ่ง chunk ตามเส้นทางการเข้าถึง เช่น เมื่อเข้าสู่เพจ /articles ก็จะทำการโหลด chunk ของ articles และเมื่อเข้าสู่เพจ /users จึงทำการหยิบชิ้นส่วนของ users อีกทีนึง
Guess.js ช่วยให้การหาสัดส่วนการเข้าถึงตาม chunk เป็นเรื่องง่ายขึ้น ด้วยการสร้างโมเดล Markov Chain อิงตามน้ำหนักว่าเมื่อผู้ใช้เข้าสู่เพจนี้แล้วมักจะโหลด chunk ไหนต่อเป็นสัดส่วนเท่าไหร่ อย่าลืมนะครับเรามักแบ่ง chunk ตามเส้นทางการเข้าถึงเพจ ถ้าเข้าเพจไหนเยอะสัดส่วนของ chunk เพจนั้นก็จะสูงตาม
1{2 '/': [3 { chunk: 'articles.js', probability: 0.7 },4 { chunk: 'login.js', probability: 0.3 }5 ],6 '/articles': [7 { chunk: 'login.js', probability: 0.1 },8 { chunk: 'courses.js', probability: 0.9 }9 ],10 ...11}
การใช้งานค่าสัดส่วนเช่นนี้ช่วยให้เราได้ประโยชน์ในการพยากรณ์ล่วงหน้าว่าเพจถัดไปที่ผู้ใช้งานจะเข้าถึงคือเพจใด เราจะได้ทำการโหลดเพจนั้นเข้ามาก่อนล่วงหน้า เราอาจระบุเพิ่มเติมได้ว่าเมื่อการเชื่อมต่อเป็นแบบ 4G ให้โหลดเพจถัดไปทั้งหมดที่มีความน่าจะเป็นเกิน 0.15 เอาให้ปลายเดือนกินมาม่าแทนไปเลย หรือถ้าเน็ตเต่าตนุแบบ 2G ให้โหลดเฉพาะเพจถัดไปที่มีค่าสัดส่วนเกิน 0.5 เท่านั้น
Prefetch ด้วยค่าสัดส่วนทำงานอย่างไร
เมื่อเราเข้าถึงหน้าเพจหนึ่ง ๆ ก็ถึงเวลาที่ต้องตัดสินใจว่าเพจถัดไปที่ควรโหลดมาล่วงหน้าคือ chunk อะไร หลังจากพิจารณาตามค่าสัดส่วนแล้วพบว่า chunk ไหนจะถูกโหลด เราเพียงทำการเพิ่ม <link rel='prefetch' href='<CHUNK>.js'>
เข้าไปในส่วน head ของเพจปัจจุบัน เพียงเท่านี้เพจถัดไปก็จะถูกโหลดและทำการแคชพร้อมเสริฟเป็นที่เรียบร้อย
ใช้งาน Guess.js กับ React
ขั้นตอนต่าง ๆ ฟังเหมือนจะง่าย แต่ถ้าจะให้ทำเองคงขอตายแผล็บ หัวข้อนี้เราจึงจะมาใช้ Guess.js เพื่อช่วยพิจารณาการ prefetch chunk ถัดไปกันครับ
ก่อนอื่นเราต้องทำ Code Spliting ตาม route เสียก่อน เนื่องจาก Guess.js จะคาดเดาเพจถัดไปแล้วทำการ prefetch ตาม chunk
1<Route path="/articles" component={AsyncComponent(() => import('./articles'))} />2<Route path="/courses" component={AsyncComponent(() => import('./courses'))} />3<Route path="/login" component={AsyncComponent(() => import('./login'))} />
จากนั้นจึงทำการติดตั้ง GuessPlugin ใช้งานคู่กับ Webpack
1new GuessPlugin({2 GA: '123456789',3 period: {4 startDate: new Date('2016-1-1'),5 endDate: new Date('2018-2-24'),6 },7 routeFormatter(r) {8 return r.replace(/^\/app/, '')9 },10})
สั้น ๆ เลยนะฮะ -- จบ
ปุกาศ ปุกาศ ตอนนี้ทางเพจ Babel Coder มีสอนพัฒนาโมบายแอพพลิเคชันด้วย React Native ด้วยหละ เรียนวันที่ 23 - 24 มิย 2561 ครับ
รายละเอียดเพิ่มเติม จิ้มลิงก์นี้จ้า
เอกสารอ้างอิง
Guess.js. Retrieved May, 11, 2018, from https://github.com/guess-js/guess
Guess.js React demo. Retrieved May, 11, 2018, from https://github.com/mgechev/guess-js-react-demo
Machine Learning-Driven Bundling. The Future of JavaScript Tooling.. Retrieved May, 11, 2018, from https://blog.mgechev.com/2018/03/18/machine-learning-data-driven-bundling-webpack-javascript-markov-chain-angular-react/
สารบัญ
- พยากรณ์เพจถัดไปด้วย Google Analytics
- สัดส่วนการเข้าถึงเพจตาม chunk
- Prefetch ด้วยค่าสัดส่วนทำงานอย่างไร
- ใช้งาน Guess.js กับ React
- เอกสารอ้างอิง