การใช้งาน Watcher ตรวจสอบการเปลี่ยนแปลงข้อมูล ใน VueJS

บทความใหม่ ปีนี้ โดย Ninenik Narkdee
vuejs watch watcher watcheffect

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ vuejs watch watcher watcheffect

ดูแล้ว 503 ครั้ง


เนื้อหาตอนต่อไปนี้ เรามาดูเกี่ยวกับ watcher ซึ่งเป้นสวนสำคัญหนึ่งใน vuejs
ถ้าแปลตามความหมายคร่าวๆ ก็เหมือนผู้ที่คอยเฝ้าดู ดังนั้นเข้าใจอย่างง่าย
watcher ก็คือเครื่องมือที่ช่วยให้เราตรวจจับการเปลี่ยนแปลงของข้อมูล state
แล้วทำงานใดๆ ตามต้องการหรือกำหนดเมื่อเกิดการเปลี่ยนแปลงนั้น
 

การใช้งานฟังก์ชั่น watch()

ใน vuejs เราสามารถใช้งานฟังก์ชั่น watch() เพื่อกำหนดให้ทำงานใดๆ หลังจากข้อมูล state
ที่ต้องการตรวจสอบ มีการเปลี่ยนแปลง  ดูตัวอย่างโค้ดเบื้องต้นประกอบ
 
<script setup>
import { ref, watch } from 'vue'

const message = ref('');

watch(message, (newVal, oldVal) => {
  console.log(`message changed from ${oldVal} to ${newVal}`)
})
</script>

<template>
  <h1>{{ message }}</h1>
  <input v-model="message">
</template>
 
เราใช้ฟังก์ชั่น watch() สำหรับเป็น watcher เพื่อตรวจจับค่า state หรือตัวแปร message
หากมีการเปลี่ยนแปลง ก็ให้ทำงานฟ้งก์ชั่นที่กำหนด โดยสามารถใช้ค่าเดิม และค่าใหม่ เข้าไปจัดการ
ในฟังก์ชั่นได้
 

ตัวอย่างกรณีกำหนดหลายๆ ค่าพร้อมกัน 

<script setup>
import { ref, watch } from 'vue'

const message = ref('');
const name = ref('')
watch([message ,name], ([newMsg, newName],[oldMsg, oldName]) => {
    console.log(`message changed from ${oldMsg} to ${newMsg}`)    
    console.log(`name changed from ${oldName} to ${newName}`)
})
</script>

<template>
  <h1>{{ message }} {{ name }}</h1>
  <input v-model="message" placeholder="Edit message"><br>
  <input v-model="name" placeholder="Enter name">
</template>
 
 
เมื่อกำหนดตัวแปร state หลายๆ ค่าที่จะทำการคอยตรวจจับการเปลี่ยนแปลงค่า เราสามารถส่งเข้า
ไปใน paramter แรกในรูปแบบ array และให้ส่วนของ ค่าที่จะส่งเข้าไปในฟังก์ชั่น ก็จะใช้รูปแบบ
array ที่สัมพันธ์กันของข้อมูลด้วย 
 
ชนิดของข้อมูลที่เราต้องการตรวจสอบสามารถเป็นรูปแบบที่แตกต่างกันได้ เช่น ข้อมูลจาก ref() 
ข้อมูลจาก reactive() ข้อมูลจาก computed(ref()) ข้อมูล reactive object ข้อมูลในรูป
แบบ getter ฟังก์ชั่น หรือข้อมูล array ที่รวมหลายๆ ข้อมูลก็ได้ ดูตัวอย่างด้านล่างประกอบ
 
<script setup>
import { ref, watch, reactive } from 'vue'

const x = ref(0)
const y = ref(0)
const obj = reactive({ count: 0 })

// single ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})

// getter  ฟังก์ชั่น เมื่อ ค่าของ x + y
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)

// array of multiple sources ค่าของ x และค่ของ ฟังก์ชั่น ที่คืนค่า y
// ตัวอย่าง ไม่ส่งค่าข้อมูล เก่าเข้าไปใน callback ฟักง์ชั่น ส่งแค่ค่าใหม่
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

