มีอะไรใหม่บ้างใน Next.js 6.0

Nuttavut Thongjor

React ยุคน้ำแข็งเราต่างเริ่มโปรเจคกันด้วยสารพัดการตั้งค่า โดยเฉพาะอย่างยิ่งคือ Webpack... เกลียด การมาของแจ็คหม่าทำให้การศึกษาไทยพัฒนาขึ้น สมัยนี้เมื่อจะเริ่มโปรเจค React เราต่างนึกถึง create-react-app ทว่าเมื่อต้องการความสะดวกในการทำ Server-Side Rendering เข้าไปด้วย มันจึงยากที่จะเลี่ยงพูดถึงเครื่องมืออีกตัวที่ชื่อ Next.js ไม่ได้

ไม่กี่ชั่วโมงที่ผ่านมา Next.js เวอร์ชัน 6.0 ได้ถือกำเนิดขึ้น คุณสมบัติหลายอย่างที่ทำให้ Next.js นั้นง่ายอยู่แล้ว ยิ่งดีงามพระรามนางสีดาเข้าไปอีก ไม่ว่าคุณจะใช้ Next.js ที่กรุงเทพหรือหมู่บ้านป่าแหว่งความง่ายของมันก็มิต่างกัน ทำไมหนะรึ ไปดูกันฮะ

ความเร็วในการพัฒนาที่เพิ่มขึ้น

รายงานใน Github จากกระทาชายนายนึงระบุว่า Next.js เวอร์ชัน 5 นั้นใช้เวลา build ไฟล์แรกเริ่มได้ช้าบัดซบเมื่อเทียบกับเวอร์ชันก่อนหน้า ผมนี้อยากยืนขึ้นพร้อมปรบมือดัง ๆ ซัก 99 แปะ เพราะมันเป็นเช่นนั้นจริง ๆ

Next.js เวอร์ชันใหม่นี้จึงได้ทำการเปลี่ยน source map ให้เหมาะสมมากขึ้นในโหมดนักพัฒนา โดยเปลี่ยนจาก source-map เป็น cheap-module-source-map ที่ build / rebuild ได้เร็วกว่า ส่วนจะเร็วแค่ไหนต้องลองเองแล้ว จุดธูปสามดอก ท่องนะโมสามจบก่อนรันโปรเจคเพื่อความขลัง

เหนือกว่าการเปลี่ยนประเภท source-map คิอการทำให้ stacktraces ดูมีชาติตระกูลมากขึ้น คราใดที่ข้อผิดพลาดเกิดขึ้น source-map ที่เกี่ยวข้องจะได้รับเลือกเพื่อเข้าคู่ในการแสดงผลข้อผิดพลาดนั้น ๆ ได้อย่างถูกต้องมากขึ้น

Next.js 6.0 ยังมาพร้อมกับการอัพเกรดเวอร์ชันของ Babel (ไม่ใช่ Babel Coder นะ!) เป็นเวอร์ชัน 7 อะไร ๆ ที่ทำได้ในเวอร์ชัน 7 ของ Babel จึงเป็นอานิสงค์ตกค้างเหมือนอาหารปนเปื้อนมากับ Next.js ด้วย

แต่เดิมเราสามารถใช้ Fragment ได้ผ่านการนำเข้าจากไลบรารี่ของ React

JSX
1import { Fragment, Component } from 'react'
2
3class App extends Component {
4 render() {
5 ;<Fragment>
6 <h2>Fragment Na Ja</h2>
7 <div>...</div>
8 </Fragment>
9 }
10}

ทางลัดใหม่ที่ทำได้คือ

JSX
1import { Component } from 'react'
2
3class App extends Component {
4 render() {
5 ;<>
6 <h2>Fragment Na Ja</h2>
7 <div>...</div>
8 </>
9 }
10}

ก็นั่นหละฮะ อะไรที่เป้นเลข 7 ก็จะฟินจนเหมือนขึ้นสวรรค์ชั้น 7 แต่โดยส่วนตัวแล้วผมคงใช้ Fragment เช่นเดิมครับมันคงไม่หนักหนาอะไรกับการเพิ่มไม่กี่ตัวอักษรแต่สื่อความได้ชัดเจนขึ้น

