ใช้ Vue.js แล้วชีวิตสดใส เลยมาบอกต่อ :3

Vue.js

ที่มาของ Blog นี้ ก็คือ มีงานที่กำลังทำอยู่ + อยากลองพวก Library ที่ช่วยงานพวก UI แบบจริงจังซักที ตอนแรกก็ว่าจะใช้ Angular แต่ลองอ่านๆ ดูแล้วพบว่า มันเยอะเกินกว่าที่อยากใช้มากไป ประกอบกับว่าใช้ Laravel อยู่ ซึ่งก็ดันเจ้า Vue.js มากถึงขนาดยัดมากับ Framework หลักเลย หวยจึงมาตกที่ Vue.js เนาะ (ออกเสียงว่า “วิว” — View)


ขอขยายความอีกซักเล็กน้อยก่อนนะครับ ว่าลักษณะการทำ UI ในส่วนของ View layer ที่ผมทำตามปกติก่อนนะครับ ตรงนี้ขอย้อนเวลากลับไปซักราวๆ 10 ปีที่แล้ว ในยุคที่ AJAX เริ่มแสดงความสามารถออกมาชนิดที่ว่า ใครไม่ใช้นี่เป็นบาป ในยุคนั้น การเอาข้อมูลที่ได้มาจาก Server แล้วมา Update บน View นั้นมันชั่งยากเย็นมากๆ

document.getElementById("ele").innerHTML("Lorem ipsum");

ภาพเก่าๆ มันกลับมาเยือน ผมเชื่อเหลือเกินว่า ใครที่ทันคำสั่งนี้ น่าจะมีอายุใกล้หรือเกิน 30 ปีกันหมดแล้ว :3

ดั่งที่ใครๆ เขาก็พูดกันครับ ในโลกของ Programming ไม่มีความลำบากใดที่ยั่งยืน จึงเป็นที่มาของการกำเนิดพระเอกคนสำคัญ ขึ้นมา 1 คนครับ

ภาพประกอบนี้ไม่เกี่ยวกับเนื้อหานะครับ

ซึ่งพระเอกในตอนนั้นก็ไม่ใช่ใครอื่น นั่นคือ jQuery ที่เราๆ ท่านๆ น่าจะคุ้นเคยกันดีนี่แหละครับ โดยเจ้า jQuery เนี่ยมันช่วยทำให้คำสั่งยาวๆ ด้านบน เหลือเพียง

$("#ele").html("Lorem ipsum");

หลายคอาจจะบอกว่า เอ๊ะ มันก็แค่เขียนสั้นลงนิดเดียวเอง ก็จริงครับ ว่าสั้นลงนิดเดียว แต่ลึกๆแล้ว $() มันยังมีความดีงามอีกมากมาย ชนิดที่เรียกว่าเขียนบอกกัน 3 หน้า 4 หน้าก็ไม่หมด ฉะนั้นจึงขอละไว้ในฐานที่เข้าใจนะครับ เพราะว่าไม่ใช่ประเด็นหลัก

แน่นอนครับว่าพระเอก jQuery จะมีความสามารถเพียงเท่านี้ได้อย่างไร คือระดับพระเอกแล้วเนี่ย สกิลอัลติมันจะต้องเทพ ซึ่งคำสั่ง $.ajax() อันนี้แหละที่ถือได้ว่าเป็นทีเด็ดเลย

$.ajax({
url: "test.html",
method: "GET",
success: function(data) {
...
}
});

เพราะด้วยคำสั่งผีบอกนี้ บอกลาการเขียน httpRequest แบบเต็มๆ ไปเลยครับ คือ ตอนนั้นนี่แบบ น้ำตาไหลไม่รู้ตัวเลย อารมณ์แบบว่า จากต้อง Code 10 หน้า ก็จะลดลงไปเหลือซัก 6 หน้าได้