// รูปแบบด้านล่างนี้จะไม่ทำงาน เพราะเป็นการส่งตัวเลข 0 เข้าไปในฟังก์ชั่น watch()
/*watch(obj.count, (count) => {
  console.log(`Count is: ${count}`)
})
*/
// ให้ใช้รูปแบบนี้แทน เป็นแบบ getter function หรือฟังก์ชั่นที่คืนค่าข้อมูลมาอีกที
watch(
  () => obj.count,
  (count) => {
    console.log(`Count is: ${count}`)
  }
)

</script>

<template>
  <h1>{{ x }} {{ y }}</h1>
  <input type="number" v-model="x" placeholder="Enter X"><br>
  <input type="number" v-model="y" placeholder="Enter Y"><br>
  <button @click="obj.count++">{{ obj.count }}</button>
</template>
 
 

การใช้งาน Deep Watchers

ข้อมูลประเภท reactive object ที่กำหนดด้วยฟังก์ชั่น reactive() เมื่อมีการใช้งาน watch() 
จะมีการกำหนดค่าเริ่มต้นให้กับ watcher เป็นแบบ deep watcher นั่นคือให้ตรวจจับ property
ทั้งหมดของ object ถ้ามีการเปลี่ยนแปลง ดูตัวอย่าง
 
<script setup>
import { ref, watch, reactive } from 'vue'

const obj = reactive({ count: 0 })

// แตกต่างจากตัวอย่างด้านบนตรงที่ด้านบน ระบุ property ที่ต้องการได้เลย
// ในขณะที่วิธีนี้ตรวจสอบทั้ง object รวม property ทั้งหมดภายใน
watch(
  obj,
  (newValue, oldValue)  => {
    console.log(`Count is: ${newValue.count}`)
  }
)
</script>

<template>
  <button @click="obj.count++">{{ obj.count }}</button>
</template>
 
จากตัวอย่าง เป็นการตรวจสอบข้อมูล reactive object ในกรณีนี้จะเป็นแบบ deep watcher 
โดยอัตโนมัติ ค่า newValue และ oldValue จะมีค่าเดียวกับ เพราะเป็น object ตัวเดียวกัน 
สามารถส่งเข้าไปแค่ค่าเดียวก็ได้ แต่ถ้าเราต้องการใช้แบบ getter ฟังก์ชั่น และต้องการกำหนด
แบบ deep watcher ให้เราเพิ่ม argument ตัวที่ 3 เป็น { deep: true } ดังนี้เข้าไป
 
<script setup>
import { ref, watch, reactive } from 'vue'

const obj = reactive({ count: 0 })

watch(
  () => obj.count,
  (newValue, oldValue)  => {
    console.log(`Count is: ${newValue}`)
  },
  { deep: true }
)
</script>

<template>
  <button @click="obj.count++">{{ obj.count }}</button>
</template>
 
อย่างไรก็ในตัวอย่างข้างต้น การกำหนด deep เข้าไป จะไม่มีผล เพราะรูปแบบ object ไม่ได้มี
ส่วนที่ซ้อนอยู่ด้านในอีก ตัวอย่าง object ที่มีผลถ้ากำหนด deep เช่น
 
const obj = reactive({ nested: { count: 0 } })

// Watcher with { deep: true }
watch(
  () => obj.nested,
  (newValue, oldValue)  => {
    console.log('Nested object changed')
  },
  { deep: true }
)

// Watcher without { deep: true }
watch(
  () => obj.nested,
  (newValue, oldValue)  => {
    console.log('Nested object changed')
  }
)
 
Watcher ที่ใช้ { deep: true }: จะตรวจจับการเปลี่ยนแปลงทุกระดับภายใน obj.nested
Watcher ที่ไม่ใช้ { deep: true }: จะตรวจจับเฉพาะการเปลี่ยนแปลงที่ระดับแรกของ obj.nested
ถ้า obj.nested.count = 1  // ตัว watcher จะไม่ทำงาน
 