สามารถเข้าถึง build manifest ได้แล้ว

เราทราบกันดีว่า Next.js นั้นมาพร้อมกับการจัดการ CSS ด้วย styled-jsx แต่นั่นไม่ใช่ทั้งหมดที่ทำได้ เพราะหากเราเหม็นขี้หน้า styled-jsx แล้วมั่นหน้าที่จะใช้ CSS ต่อไปเราก็สามารถทำได้ผ่าน next-css plugin

JSX
1// ./pages/_document.js
2import Document, { Head, Main, NextScript } from 'next/document'
3
4export default class MyDocument extends Document {
5 render() {
6 return (
7 <html>
8 <Head>
9 <link rel="stylesheet" href="/_next/static/style.css" />
10 </Head>
11 <body>
12 <Main />
13 <NextScript />
14 </body>
15 </html>
16 )
17 }
18}

การใช้ next-css จะทำให้ได้ผลลัพธ์ออกมาเป็นก้อน CSS อยู่ในไฟล์ /_next/static/style.css เพื่อให้สไตล์ชีทนี้คงสดใสอยู่ในหน้าเพจเราจึงต้องเรียกใช้งานมันผ่าน link

สิ่งหนึ่งที่เราสังเกตได้นั่นคือบน production ไฟล์ของเรายังไงก็คงเป็น style.css เสมอเพราะ Next.js ไม่เคยเปลี่ยนชื่อมัน แม้ครั้งถัดไปที่เราโยน style.css ตัวใหม่ลงเซิฟเวอร์ แต่เว็บเบราเซอร์ก็จะไม่ดูดไฟล์ใหม่ไปใช้ นั่นเพราะแท็ก link ยังอ้างถึงชื่อเดิมคือ style.css เบราเซอร์จึงคิดว่าของที่แคชอยู่ก็ใช้ได้อยู่แล้วเพราะชื่อเดียวกัน ใยต้องไปโหลดใหม่ให้เสียเวลาหละ สิ่งที่เราควรแก้ไขจึงเป็นการต่อตูดไฟล์ด้วยอักขระอะไรซักอย่างให้แปลกแยกจากของเดิม เบราเซอร์จะได้โหลดใหม่ทุกครั้งที่ไฟล์ CSS ของเราเปลี่ยน

Next.js 6.0 จะทำการส่ง buildManifest เข้ามาให้ทำให้เราสามารถเรียกใช้เพื่อประกบค่า chunkhash เข้าไปได้

JSX
1// ./pages/_document.js
2import Document, { Head, Main, NextScript } from 'next/document'
3import flush from 'styled-jsx/server'
4
5export default class MyDocument extends Document {
6 render() {
7 const { buildManifest, pathname } = this.props
8 const { css } = buildManifest
9 return (
10 <html>
11 <Head>
12 {css.map((file) => {
13 return <link rel="stylesheet" href={`/_next/${file}`} key={file} />
14 })}
15 {}
16 </Head>
17 <body className="custom_class">
18 {this.props.customValue}
19 <Main />
20 <NextScript />
21 </body>
22 </html>
23 )
24 }
25}

ทั้งนี้หน้าตาของเจ้า buildManifest ก็จะคล้าย ๆ แบบนี้ฮะ

JavaScript
1{
2 "css": [
3 "static/style.1234567890.css"
4 ],
5 "main.js": [
6 "static/commons/main.0987654321.js"
7 ],
8 "manifest.js": [
9 "static/commons/manifest.11223344.js"
10 ]
11}

นั่นจึงทำให้รอบแรก style.css ของเราอาจมีชื่อเป็น style.1234567890.css แต่ในการแก้ไขและ build ครั้งถัดไปอาจเป็น style.prayuuuuuuuuud.css แทน

สำหรับ main.js และ manifest.js จะมีการเพิ่ม chunkhash ให้อัตโนมัติบน production

Custom คอมโพแนนท์ App ได้อย่างที่ใจต้องการ

เป็นความรู้สึกตะเตือนไตระดับ 10 ริกเตอร์ที่เมื่ออยากมี header และ footer เหมือนกันในทุกหน้าเพจ ต้องอัญเชิญคอมโพแนนท์เหล่านี้ไปใส่ในทุก page ของ Next (หรือยัดใส่ Layout แล้วนำ Layout ไปใช้ทุก Page)

