[Tutorial] เซ็ตอัพ Webpack และ React ตั้งแต่เริ่มต้นจน Deploy

aofleejay
THiNKNET Engineering
6 min readApr 16, 2018

--

เมื่อไม่นานมานี้มีโอกาสพาน้องๆเซ็ตอัพโปรเจค React กันตั้งแต่เริ่มต้น จน deploy ขึ้น Netlify ก็เลยถือโอกาสนี้เขียนบล็อกเพื่อเก็บสิ่งที่ได้แบ่งบันเอาไว้

ทำความรู้จักกับ Webpack กันก่อน

Webpack เป็น module bundler ครับ แปลง่ายๆ คือ เครื่องมือที่ใช้ในการรวม module ที่เราเขียน ให้เป็นไฟล์ที่เราจะนำไปใช้งานจริงๆ ความสามารถหลักๆของมันมีดังนี้ครับ

  • รวม module หลายๆ module ให้กลายเป็นไฟล์ๆเดียว (หรือมากกว่าก็ได้)
  • แปลง ES6, JSX, CSS เป็นต้น ให้สามารถใช้งานบน browser ทั่วไปได้

ไม่รอช้า เรามาเริ่มลงมือกันเลย

ติดตั้ง Webpack กันก่อนนะ

yarn add webpack webpack-cli --dev

ในไฟล์ package.json เพิ่มสคริปสำหรับ build โค้ดลงไปครับ

"scripts": {
"build": "webpack"
}

Webpack 4 เราสามารถ bundle ไฟล์ได้โดยที่ไม่ต้องมี config file เลย ดังนั้นเราจะลอง build กันดูครับ สั่ง yarn build ได้เลย

อาว error ซะงั้น อ่อๆ ยังไม่มีโค้ดนั่นเอง

ถ้าเจอ error ก็ไม่ต้องตกใจครับ เพราะ ค่าเริ่มต้นของ webpack จะ bundle ไฟล์จาก src/index.js ซึ่งเรายังไม่มีไฟล์นี้เลย ดังนั้นให้สร้างไฟล์ src/index.js ขึ้นมาก่อนเลย

คราวนี้ลองสั่ง yarn build อีกรอบ Webpack จะ bundle ไฟล์จาก src/index.js ไปที่ dist/main.js ครับ

เมื่อสั่ง yarn build แล้ว โค้ดทั้งหมดจะถูกรวมไว้ที่ dist/main.js

ถ้าหาก src/index.js มีการเรียก module อื่น เช่น โค้ดจากไฟล์อื่นๆ หรือ third party library โค้ดทั้งหมดก็จะถูกนำไปรวมไว้ที่ dist/main.js เหมือนกันครับ

เพิ่ม Config ด้วย webpack.config.js

ใน Webpack เราสามารถเพิ่มเงื่อนไขในการ bundle ไฟล์ได้ โดยการสร้างไฟล์ webpack.config.js ครับ

เรามาลองกำหนดไฟล์ต้นทางและไฟล์ปลายทางกัน โดยเพิ่มโค้ดข้างล่างลงไปครับ

const { resolve } = require('path')module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist'),
},
}

อย่าลืมลง path กันด้วยนะครับ

yarn add path --dev

คราวนี้ถ้าสั่ง build อีกรอบ จะได้ไฟล์ปลายทางอยู่ที่ dist/bundle.js นะครับ

ใช้งาน Webpack ร่วมกับ React

คราวนี้เราจะลองสร้างโปรเจค React แล้ว build ให้อยู่ในไฟล์ dist/bundle.js เพื่อเอาไปใช้ต่อกันครับ เริ่มต้นโดยการสร้างโปรเจคง่ายๆด้วย React กันก่อนเลย

ติดตั้ง React กันก่อน โดยลงทั้ง react และ react-dom

yarn add react react-dom

เพิ่มโค้ดใน src/index.js ซึ่งเป็น entry point ของเว็บเราครับ

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

แล้วก็สร้างไฟล์ src/App.js เพื่อสร้าง component App ขึ้นมาครับ

import React from 'react'const App = () => <h1>Webpack React Setup.</h1>export default App

ใครจะสร้างเยอะกว่านี้ก็ได้นะครับ แต่แค่นี้ก็เพียงพอสำหรับ tutorial นี้แล้วครับ