ข้อพึงระวังของการกำหนด deep ก็คือ ไม่ควรใช้กับข้อมูลที่มีโครงสร้างที่ซับซ้อน เพราะจะทำให้
ใช้หน่วยความจำมาก และมีผลต่อการทำงานของโปรแกรมโดยรวมถ้าเกิดหน่วยความจำรั่วไหล 
ดังนั้นใช้เฉพาะที่จำเป็น
 
 

การใช้งาน Eager Watchers

ปกติแล้วฟังก์ชั่น watch() จะทำงานในลักษณะมีเวลาที่ต้องรอ หรือก็คือต้องรอจนกว่าข้อมูลที่ต้อง
การตรวจสอบเปลี่ยนแปลง จึงไปเรียกใช้ฟังก์ชั่นที่กำหนด แต่กรณีที่เราจำเป็นที่จะให้ทำงานที่จะมี
การเปลี่ยนแปลงข้อมูล state หรือก็คือให้ทำงานทันที หลังจาก mount โดยเพิ่มส่วนนี้เข้าไป
{ immediate: true } เช่น กรณีอาจจะต้องการ ดึงค่าเริ่มต้นมาใช้ ดูตัวอย่าง
 
<script setup>
import { ref, watch, reactive } from 'vue'

const message = ref('Hello World')

watch(
  message,
  (newVal, oldVal) => {
    console.log(`message changed from ${oldVal} to ${newVal}`);
  },
  { immediate: true }
);

// การเปลี่ยนแปลงค่าของ message หลังจาก mount จะถูกจับโดย watcher ทันที
setTimeout(() => {
  message.value = 'Hello Vue 3!';
}, 1000);
</script>

<template>
  <h1>{{ message }}</h1>
</template>
 
กรณีนี้ก่อนข้อมูลจะเปลี่ยนแปลงใน 1 วินาทีตามที่เรากำหนดจำลองไว้ watch() จะถูกเรียกใช้งาน
ครั้งแรกก่อน และค่า oldVal จะเป็น undefined แลหลังจาก 1 วินาที ตัว watch ก็จะทำงาน
ปกติ

 

การใช้งาน Once Watchers

สำหรับรูปแบบนี้จะใช้กรณีต้องการให้ทำการตรวจจับการเปลี่ยนแปลงข้อมูลและทำงานเพียงครั้ง
แรกครั้งเดียวเท่านั้น โดยใส่ค่านี้เข้าไป { once: true } ส่วนนี้น่าจะไม่ต้องอธิบายเพิ่มเติม
 
watch(
  source,
  (newValue, oldValue) => {
    // when `source` changes, triggers only once
  },
  { once: true }
)
 

 

การใช้งานฟังก์ชั่น watchEffect()

การทำงานก็จะคล้ายๆ กับ watch แต่รูปแบบวิธีการเขียนจะแตกต่าง และสะดวกในอีกแบบ คือในการ
ใช้งาน watchEffect เราไม่จำเป็นต้องกำหนดตัวแปร หรือ state ที่ต้องการตรวจจับการเปลี่ยน
แปลงลงไปเป็นใน parameter ตัวแรก เหมือน watch แต่ใช้วิธีใช้ค่าที่กำหนดในส่วนของฟังก์ชั่น
callback ด้านในแทนเลย คือ ในการใช้งานแบบปกติ หรือ sync ตามลำดับต่อเนื่อง ไม่มีเรื่องเวลา
ที่ต้องรอเข้ามาเกี่ยวข้อง หากเรากำหนด state ใดๆ ด้านในฟังก์ชั่น ตัว watchEffect ก็จะใช้ตัว
แปรนั้นๆ เป็นตัวที่ถูกกำหนดให้ตรวจจับการเปลี่ยนแปลง ข้อดีคือ เราไม่ต้องกำหนดซ้ำเหมือน watch
ข้อเสียคือ อาจจะดูยากว่ากำหนดตัวไหนบ้าง ต้องไล่ดูในโค้ดเอา นอกจากนั้นตัว watchEffect จะทำ
งานทันทีหลังจาก mount 1 ครั้งโดยไม่ต้องรอข้อมูลเปลี่ยนแปลง หรือคล้ายใช้รุปแบบ 
{ immediate: true } ใน watch ดูตัวอย่างด้านล่างนี้ประกอบ
 