สมมติเรามีเพจ A และ B เมื่อต้องการให้มี Header และ Footer เหมือนกัน เราจึงต้องนำ Layout มาประยุกต์ใช้เช่นนี้

JSX
1// components/shared/Layout.js
2
3class Layout extends Component {
4 render() {
5 return (
6 <Fragment>
7 <Header />
8 {this.props.children}
9 <Footer />
10 </Fragment>
11 )
12 }
13}
14
15// pages/a.js
16class A extends Component {
17 render() {
18 ;<Layout>Page A</Layout>
19 }
20}
21
22// pages/b.js
23class B extends Component {
24 render() {
25 return <Layout>Page B</Layout>
26 }
27}

ทว่าการครอบ Layout ทับเป็นสิ่งที่ปรากฎอยู่ในทุกหน้าเพจไฉนเราจึงต้องเขียนซ้ำไปซ้ำมาในทุก ๆ เพจด้วย

Next.js มาพร้อมกับคุณสมบัติใหม่ที่ทำให้เราแก้ไขดัดแปลง App.js อันเป็นคอมโพแนนท์ตัวบนของเพจต่าง ๆ ได้ เมื่อเป็นเช่นนี้เราจึงสามารถย้าย Layout ไปอยู่ใน App.js โดยไม่ต้องนั่งเทียนเขียนมันในทุกหน้าเพจ

JSX
1// pages/_app.js
2import App, { Container } from 'next/app'
3
4export default class MyApp extends App {
5 render() {
6 const { Component, pageProps } = this.props
7 return (
8 <Container>
9 <Layout>
10 <Component {...pageProps} />
11 </Layout>
12 </Container>
13 )
14 }
15}

นอกจากปัญหาของ Layout แล้ว App.js ยังช่วยแก้ปัญหาของการแสดงผลอนิเมชันเมื่อทำการเปลี่ยนหน้าเพจได้ด้วย อีกทั้งอะไร ๆ ที่เราต้องยัดลงทุกหน้าเพจเช่น withRedux ก็อาจย้ายมาอยู่ที่ App.js ตัวเดียวได้เช่นกัน

อีกตัวอย่างหนึ่งของการใช้งานคือการดักจับข้อผิดพลาดที่เกิดขึ้นบนคอมโพแนนท์ต่าง ๆ ในระบบ ซึ่งสามารถเพิ่ม componentDidCatch Lifecycle ตัวใหม่ใน React 16 เข้าไปใน App.js เพื่อดักจับความผิดพลาดใด ๆ ตลอดทั้ง 7 ชั่วโคตร

JSX
1import App from 'next/app'
2
3export default class MyApp extends App {
4 componentDidCatch(error, errorInfo) {
5 console.log('CUSTOM ERROR HANDLING', error)
6 super.componentDidCatch(error, errorInfo)
7 }
8}

Next.js 6.0 นั้นยังมาพร้อมกับการอัพเดทอื่น ๆ อีก เพื่อน ๆ ที่สนใจสามารถอ่านเพิ่มเติมได้จาก ลิงก์นี้ครับ

อาลีบาบา

Babel Coder มีคอร์สสอนสด React ด้วยนะ! 9 - 10 มิย 2561

ลดสูงสุดเหลือ 4,500 บาทต่อท่าน

ครอบคลุมเนื้อหาทั้ง ES6+, React, Redux, React Router และอื่น ๆ

รายละเอียดเพิ่มเติม: คลิกดูที่นี่ฮะ

สนใจติดต่อเพจ BabelCoder ครับ

React Fundamentals

เอกสารอ้างอิง

Add build manifest. Retrieved April, 30, 2018, from https://github.com/zeit/next.js/pull/4119

Improved stacktraces (minor). Retrieved April, 30, 2018, from https://github.com/zeit/next.js/pull/4156

สารบัญ

สารบัญ

  • ความเร็วในการพัฒนาที่เพิ่มขึ้น
  • สามารถเข้าถึง build manifest ได้แล้ว
  • Custom คอมโพแนนท์ App ได้อย่างที่ใจต้องการ
  • อาลีบาบา
  • เอกสารอ้างอิง