By Arnon Puitrakul - 18 มีนาคม 2021
เป็นคำถามที่น่าสนใจมาก เพราะก่อนหน้านี้เราเองก็เคยสงสัยเมื่อนานมาแล้วเหมือนกัน โดยเฉพาะตอนที่เราเรียนพวกเรื่อง HTML ก็บอกกันว่า <script> ควรอยู่ที่ Head นะ สักพักนึงพอเราไปอ่าน Document ของพวกที่ใช้ Javascript รัน ก็บอกให้เอาไปไว้ท้าย Body เอ้า เลยไปไกลเลยนั่น วันนี้เรามาลองดูเหตุผลกันว่า ทำไมเขาถึงบอกแบบนั้นกัน และเราควรทำแบบไหน จะได้ไม่ลำบากลูกหลานน้อง ๆ โปรแกรมเมอร์ที่ต้องมานั่งหัวเสียต่อจากเรากัน
ก่อนที่เราจะไปชี้นิ้วบอกว่า แกต้องวางมันไว้ตรงไหน เรามาทำความเข้าใจกันก่อนว่ามันจัดการกับ Script Tag อย่างไร เรื่องราวมันจะต้องเริ่มจาก Web Browser ได้รับ HTML Document จาก Web Server ตามที่ Web Browser ได้ทำการ Request ไปก่อนหน้า
HTML Document ที่ได้รับจาก Web Server มันก็จะเป็น Text Format ธรรมดาที่เราสามารถอ่านได้ แต่ Web Browser ไม่สามารถทำความเข้าใจมันได้โดยตรง มันจะต้องทำการแปลง หรือ Parse เพื่อให้เป็นรูปแบบที่ Web Browser ทำงานได้ง่าย ซึ่งเราเรียก Format นี้ว่า Document Object Model (DOM)
ถ้าเราอยากรู้ว่า Web Browser มัน Parse มาแล้วเป็นยังไง ลอง Inspect เราจะเห็นว่า HTML มันถูกอ่าน และแปลงให้เป็น Format ที่หน้าตาเหมือน Tree ถ้าเราจิ้ม Div Tag มันก็จะเปิดลูกของมันออกมา นั่นละคือการที่มันอ่าน HTML
ณ เวลาที่มันอ่าน มันก็จะค่อย ๆ อ่านไปทีละบรรทัดเรื่อย ๆ จนมาถึงบรรทัดเจ้าปัญหาของเราในวันนี้คือ <script> สิ่งที่มันทำคือ มันจะยิง Request ไปที่ Web Server หรือ Server ที่มีไฟล์ Javascript ตัวนั้นอยู่ และทำการโหลดไฟล์มา
หลังจากที่ไฟล์ถูกโหลดแล้ว Javascript ที่โหลดมาจะถูก Execute แต่ระหว่าง การโหลด และ Execute Javascript การ Parse HTML จะถูกหยุดชะงักไว้ก่อนจนกว่าจะทำเสร็จ หลังจากเสร็จ มันถึงจะกลับมา Parse HTML ต่อ ถ้าเจออีกก็ทำอีกไปเรื่อย ๆ
document.addEventListener('DOMContentLoaded', (event) => {
// Do Sth
})
ทำให้เวลาเราเขียน Javascript เพื่อ Manipulate DOM เราจะต้องเขียน Block ด้านบนไว้ เพื่อสั่งให้ Script Block นี้มันทำงาน เมื่อ DOM ถูก Parse เรียบร้อยแล้ว ไม่งั้น เราจะได้เจอ Error ว่ามันหาตำแหน่งที่เราอ้างถึงไม่เจอนั่นเอง
พอเวลาเราไปอ่าน Document ของบาง Library เขาจะบอกให้เราวาง <script> ของเขาไว้ที่ท้ายของ Body คำตอบอยู่ที่เราบอกไปเมื่อกี้แล้วว่า เขาต้องการให้ DOM ถูก Parse ออกมาให้หมดก่อน แล้วค่อยให้ Script ทำงานทีหลังไป อาจจะเป็น Script ที่เข้ามา Manipulate DOM เลยยิ่งทำให้เราต้องวางมันไว้ที่ท้ายเสมอ
บาง Script ที่เขียนออกมา ที่บอกให้เราเอาไว้วางไว้ด้านล่างก็อาจจะไม่ได้เขียน Script สำหรับการเช็คสถานะการโหลด ก็ทำให้ถ้าเราเอาไปวางด้านบน มันก็จะมี Error ออกมาว่า มันหา DOM ตำแหน่งนั้น ๆ ไม่เจอ เราก็เลยต้องเอาไปไว้ข้างล่างนั่นเอง
คำตอบขึ้นกับว่า Script ของเราทำอะไร ถ้าเราต้องการ Manipulate DOM จาก Event ต่าง ๆ เช่นการกดปุ่ม แล้วมีอะไรเปลี่ยนแปลง อันนั้น เวลาเราเขียน เราจะต้องมี Target ของ DOM ที่เราต้องการเปลี่ยนแปลง และ แน่นอนว่าส่วนนี้มันจะเกิดขึ้นตอนที่หน้าถูกโหลดจนครบ User ถึงจะกดได้ ทำให้ของพวกนี้ เราจะต้องวางไว้ด้านล่าง เพื่อให้มั่นใจว่าหน้าทั้งหมดถูก Parse เรียบร้อยแล้วก่อนที่ User จะเห็นและกดนั่นเอง
ตรงกันข้าม ถ้าเราเรียกของที่เราอาจจะต้องใช้ก่อนที่หน้าจะโหลด เราก็สามารถเอาไปวางไว้ที่ Head ก็ทำให้ Script นี้ถูกโหลด และ รันก่อนที่หน้าจะโหลดได้นั่นเอง ดังนั้นมันเลยขึ้นกับว่า Script ของเราทำอะไร และเราต้องการมันตอนไหนนั่นเอง
ข้อเสียของการที่เราต้องวาง Script บนบ้าง ล่างบ้าง ปัญหาคือ มันอยู่ไม่เป็นที่สักที ทำให้บางทีมันน่าปวดหัวในการเข้าไปนั่งหาอะไรแบบนี้ไปเรื่อย ๆ มันน่าจะดี ถ้าเราสามารถวางไว้บน Head และ ให้มันทำงานทีหลังได้ โดยที่ไม่กระทบกับ Performance ในการโหลดของเรา
ทำให้ Web Browser รุ่นใหม่ ๆ ก็มีการ Implement ของเพิ่มเติม เพื่อให้นักพัฒนาสามารถบอกให้ Web Browser จัดการกับ Script นี้อย่างไร ผ่าน 2 คำคือ Differ กับ Async
<script src="script.js" async></script>
เราสามารถใส่ Async ไว้ใน Script Tag ได้เลย เพื่อเป็นการบอกให้ Web Browser รู้ว่า Script File นี้สามารถ โหลด และ Execute เป็นเบื้องหลัง ทำให้ไม่ต้องหยุดการ Parse HTML ได้ ทำให้ User ไม่เจอหน้าขาว ถ้า Script เราใช้เวลาในการทำงานนานแล้ว
<script src="script.js" defer></script>
แต่ถ้าเป็น Differ มันจะสั่งให้ Javascript ถูกโหลดจาก Server ลงมาก่อน และ ให้ Execute เมื่อหน้าทั้งหมดถูกโหลด และ Parse เรียบร้อยแล้ว และถ้ามีหลายอัน มันก็จะค่อย ๆ ไล่ Execute มาทีละอัน อีกด้วย ทำให้จริง ๆ แล้ว มันก็เหมาะกับ การที่เราเอาไปใส่กับพวก Script ที่ให้วางไว้ท้าย ๆ ของไฟล์
สรุปของบทความนี้สั้นเลยคือ หยุด พักก่อน กับการเอา Script ไปใส่ไว้ที่ต้นบ้าง ท้ายบ้าง หรือที่อื่น ๆ บ้าง เขียนให้มันอยู่บน <head> ที่เดียวเลย แต่ถ้าอันที่เราต้องการให้มันทำงานแบบไหน เราก็เลือกใส่ Differ หรือ Async เข้าไป ก็จะช่วยทำให้ เราได้เรื่อง Performance ที่ดีขึ้น ผู้ใช้ไม่ต้องมานั่งดูหน้าขาว กว่าหน้าจะโหลด
เราเป็นคนที่อ่านกับซื้อหนังสือเยอะมาก ปัญหานึงที่ประสบมาหลายรอบและน่าหงุดหงิดมาก ๆ คือ ซื้อหนังสือซ้ำเจ้าค่ะ ทำให้เราจะต้องมีระบบง่าย ๆ สักตัวในการจัดการ วันนี้เลยจะมาเล่าวิธีการที่เราใช้ Obsidian ในการจัดการหนังสือที่เรามีกัน...
หากเราเรียนลงลึกไปในภาษาใหม่ ๆ อย่าง Python และ Java โดยเฉพาะในเรื่องของการจัดการ Memory ว่าเขาใช้ Garbage Collection นะ ว่าแต่มันทำงานยังไง วันนี้เราจะมาเล่าให้อ่านกันว่า จริง ๆ แล้วมันทำงานอย่างไร และมันมีเคสใดที่อาจจะหลุดจนเราต้องเข้ามาจัดการเองบ้าง...
ก่อนหน้านี้เราเปลี่ยนมาใช้ Zigbee Dongle กับ Home Assistant พบว่าเสถียรขึ้นเยอะมาก อุปกรณ์แทบไม่หลุดออกจากระบบเลย แต่การติดตั้งมันเข้ากับ Synology DSM นั้นมีรายละเอียดมากกว่าอันอื่นนิดหน่อย วันนี้เราจะมาเล่าวิธีการเพื่อใครเอาไปทำกัน...
เมื่อหลายวันก่อนมีพี่ที่รู้จักกันมาถามว่า เราจะโหลด CSV ยังไงให้เร็วที่สุด เป็นคำถามที่ดูเหมือนง่ายนะ แต่พอมานั่งคิด ๆ ต่อ เห้ย มันมีอะไรสนุก ๆ ในนั้นเยอะเลยนี่หว่า วันนี้เราจะมาเล่าให้อ่านกันว่า มันมีวิธีการอย่างไรบ้าง และวิธีไหนเร็วที่สุด เหมาะกับงานแบบไหน...