<script setup>
import { ref, watchEffect, reactive } from 'vue'

const someData = ref('Hello World');
const anotherData = ref(123);

watchEffect(() => {
  console.log(`someData is now: ${someData.value}`);
  console.log(`anotherData is now: ${anotherData.value}`);
});

// จำลองการเปลี่ยนแปลงค่าของ someData และ anotherData หลังจาก mount
setTimeout(() => {
  someData.value = 'Hello Vue 3!';
  anotherData.value = 456;
}, 1000);
</script>

<template>
  <div>{{ someData }}</div>
  <div>{{ anotherData }}</div>
</template>
 
ตัวแปร someData และ anotherData ถูกใช้งานในฟังก์ชั่น และตัวแปรทั้งสอง จะเป้นตัวที่
watcher ตรวจจับการเปลี่ยนแปลงอัตโนมัติ โดยไม่ต้องกำหนดว่าใช้ตัวแปรใด เหมือน watch
นอกจากนั้นฟังก์ชั่นยังทำงานทันที 1 ครั้งหลังจาก mount แต่แตกต่างกับ watch ตรงที่ค่า
เริ่มต้นจะไม่ใช่ undefined แต่จะเป็นค่าที่เรากำหนด
 
กรณีที่ใช้งานกับรูปแบบฟังก์ชั่นที่มีเวลาที่ต้องรอเขามาเกี่ยวช้อง หรือรูปแบบ async  เฉพาะตัวแปร
หรือ state ที่อยู่ await เท่านั้น ที่ตัว watcher จะตรวจจับการเปลี่ยนแปลง ด้านล่างเป็นการจำลอง
กรณีเรียกข้อมูลแบบ async ในวินาทีที่ 10 เราให้ข้อมูล  anotherData มีการเปลี่ยน แต่เพราะว่า
มีเฉพาะตัวแปร someData เท่านั้นที่ถูกเรียกใช้งานก่อน await ตัวแรก ดังนั้นการเปลี่ยนแปลงของ
anotherData จึงไม่มีผลให้ watchEffect() ทำงาน
 
<script setup>
import { ref, watchEffect, reactive } from 'vue'

const someData = ref('Hello World');
const anotherData = ref(123);

watchEffect(async () => {
  // กรณีนี้  เฉพาะ someData เท่านั้น ที่ watcher ตรวจจับการเปลี่ยนแปลง
  // เพราะเป็นส่วนของ state ที่ถูกเรียกใช้ก่อนรูปแบบ await แรก
  console.log(`someData is now: ${someData.value}`)
  await new Promise(resolve => setTimeout(() => {
    console.log(`anotherData is now: ${anotherData.value}`)  
    return resolve
  },3000));
});

// จำลองการเปลี่ยนแปลงค่าของ someData และ anotherData หลังจาก mount
// กรณีนี้มีผลต่อ watcher เพราะมี someData ที่มีค่าเปลี่ยนแปลง
setTimeout(() => {
  someData.value = 'Hello Vue 3!';
  anotherData.value = 456;
}, 1000);

// จำลองการเปลี่ยนแปลงที่วินาทีที่ 10
// anotherData ไม่มีผลต่อ watcher
setTimeout(() => {
  anotherData.value = 789;
}, 10000);
</script>

<template>
  <div>{{ someData }}</div>
  <div>{{ anotherData }}</div>
</template>

ข้อแตกต่างระหว่าง  watch และ watchEffect

 
watch
- การใช้งาน: ใช้เพื่อติดตามการเปลี่ยนแปลงของค่าเฉพาะเจาะจง 
(specific data properties) โดยการระบุ dependencies ที่ต้องการติดตาม
- วิธีการทำงาน: เราต้องระบุ source ของการเปลี่ยนแปลงที่ต้องการติดตาม 
(getter function) และ callback function ที่จะถูกเรียกเมื่อค่ามีการเปลี่ยนแปลง
- ความยืดหยุ่น: มีความยืดหยุ่นในการควบคุม dependencies ที่ต้องการติดตาม
และสามารถระบุ deep watching และ immediate execution ได้
 
