ทำความรู้จักกับ Reactive ใน Vue.js และสิ่งที่มักจะพลาดกัน
จากเมื่อก่อนทีมเราเองนั้นได้เขียน React.js กันมาโดยตลอด (แต่เมื่อ Apple ก็ยังใช้ Vue.js ฮ่า ๆ อันนี้หยอกครับ)
เมื่อทีมพวกเราได้มาจับ Vue.js เรามักจะพบปัญหาบางอย่างที่มันแปลกมาก ๆ ดังตัวอย่างด้านล่างนี้ครับ
เราจะพบว่าสามารถเล่นกันกับข้อมูลได้อย่างง่ายดาย ลองทดสอบโดยการกดที่ปุ่ม add
จะพบว่าเราสามารถเพิ่มชื่อของคนลงไปใน Array ได้ง่าย ๆ
ทีนี้ให้ลองกด change name
เราจะพบว่าความตั้งใจคือการไปเปลี่ยนชื่อของคนที่อยู่ใน Array ที่ index : 1 หรือนั่นก็คือสมาชิกลำดับที่ 2 เป็นคำว่า Test
แต่เอ๊ะ!
มันเกิดอะไรขึ้น?
ค่าที่มาแสดงทำไมมันไม่เปลี่ยน?
Logic ที่เราเขียนมันผิด?
ทำไมมันไม่สามารถทำงานได้อย่างถูกต้อง?
ปัญหานี้มันเกิดจากการที่เรายังไม่ได้เข้าใจหลักการการทำงานของ Reactive นั่นเอง
ต่อไปนี้พวกเราจะลงลึกไปอีกเพื่อเข้าใจกับปัญหานี้ และ ทำความรู้จักกันกับ Reactive มากยิ่งขึ้นครับ ว่าทำไมมันถึงเกิดปัญหานี้ขึ้นมาได้
เปรียบเทียบจุดสำคัญที่แตกต่างกันของ Vue.js กับ React.js
สิ่งที่แตกต่างที่สำคัญคือการเปลี่ยนค่าของ State ต่าง ๆ ครับ (แต่ถ้าหากคุณเคยเขียน Angular มาก่อนจะพบว่า Vue.js มีความคล้ายกันกับ Angular)
- React.js ใช้หลักการ
setState
กล่าวคือ มันไม่สนใจหรอกว่าคุณจะเปลี่ยนแปลงค่าอะไร มันแค่ทำหน้าที่ว่าถ้าคุณเรียกการใช้งานsetState
แล้วโยนค่าใหม่เข้ามา มันก็จะเปรียบเทียบค่าเดิม จากนั้นถ้ามีการแก้ไขก็จะไปอัพเดทค่าที่แสดงใน Template
จากโค๊ดด้านบนเราจะเห็นว่าเราสามารถเปลี่ยนค่าตัวแปร count
ได้ก็ต่อเมื่อมีการโยนค่าใหม่ทั้งก้อนของ state เข้า setState
ครับ
- Vue.js ใช้หลักการของ Reactivity เมื่อเราทำการเปลี่ยนแปลงข้อมูลโดยตรงกับ data นั้น ๆ มันจะคอยสอดส่องดูว่ามีการแก้ไขไหม ถ้ามีก็จะไปแก้ไขค่าที่แสดงใน Template อีกทีนึง
จากโค๊ดด้านบนจะเห็นว่าเราสามารถเปลี่ยนค่าตัวแปร count
กันได้ตรง ๆ เลยครับ
จริง ๆ แล้ว มันมีจุดที่แตกต่างหลายที่ครับ และ หัวข้อนี้ผมก็ได้ใช้ตัวอย่างบางส่วนที่เอามาจากลิงค์ด้านล่างครับ แนะนำว่าให้ลองดูในหัวข้อไว้ด้วยครับ
การทำงานของ Reactive ในโลกของ Vue.js
ลองดูการประกาศตัวแปรใน Vue.js กันครับ ซึ่งเราต้องประกาศใน data
var vm = new Vue({
data: {
msg: "test"
}
})console.log(vm.$data)
ผลที่ออกมาเราจะได้แบบนี้
เราจะพบว่ามีฟังก์ชั่น get msg, set msg มาด้วย ซึ่งนี้เป็นการใช้ความสามารถของ getter / setter (IE8 ลงไปไม่ลองรับความสามารถนี้) ทำให้ตัว Vue เองสามารถใส่ reactiveGetter / reactiveGetter ของตัวเองมาครอบเองไว้ได้ก่อนไปถึงค่าของมันจริง ๆ นั่นเอง
ตัวอย่างการใช้งาน getter / setter
var o = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2;
}
};
console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
แล้วประโยชน์ของมันหรอครับ ก็คือการที่เราสามารถดักจับได้ตลอดว่าแต่ละตัวแปรที่อยู่ของเรามีการเปลี่ยนแปลง หรือ มีการเรียกใช้งานไหม
จากตัวอย่างจะเห็นวงกลมสีม่วง ที่ทำหน้าที่ในการ trigger ไปหา watcher ของ Vue ว่าเกิดการเปลี่ยนแปลง หรือ เรียกใช้งานอะไรบ้างในตัวแปรที่ประกาศไว้
ดังนั้นถ้าเราต้องการให้ Vue มันรู้โดยใช้ความสามารถ Reactive ก็ต้องประกาศไว้ใน data เท่านั้นน่ะครับ
สำหรับการแก้ไข Nested Object เช่น someObject.b ที่เราต้องการเปลี่ยนค่า b เป็น 2 แต่ Vue watcher ก็จะไม่สามารถทำงานได้ ทำให้ข้อมูลที่แสดงไม่เปลี่ยนไปตามค่าใหม่ ดังนั้นจึงมีคำสั่ง Vue.set ขึ้นมาเพื่อตั้งค่าเข้าไปตรง ๆ เลย
Vue.set(vm.someObject, 'b', 2)
เราสามารถระบุ object ที่ต้องการแก้ไข, object ที่ซ้อนลงไป และ ค่าที่ต้องการเปลี่ยนแปลง และ อีกท่าที่เราชาว React.js ก็คือการสร้าง object ทั้งก่อนใหม่ขึ้นมาเลย แล้วใส่กลับไปที่ตัวเดิมที่เราต้องการแก้ไข
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
นี่แหละครับคือสาเหตุที่เราไม่สามารถแก้ไขค่าได้ตรง ๆ ใน Nested object ทีนี้หลายคนก็สงสัยว่า เอ๊ะ ปกติ เราก็ใช้พวก pop, push, splice ใน Array ได้นี่ และ Vue.js ก็สามารถรู้ได้ว่ามันมีการเปลี่ยนค่านั้น
ใช่แล้วครับ เพราะ Vue.js ได้ทำการครอบ method พวกนี้ไว้ ทำให้ดักจับการแก้ไขได้
method ที่ครอบไว้มีดังนี้
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
สรุป
เราจะไม่สามารถเขียนค่าลงไปตรง ๆ ใน Netsted Object ได้ รวมทั้งการ access ผ่าน index ตรง ๆ ของ Array
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // is NOT reactive
เราต้องเปลี่ยนแปลงค่าผ่าน vue.set
ดังนี้เท่านั้น ถึงจะทำให้ข้อมูลที่เราแก้ไข เข้าสู่การทำงานแบบ Reactive
Vue.set(vm.items, indexOfItem, newValue)
อ้างอิง
แนะนำให้อ่านบทความอื่น ๆ ได้จากลิงค์ด้านล่างได้เลยครับ
หากเนื้อหาตรงไหนในบทความนี้ ไม่ครบถ้วนหรือไม่สมบูรณ์ สามารถ comment ไว้ได้เลยครับ จะได้ปรับเนื้อหาให้ถูกต้อง และ ครบถ้วนครับ
อย่างที่บอกไปตอนแรกครับ พวกเรา I GEAR GEEK ก็กำลังเขียน / ศึกษาภาษา React.js และ Vue.js กันครับ ดังนั้นถ้ามีบทความใหม่ ๆ ที่เรามองว่าน่าสนใจอยากแบ่งปันให้ทุกคนเกี่ยวกับเรื่องที่พวกเราได้เรียนรู้นี้ พวกเราจะมาแชร์กันอีกน่ะครับ เจอกันบทความหน้าครับ :)