หลังจากที่เจ้า jQuery กดสกิลอัลติครั้งที่ 1 ก็ยังจัดคอมโบตามมาอีกรัวๆ ไม่ว่าจะเป็นเรื่อง Event Handling หรือแม้แต่การออกลูกออกหลาน (jQuery UI) พูดได้เลยครับว่า การมาของ jQuery และผองเพื่อน ได้เข้ามาพลิกหน้าประวัติศาสตร์การทำ UI ไปมากทีเดียว

แต่ความอยากสบายของ Programmer มันไม่ได้หยุดอยู่แค่นั้นครับ เพราะถึงแม้ jQuery จะเทพยังไง มันก็ยังมีอีกหลายอย่างที่ทำไม่ได้ง่ายๆ เช่น การทำ data binding หรือการทำ UI Template เป็นต้น จึงเกิด Library ใหม่ๆ ขึ้นมาอีกมากมาย ที่ดังๆ ในตอนนี้ ก็เป็นพวก Angular, React ,ionic เป็นต้น


กลับมาที่ปัจจุบันครับ

ขอย้ำอีกทีนะครับว่า ที่ผมเลือกใช้ Vue.js นั้นก็มีเหตุผลสำคัญ คือใช้ง่าย และเรียนรู้ได้ไวมากครับ สิ่งที่ Vue.js เข้ามาช่วยงานผมมากๆ จะเป็นเรื่องการทำ data binding และการทำ UI Template ซึ่ง 2 อย่างนี้เป็นสิ่งจำเป็นมากๆ ในการทำ Web แบบ One Page App นะครับ

Data Binding คืออะไร

Data binding คือการผูกข้อมูลในฝั่งของ Code JavaScript เบื้องหลัง เข้ากับ View ที่ Render แล้วเข้าด้วยกัน นั่นคือเมื่อมีการเปลี่ยนแปลงข้อมูลที่ view ก็จะมีการเปลี่ยนแปลงของข้อมูลที่ Code JavaScript เบื้องหลัง และในทำนองเดียวกัน เมื่อมีการเปลี่ยนแปลงข้อมูลที่ Code JavaScript เบื้องหลัง ก็จะมีการเปลี่ยนแปลงของข้อมูลที่ view ด้วย งงมั้ย? ก็น่าจะงงนะครับ งั้นยกตัวอย่างเลยละกัน

// HTML
<input type="text" id="input1">
// JavaScript
let foo = "foo"
$("#input1").val(foo)

ลองเล่นที่นี่ https://jsfiddle.net/puuga/m6xh2cpv/

จาก Code ที่ยกตัวอย่างไว้ ทุกท่านน่าจะจินตนาการออกว่าตอนที่ browser render ออกมาให้ จะมี Text box 1 อัน และมีข้อความว่า “foo” อยู่ในนั้น แล้วถ้าเราอยากให้ foo เก็บค่าใหม่จาก input ล่ะ

// HTML
<input type="text" id="input1">
// JavaScript
let foo = "foo"
$("#input1").val(foo)
$("#input1").on('input', () => {
foo = $("#input1").val()
console.log(foo)
})

ลองเล่นที่นี่ https://jsfiddle.net/puuga/922uxssd/1/

จะเห็นว่านี่แค่ผูกข้อมูลกันแบบง่ายๆ ยังต้องมี Code มาคุมมากมาย แล้วลองจินตนาการถึง Web อย่าง Facebook ว่าจะต้องมี Code มาคุมมากขนาดไหนนะครับ

ทั้งนี้ Data binding จะแบ่งออกเป็นได้เป็น 2 แบบใหญ่ๆ คือ 1-Way binding กับ 2-Way binding ในส่วนของ Code ตัวอย่างในบทความนี้จะเป็นแบบ 1-Way binding นะครับ (เพราะ Vue.js เป็น 1-Way binding) ส่วน 2-Way binding นั้นให้ลองนึกภาพว่าเมื่อค่าของ foo เปลี่ยนไป มันก็จะต้องเอาค่าไปแสดงใน input ได้เลยครับ