เมื่อได้โปรเจคแล้ว ลองสั่ง build ดูหน่อยครับ ว่ายังใช้ได้ไหม

เจอ syntax error ซะอย่างนั้น

ถ้า error ก็ไม่ต้องตกใจอีกเช่นกัน ทาง Webpack ได้ใบ้ๆ ให้เราแล้วดังนี้ครับ “You may need an appropriate loader to handle this file type.” คือเราต้องการ loader เพิ่มเติม เพื่อแปลง syntax บางอย่าง เช่น ES6, JSX ครับ

รู้จักกับ Webpack loader

หน้าที่ของ loader จะเอาไว้แปลงโค้ดที่เขียนด้วย syntax ต่างๆ เช่น ES6, JSX หรือแม้แต่ CSS, SASS ให้ใช้งานได้

ซึ่ง error ข้างต้น เกิดเพราะใช้ import ซึ่งเป็น syntax ของ ES6 และใช้ <App /> ซึ่งเป็น syntax ของ JSX ดังนั้นเราจะลง loader เพิ่มดังนี้

yarn add babel-core babel-loader babel-preset-env babel-preset-react --dev

และเพิ่ม config ของ loader ใน webpack.config.js ครับ

module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['babel-preset-env', 'babel-preset-react'],
},
},
},
],
},

}

อธิบายการทำงานคร่าวๆ ดังนี้ครับ

  • test: /\.js$/ — เราต้องกำหนดก่อนว่าจะให้ไฟล์ไหนใช้ loader ได้บ้าง โดยกำหนดเป็น regex ครับ
  • exclude: /(node_modules)/ — เราสามารถเลือก exclude ไฟล์ที่ไม่ต้องการแปลงได้ครับ (กลับกันสามารถเลือกเฉพาะไฟล์ที่เราอยากแปลงได้โดยเปลี่ยน key จาก exclude เป็น include)
  • loader: 'babel-loader' — เป็นชื่อของ loader ที่เราจะใช้ ในที่นี่คือ babel-loader เอาไว้ transpile javascript ครับ
  • options — เราสามารถเพิ่ม options ให้กับแต่ละ loader ได้ จากตัวอย่างเป็นการเลือก presets ในการแปลงครับ ซึ่ง babel-preset-env เอาไว้แปลง ES6 และ babel-preset-react ไว้แปลง JSX ครับ

ลองสั่ง build คราวนี้จะไม่มี error แล้วครับ คราวนี้เราสามารถสร้างไฟล์ HTML เพื่อเรียก dist/bundle.js ไปใช้งานได้แล้ว

สร้างไฟล์ HTML ที่เรียกใช้ React

จริงๆแล้วเราจะเข้าไปสร้างไฟล์ index.html ภายใต้โฟล์เดอร์ dist เลยก็ได้ แต่มันดูแมนนวลไปหน่อยน่ะ เรามาลองให้ webpack สร้างไฟล์ HTML ขึ้นมาทุกครั้งที่เรา build ดีกว่า

เราจะใช้ Webpack plugin ชื่อ html-webpack-plugin ในการสร้างไฟล์ index.html กัน โดยทุกครั้งที่ build จะได้ไฟล์ dist/index.html ขึ้นมาครับ

yarn add html-webpack-plugin --dev

แล้วเพิ่มโค้ดใน webpack.config.js ดังนี้

const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
],

}

จากบรรทัดที่ว่า template: ‘src/index.html’ คือเราสามารถสร้างโครง html ไว้ที่ src/index.html เพื่อจะเป็นโครงที่ใช้ในการ build ลง dist/index.html ครับ ลงมือสร้างกันได้เลย

<!DOCTYPE html>
<html>
<head>
<title>Webpack + React Setup</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

เมื่อสั่ง build เราจะได้ไฟล์ dist/index.html ที่หน้าตาเหมือน src/index.html แต่พิเศษที่มีแท็ก <script src="dist/bundle.js"> ชี้ไปที่ bundle.js ให้เองเลยแบบนี้ครับ

ถ้าเราลองเปิดไฟล์ dist/index.html ก็จะเห็นเว็บ React ของเราแล้ว

Webpack dev server

