ทำความรู้จักกับ Reactive ใน Vue.js และสิ่งที่มักจะพลาดกัน

Nitipat Lowichakornthikun
I GEAR GEEK
Published in
3 min readJun 7, 2019

จากเมื่อก่อนทีมเราเองนั้นได้เขียน 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 กันครับ ดังนั้นถ้ามีบทความใหม่ ๆ ที่เรามองว่าน่าสนใจอยากแบ่งปันให้ทุกคนเกี่ยวกับเรื่องที่พวกเราได้เรียนรู้นี้ พวกเราจะมาแชร์กันอีกน่ะครับ เจอกันบทความหน้าครับ :)

--

--