จะ Single Page App (SPA) ไปทำไม

คำตอบน่าจะมีในใจกันอยู่แล้วนะครับ ว่า ถ้า facebook ต้อง refresh หน้าใหม่ทุกครั้ง ที่มีอะไรเปลี่ยนแปลงละก็… เข้าใจตรงกันนะ :3

หลักๆ แล้วที่ทำ Single page application กัน ก็จะเป็นเหตุผลใหญ่ๆ ดังนี้

  1. ความลื่นไหลต่อเนื่องของการแสดงผล
  2. ลดโหลดของ Server
  3. เท่

แต่เอาเข้าจริงแล้ว เว็บใหญ่ๆ หลายๆ เว็บ ก็ไม่ได้เป็น Single page application แบบสมบูรณ์นะครับ ก็ยังคงใช้แบบผสมผสานกันไปตามรูปแบบ หรือสถานการณ์ เช่น อะไรที่เป็นการใช้งานที่ต่อเนื่องกันจริงก็จะออกแบบให้เป็น Single page application เพื่อให้การใช้งานลื่นไหลมากที่สุด


Vue.js ไม่ยาก แต่ก็ไม่ง่ายนะ

จั๋วหัวมาแบบนี่หลายคนน่าจะงง ว่าตกลงแล้ว Vue.js มันยากง่ายขนาดไหนกันแน่(ฟร่ะ) คือแบบนี้ครับ เราสามารถใช้ Vue.js ได้ในแบบที่ไม่อะไรกับชีวิตเรามากนักครับ ประมาณว่า ใครที่เข้าใจ HTML, CSS และ JavaScript มาแล้ว ก็ใช้ได้ทันที แต่ตัวมันเองก็มีความสามารถแบบ Advance ให้ใช้ได้ด้วย

ด้วยความที่ Vue.js นั้น ถูกออกแบบมาให้นำไปใช้ได้ง่ายมาก ความสามารถหลักๆ จะเกี่ยวกับการใช้สร้าง View โดยเฉพาะ สามารถเอาไปรวมกับโปรเจ็คที่มีอยู่แล้ว แต่ถ้าอยากใช้แบบ Advance ก็แค่เสริม Support Library เข้าไปครับ

ใช้ Vue.js แสดงผล List และวาง Condition เล็กน้อย

นี่คือสิ่งที่ผมต้องการจาก Vue.js ครับ สถานการณ์ คือ มี Service แล้ว ฝั่งหน้าเว็บ ใช้ jQuery เรียกข้อมูลจาก Service แล้ว และจะใช้ Vue.js มาช่วยแสดงผล List และวาง Condition ลงไปเล็กน้อยครับ

Getting Start

เรามาเริ่มต้นกันที่การใช้งาน Vue.js ก่อนนะครับ ก็ทำตามธรรมชาติของการใช้งาน JavaScript Library นะครับ ด้วยคำสั่งง่ายๆ

<script src="https://unpkg.com/vue/dist/vue.js"></script>

วางไว้ใน Head จะดีที่สุดครับ


Declarative-Rendering

วิธีการใช้งาน Vue.js ให้แสดงข้อมูลไปที่ตำแหน่งที่ต้องการ จะทำผ่าน Template นะครับ ทำได้ในลักษณะนี้

// HTML
<div id="app">
{{ message }}
</div>
// JS
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})

ผลที่ได้คือ ใน Tag div#app จะถูกผูกเข้ากับ Vue instant ที่ประกาศไว้นะครับ สิ่งที่แสดงออกมาจะเป็นดังนี้

ผลลัพท์ app

มาถึงขั้นนี้ ถ้าใครได้ทำตาม อยากให้ลองเปิด Web Console แล้วลองพิมพ์ไปว่า

app.message = 'some new message'

แล้วจะเห็นความมหัศจรรย์ครับ :3