อีกหนึ่งฟีจเจอร์ที่น่าสนใจ คือการใช้ webpack-dev-server เพื่อจำลองเซิฟเวอร์ในการพัฒนาครับ นอกจากนั้นแล้ว ยังสามารถรีเฟรชหน้าเว็บเมื่อเราแก้โค้ดด้วยนะ

ไม่รอช้าลง package กันเลย

yarn add webpack-dev-server --dev

เพิ่ม script dev ใน package.json

"scripts": { 
"dev": "webpack-dev-server --open",
"build": "webpack"
}

เมื่อเราสั่ง yarn dev ก็จะสามารถเข้าเว็บผ่าน http://localhost:8080/ ได้เลยครับ

Development Mode vs Production Mode

สำหรับใครที่รู้สึกว่าเวลาในการ build มันช่างนานเหลือเกิน ไม่ต้องตกใจนะครับ เพราะค่าเริ่มต้นในการ build จะเป็นการ build bundle สำหรับ production ครับ คือ optimize ทุกอย่าง เพื่อให้ใช้งานบน production ได้ดีขึ้น แลกมาด้วยเวลาที่ build นานขึ้นนั่นเอง

แต่ตอนเราเขียนโค้ดเราอยากให้มัน build เร็วๆใช่ไหมครับ ซึ่ง Webpack เองก็สามารถเลือกได้ด้วยว่าจะใช้ development mode หรือ production mode โดยเข้าไปแก้ script ใน package.json ตามนี้เลย

"scripts": {
"dev": "webpack-dev-server --mode development --open",
"build": "webpack --mode production"
}

แค่นี้ script dev ก็จะ build เร็วขึ้นเยอะแล้วครับ

อย่าลืม!! เปิด webpack-dev-server ใหม่ทุกครั้งที่มีการแก้ script dev หรือแก้ webpack.config.js ด้วยนะครับ ทำได้โดยการกด control+c แล้วรันใหม่นะ

ลองใช้ CSS และ SASS กันหน่อย

สำหรับใครที่อยากเขียน CSS แบบแยกไฟล์ เรามาลองสร้าง src/App.css ดูซิว่ามันจะใช้งานได้เลยไหม โดยเพิ่มโค้ดดังนี้

h1 {
color: salmon;
}

import มาใช้ในไฟล์ src/App.js

import React from 'react'
import './App.css'
const App = () => <h1>Webpack React Setup.</h1>export default App

หลัง import ไฟล์ src/App.css จะเจอ syntax error ที่ไฟล์ src/App.css แน่นอนล่ะครับ ว่าต้องการ loader เพิ่ม ให้เราลองลง loader เพื่อแปลง css กันเลย

yarn add css-loader style-loader --dev

เพิ่ม loader ใน webpack.config.js

module.exports = {
...
module: {
rules: [
{
...
},
{
test: /\.css$/,
exclude: /(node_modules)/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
],
},

],
},
}

ลอง build อีกรอบคราวนี้ก็จะใช้ได้แล้วนะ

และถ้าใครอยากใช้ SASS ให้ลง loader เพิ่มอีกหน่อยครับ

yarn add sass-loader node-sass --dev

แล้วเพิ่ม loader สำหรับไฟล์ scss

module.exports = {
...
module: {
rules: [
{
...
},
{
test: /\.scss$/,
exclude: /(node_modules)/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
},

],
},
}

แค่นี้ก็จะสามารถอ่าน SASS ได้ จากไฟล์ .scss ได้แล้วครับ

ใส่รูปภาพกันสักนิด

สำหรับใครที่ต้องการใส่รูปภาพเราเองก็ต้องการ loader เพิ่มเช่นกันครับ ในกรณีนี้เราใช้ file-loader กันครับ

yarn add file-loader --dev

เพิ่ม loader ในไฟล์ webpack.config.js (จากตัวอย่างเป็น loader ของไฟล์ .png นะครับ ใครอยากได้ไฟล์อื่นด้วยก็ใส่ regex เพิ่มได้เลย)

module.exports = {
...
module: {
rules: [
{
...
},
{
test: /\.png$/,
exclude: /(node_modules)/,
use: [
{ loader: 'file-loader' },
],
},

],
},
}

เพิ่มโค้ด import รูปภาพใน src/App.js