watchEffect
- การใช้งาน: ใช้เมื่อต้องการให้ effect ทำงานทันทีและติดตามการเปลี่ยนแปลงของ 
reactive data ใด ๆ ที่ถูกใช้งานในฟังก์ชัน โดยไม่ต้องระบุ dependencies อย่างชัดเจน
- วิธีการทำงาน: จะทำงานทันทีเมื่อ component ถูกสร้างขึ้นและจะทำงานใหม่ทุกครั้ง
ที่มีการเปลี่ยนแปลงใน reactive data ใด ๆ ที่ถูกใช้ภายในฟังก์ชัน
- ความสะดวก: เหมาะสำหรับการติดตามหลายๆ reactive data พร้อมกัน 
โดยไม่ต้องระบุ dependencies อย่างชัดเจน
 
 

การใช้งาน Callback Flush Timing

หรือการกำหนดเวลาในการเรียก callback function ให้กับฟังก์ชั่น watch หรือ watchEffect
โดยจะเป็นการกำหนดค่า flush option เพิ่มเข้าไป ซึ่งค่านี้โดยทั่วไปจะมีด้วยกัน 3 ค่า คือ
 
'pre': เรียก callback function ก่อนการอัปเดต DOM (ค่าเริ่มต้น ไม่ต้องกำหนดอะไรก็ได้)
'post': เรียก callback function หลังจากการอัปเดต DOM เสร็จสิ้นแล้ว
'sync': เรียก callback function ทันทีโดยไม่ต้องรอการอัปเดต DOM ทำทันที่ที่ข้อมูลเปลี่ยน
 

ตัวอย่างการใช้งาน ใช้ได้ทั้งแบบ watch และ watchEffect

<script setup>
import { ref, watch, watchEffect, watchPostEffect,
   watchSyncEffect } from 'vue'

const someData1 = ref('Hello World 1');
const someData2 = ref('Hello World 2');
const someData3 = ref('Hello World 3');

watchEffect( () => {
  // callback function ก่อนการอัปเดต DOM (ค่าเริ่มต้น)
  console.log(`someData1 is now: ${someData1.value}`);
});

watchEffect( () => {
  //  เรียก callback function หลังจากการอัปเดต DOM เสร็จสิ้นแล้ว
  console.log(`someData2 is now: ${someData2.value}`);
},
{ flush: 'post' }
);
// หรือใช้เป็น watchPostEffect
/*
watchPostEffect( () => {
  // callback function ก่อนการอัปเดต DOM (ค่าเริ่มต้น)
  console.log(`someData1 is now: ${someData1.value}`);
});
*/

watchEffect( () => {
  // เรียก callback function ทันทีโดยไม่ต้องรอการอัปเดต DOM
  console.log(`someData3 is now: ${someData3.value}`);
},
{ flush: 'sync' }
);
// หรือใช้เป็น watchSyncEffect
/*
watchSyncEffect( () => {
  // callback function ก่อนการอัปเดต DOM (ค่าเริ่มต้น)
  console.log(`someData1 is now: ${someData1.value}`);
});
*/

setTimeout(() => {
  someData1.value = 'Hello Vue 1!';
  someData2.value = 'Hello Vue 2!';
  someData3.value = 'Hello Vue 3!';
}, 3000);
</script>

<template>
  <div>{{ someData1 }}</div>
  <div>{{ someData2 }}</div>
  <div>{{ someData3 }}</div>
</template>
 
 
 

การหยุดการทำงาานของ Watcher (Stopping a Watcher)