นี่แหละครับ ง่ายนิดเดียวเอง

ในกรณีที่ไม่อยากใช้ Tag ของ HTML มาทำแบบนี้ ใช้ Tag อื่นแทนได้มั้ย? ได้ครับ ให้ใช้ <template></template> แทนได้ครับ แบบนี้ครับ

<template id="app">
<div>
{{ message }}
</div>
</template>

เดี๋ยวในเรื่องต่อๆไป จะทำให้เห็นชัดขึ้นนะครับว่าใช้ <template> แล้วมันดียังไง


Conditional-Rendering

ตัว Vue.js นั้นอนุญาติให้เราวางเงื่อนไขแบบง่ายๆ ลงไปได้ (เงื่อนไขแบบซับซ้อนก็ได้) ประมาณนี้ครับ

// HTML
<div id="app-3">
<p v-if="seen">Now you see me</p>
</div>
// JS
var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})

ผลที่ได้คือ

ผลลัพท์ app3

สังเกตุตรง v-if (v มาจาก Vue.js) ตรงนี้ตะคล้ายๆ กับของ Angular ครับ (ใช้ ng-) สิ่งที่เกิดขึ้นคือ เราวางเงื่อนไขไว้ที่ <p> ถ้าเงื่อนไขนี้เป็นจริง <p> จะแสดง แต่ถ้าเป็นเท็จ <p> จะหายไปยกก้อนเลย สิ่งที่ Web จะใช้ Render จะเป็นแบบนี้ครับ

// true
<div id="app-3">
<p v-if="seen">Now you see me</p>
</div>
// false
<div id="app-3">

</div>

เอาละสิ จุดนี้น่าจะมีหลายคนที่สับสนว่า อะไรจะแสดง อะไรจะหายไป <p> จะยังอยู่ แต่ ข้างในหายไป หรือ <p> หายไปยกก้อน ???

สำหรับใครที่มีปัญหาสับสนจุดนี้นะครับ แนะนำให้ใช้ <template> มาช่วยครับ แบบนี้

<div id="app-3">
<template v-if="seen">
<p>Now you see me</p>
</template>
</div>

ถ้าใช้แบบนี้ เราจะไม่ต้องมากังวลว่า <template> จะหายหรือไม่หาย เพราะมันไม่มีผลอะไรกับการ Render อยู่แล้วครับ

ต่อมาครับ เรามาพิจารณา v-if กันก่อน ในตัวอย่างที่ผ่านมา เราวาง condition โดยเอาไปผูกกับสิ่งที่เราประกาศไว้ใน Vue instant นะครับ นอกจากวิธีที่ว่ามาเราสามารถวาง Condition ลงไปตรงๆ ได้ด้วยครับ ในลักษณะนี้ครับ

// HTML
<div id="app-4">
<template v-if="type === 'student'">
<p>I am a student.</p>
</template>
</div>
// JS
var app4 = new Vue({
el: '#app-4',
data: {
type: 'student'
}
})

Loop Rendering

ในหัวข้อนี้จะใช้งาน Loop ครับ อันนี้เข้าใจง่ายมากๆ

// HTML
<div id="app-5">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
// JS
var app5 = new Vue({
el: '#app-5',
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
})

ผลที่ได้คือ

ผลลัพท์ app5

ถ้าใครได้ลองทำตามครับ อยากให้ลองซนอีกนิด เปิด Web Console ขึ้นมาครับ ละพิมพ์ไปว่า

app5.todos.push({ text: 'New item na ja' })

และเช่นเดิมครับ ใครไม่ชอบเอา v-for ไปแปะกับ <li> เราสามารถใช้ <template> มาช่วยได้ครับ แบบนี้

<div id="app-5">
<ol>
<template v-for="todo in todos">
<li>
{{ todo.text }}
</li>
</template>
</ol>
</div>

Loop and Condition Rendering