import React from 'react'
import logo from '../assets/images/logo.png'
import './App.css'
const App = () => (
<div>
<img src={logo} />

<h1>Webpack React Setup.</h1>
</div>
)
export default App

แค่นี้ก็สามารถใส่รูปภาพเข้าไปได้แล้วครับผม นอกจากนี้ยังเอาไปปรับใช้กับ font ได้ด้วยนะครับ

Hot Module Replacement

จากเดิมสังเกตว่า webpack-dev-server นั้น จะรีเฟรชเบราเซอร์ทุกครั้งที่เราแก้โค้ด มันก็เหมือนจะดีนะครับ แก้โค้ดปุ๊บเห็นผลปั๊บ แต่ลองนึกภาพว่า หากเรามีการกรอกฟอร์มเยอะๆค้างเอาไว้ แล้วเบราเซอร์เกิดรีเฟรชขึ้นมา เราต้องเสียเวลามากรอกฟอร์มใหม่หมดเลย

เราก็เลยใช้ HotModuleReplacementPlugin ร่วมกับ react-hot-loader เพื่อให้เบราเซอร์อัพเดตโค้ดตาม โดยไม่ต้องรีเฟรชเบราเซอร์ครับ

yarn add react-hot-loader --dev

เพิ่มโค้ดใน webpack.config.js

const webpack = require('webpack')module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['babel-preset-env', 'babel-preset-react'],
plugins: ["react-hot-loader/babel"],
},
},
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
],
devServer: {
hot: true,
},

}

แล้วแก้ src/App.js ครับ

import React from 'react'
import { hot } from 'react-hot-loader'
import logo from './assets/images/logo.png'
import './App.css'
const App = () => (
<div>
<img src={logo} />
<h1>Webpack React Setup.</h1>
</div>
)
export default hot(module)(App)

ลองแก้โค้ดดูจะเห็นว่าผลลัพธ์ในเบราเซอร์เปลี่ยนโดยไม่ได้รีเฟรชเลยครับ

ลบโฟลเดอร์ dist ก่อนจะ build ใหม่อีกรอบ

บางครั้งเวลาเรา build อาจจะต้องลบไฟล์เก่าๆที่ค้างอยู่ในโฟลเดอร์ dist ออกก่อน เราจะมานั่งลบเองเรื่อยๆก็คงจะไม่ไหว เราจะใช้ clean-webpack-plugin ช่วยจัดการครับ

yarn add clean-webpack-plugin --dev

เพิ่ม plugin ใน webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
...
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
],
}

ลองสั่ง yarn build จะเห็นว่าโฟลเดอร์ dist จะถูกลบไปก่อนที่จะมีการสร้างใหม่ขึ้นมาอีกรอบ ถือว่าเคลียร์ไฟล์ที่ไม่ได้ใช้ได้ดีครับ

Deploy ขึ้น Netlify

พอดีไปเจอ Netlify ซึ่งเป็น static hosting ที่น่าสนใจตัวนึงก็เลยหยิบมาลองเล่นดู โดยเจ้านี้ เราสามารถเลือก deploy โปรเจคจาก github, gitlab หรือ bitbucket ก็ได้

ที่สำคัญยังสามารถบอกได้ด้วยว่าจะให้ deploy จาก branch ไหน โฟลเดอร์อะไร จะให้รันอะไรก่อน deploy คือว่าสะดวกมากครับ สุดท้ายแค่ push branch ก็สามารถ deploy ได้เลย

ลองไปใช้กันดูได้ครับ ใช้งานง่ายมาก อธิบายแค่รูปเดียวก็น่าจะเคลียร์แล้วครับ

ลากมาซะยาว หวังว่า tutorial นี้น่าจะช่วยให้เราสามารถเซ็ตอัพโปรเจค React ด้วย Webpack กันได้ง่ายขึ้นนะครับ หากใครมีเทคนิคอะไรเพิ่มเติมก็ทิ้งคอมเม้นไว้ได้เลยนะครับ

สุดท้ายสามารถดูโค้ดทั้งหมดได้ที่นี่นะครับ ขอให้สนุกกับการโค้ดดิ้งนะครับ

--

--

I’m a software engineer passionate about frontend development. I write blogs about software engineering here and also about books, games at https://kunapot.com.