การกำหนด watcher ในตัวอย่างแบบ sync ใน setup() หรือ <script setup> ตัว watcher
จะผูกกับ component นั้นๆ และจะหยุดการทำงานอัตโนมัติเมื่อตัว component ถูกลบออกไป
โดยทั่วไปแล้วเราไม่ต้องกังวลกับการหยุดการทำงานของ watcher 
แต่ถ้าเป็นกรณีที่เป็นแบบ async หรือมีเวลาที่ต้องรอเข้ามาเกี่ยวกับ จะทำให้ตัว watcher ไม่ได้
ถูกผูกกับ component นั้นๆ และเราจำเป็นต้องหยุดการทำงาน เพื่อป้องกันการรั่วไหลของการใช้
งานหน่วยความจำ ดูตัวอย่างประกอบ
 
<script setup>
import { watchEffect } from 'vue'

// หยุดทำงานอัตโนมัติ
watchEffect(() => {})

// ...ไม่หยุดทำงานเอง ต้องกำหนดให้หยุดการทำงาน
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>
 
ดังนั้นให้เราใช้รูปแบบดังนี้ เพื่อกำหนดให้หยุดการทำงาน กรณีใช้กับ async ดูตัวอย่าง
 
<script setup>
import { ref, watchEffect, reactive } from 'vue'

const someData = ref('Hello World');
const anotherData = ref(123);

const unwatch = watchEffect(async () => {
  // กรณีนี้  เฉพาะ someData เท่านั้น ที่ watcher ตรวจจับการเปลี่ยนแปลง
  // เพราะเป็นส่วนของ state ที่ถูกเรียกใช้ก่อนรูปแบบ await แรก
  console.log(`someData is now: ${someData.value}`);
  await new Promise(resolve => setTimeout(() => {
    console.log(`anotherData is now: ${anotherData.value}`);  
    return resolve
  },3000));
});

// จำลองการเปลี่ยนแปลงค่าของ someData และ anotherData หลังจาก mount
// กรณีนี้มีผลต่อ watcher เพราะมี someData ที่มีค่าเปลี่ยนแปลง
setTimeout(() => {
  someData.value = 'Hello Vue 3!';
  anotherData.value = 456;
}, 1000);

// จำลองครบ 10 วินาที ไม่ใช้ watcher แล้ว
setTimeout(() => {
  unwatch()
}, 10000);

// จำลองการทำงานหลังจากหยุด watcher c]h;
setTimeout(() => {
  someData.value = 'Watcher Stoped!';
}, 15000);
</script>

<template>
  <div>{{ someData }}</div>
  <div>{{ anotherData }}</div>
</template>
 
เมื่อใช้ watcher กับข้อมูลแบบ async เราจำลองให้หยุดการทำงานที่วินาทีที่ 10 แล้ว
วินาทีที่ 15 เรากำหนดให้ค่าเปลี่ยน ซึ่งผลลัพธ์ค่าที่ได้จะเปลี่ยนแปลง แต่จะไม่ทำงานใน
ฟังก์ชั่น watchEffect เพราะเราหยุดการทำงานไปแล้ว
 
การทำงานแบบ async เช่น การดึงข้อมูลจาก server หรือ api มาแสดง ที่มีเรื่องของเวลา
ที่ต้องรอเข้ามาเกี่ยวข้อง
 
ก่อนจบเนื้อหานี้ มีตัวอย่างการใช้งาน watch สำหรับดึงข้อมูล api มาแสดง
 
<script setup>
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
const loading = ref(false)

// watch works directly on a ref
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
</script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" :disabled="loading" />
  </p>
  <p>{{ answer }}</p>
</template>
 
ทดสอบพิมพ์ข้อความภาษาอังกฤษ ถ้าลงท้ายด้วย ? เครื่องหมายคำถาม จะสงค่าไปยัง
api เพื่อตอบกลับ 
 
เนื้อหาของบทความนี้ค่อนข้างเข้มข้นและเริ่มยากขึ้นเรื่อยๆ แต่เป็นแนวทางอันดีในการ
นำไปใช้งานต่อไป บทคามตอนหน้าจะเป็นอะไร รอติดตาม







กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง






เนื้อหาพิเศษ เฉพาะสำหรับสมาชิก

กรุณาล็อกอิน เพื่ออ่านเนื้อหาบทความ

ยังไม่เป็นสมาชิก

สมาชิกล็อกอิน



( หรือ เข้าใช้งานผ่าน Social Login )




URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