ณ จุดๆนี้ เราจะมาทำการ Mix & Match ระหว่างการทำ Loop Rendering กับ Condition Rendering เข้าด้วยกันนะครับ เรามาเริ่มที่ตัวข้อมูลที่จะใช้แสดงผลกันก่อนนะครับ

// users
[
{
name: "a",
type: "student",
picture: "a.png"
},
{
name: "b",
type: "teacher",
picture: "b.png"
},
{
name: "c",
type: "student",
picture: "c.png"
}
]

โดยที่ เราต้องการแสดงผลเป็นแบบ List ซึ่งถ้าตัว User เป็น Student อยากให้เป็นคนละสีกับ Teacher นะครับ มาเริ่มกันเลย

// HTML
<div id="app-6">
<ol>
<template v-for="user in users">
<template v-if="user.type === 'student'">
<li style="background: blue;">
{{ user.name }}
</li>
</template>
<template v-else-if="user.type === 'teacher'">
<li style="background: red;">
{{ user.name }}
</li>
</template>
</template>
</ol>
</div>
// JS
var app6 = new Vue({
el: '#app-6',
data: {
users: [...]
}
})

เรียบร้อยครับ ก็จะได้หน้าตาแบบนี้

ผลลัพท์ app6

ขอเปลี่ยนนิดหน่อยนะครับ ไม่อยากใช้ <ol> ละ อยากให้เอา index ของ users มาใช้แทน

<div id="app-6">
<template v-for="(user, index) in users">
<template v-if="user.type === 'student'">
<p style="background: blue;">
{{ index }}. {{ user.name }}
</p>
</template>
<template v-else-if="user.type === 'teacher'">
<p style="background: red;">
{{ index }}. {{ user.name }}
</p>
</template>
</template>
</div>

ก็จะได้ผลลัพท์แบบนี้นะครับ

ผลลัพท์ app6 update1

แล้วถ้า อยากให้แสดงรูปไปด้วยล่ะ ไม่น่าจะทำแบบเดิมได้ เพราะว่าเวลาใช้ <img> เราจะต้องชี้รูปผ่าน Attribute src แบบนี้

<img scr="x.png">

ไม่ต้องกังวลครับ เพราะ Vue.js เตรียมมาให้แล้ว นั้นก็คือ v-bind: ครับ

v-bind:

ใช้งานแบบนี้ครับ

<img v-bind:src="data.picture_url">

หรือจะใช้งานแบบคำสั่งสั้นๆก็ได้นะครับ แบบนี้

<img :src="data.picture_url">

เอาไปประกอบร่างก็จะได้เป็น

<div id="app-6">
<template v-for="(user, index) in users">
<template v-if="user.type === 'student'">
<p style="background: blue;">
{{ index }}. {{ user.name }} <img v-bind:src="data.picture">
</p>
</template>
<template v-else-if="user.type === 'teacher'">
<p style="background: red;">
{{ index }}. {{ user.name }} <img v-bind:src="data.picture">
</p>
</template>
</template>
</div>

ก็จะได้ผลออกมาประมาณนี้นะครับ

ผลลัพท์ app6 update2 (รูปไม่ขึ้นนี่ถูกแล้วนะ)

ทีนี้เวลาเราเอาไปใช้จริง ก็อาจจะมีการเพิ่ม หรือลดข้อมูลจาก Users นะครับ ก็ใช้พวกคำสั่งที่เอาไว้จัดการ Array ดังต่อไปนี้

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

แล้วเดี๋ยว Vue.js จะจัดการที่เหลือให้เอง ง่ายมะ :3

จริงๆ แล้ว Vue.js ยังมีอีกหลายอย่างให้เล่น ซึ่งดีมากและง่ายมาก สามารถอ่านและลองทำได้ง่ายๆ เลย จะลองเล่นใน https://jsfiddle.net/ ก็ได้ครับ

ก็จะขอจบเนื้อหาของ Blog นี้ไว้ก่อนนะครับ สวัสดีครับ


อ้างอิง