บทความทั้งหมด

jQuery เป็นหนึ่งใน JavaScript Library ที่โด่งดังมาก (เมื่อ 10 ปีที่แล้ว) เรียกว่าในยุคนั้นแทบจะทุกเว็บจะต้องมีการติดตั้ง jQuery เอาไว้อย่างแน่นอน

แต่เมื่อยุคสมัยเปลี่ยนไป เบราเซอร์ใหม่ๆ ไม่มีปัญหาการรัน JavaScript ตั้งแต่ ES6 ขึ้นไปแล้ว การใช้งาน jQuery จึงลดน้อยลงเรื่อยๆ

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

ในบทความนี้ก็เลยลองเอาคำสั่ง jQuery ที่ใช้บ่อยๆ มาลองเทียบดู ว่าถ้าอยากเขียนแบบนี้โดยไม่ใช้ jQuery เราจะต้องทำยังไง

Ajax

ในยุค jQuery กำลังรุ่งเรือง การเรียกข้อมูลจาก API ด้วย HTTP Request หรือที่ในยุคนั้นนิยมเรียกว่า Ajax ถ้าเขียนด้วย JavaScript แบบธรรมดาจะยากมาก

การที่ jQuery มีฟังก์ชันสำหรับเรียกใช้ Ajax ง่ายๆ ด้วยเป็นหนึ่งในเหตุผลที่ทำให้มันได้รับความนิยม ถึงขนาดว่าบางครั้งโหลด jQuery ติดเข้าไปเพราะต้องการใช้ฟังก์ชัน ajax() แค่นั้นเลยก็มีนะ

$.ajax({
  method: 'GET',
  url: '/api/data',
  data: {
    page: 1,
  },
  dataType: 'json',
  success: function(data){
    console.log('Success:', data)
  },
  error: function(error){
    console.error('Error:', error)
  },
})

แต่ข้อเสียของ jQuery.ajax() ก็คือตอนนี้ฝั่ง vanilla มีทั้ง fetch() หรือไลบรารี่อย่าง Axios ให้ใช้งาน ซึ่งทั้งสองวิธีนี้ถูกสร้างมาด้วย JavaScript ยุคใหม่โดยใช้ Promise แล้ว ไม่เหมือนกับ jQuery ที่ยังใช้สไตล์ callback function อยู่เลย

//fetch API
fetch('/api/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    page: 1,
  }),
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data)
})
.catch((error) => {
  console.error('Error:', error)
})

//Axios
axios.get('/api/data', {
  params: {
    pafe: 1
  }
})
.then(function (response) {
  console.log('Success:', data)
})
.catch(function (error) {
  console.error('Error:', error)
})

Query Elements

First Match Element

หา element ตัวแรก

//jQuery
$('.ele').first()
$('.ele:first')

//Vanilla
document.querySelector('.box')

All Elements

หา element ทุกตัวที่ตรงกับ selector

//jQuery
$('.ele')

//Vanilla
document.querySelectorAll('.box')

สำหรับ jQuery แล้วการหา element แค่ 1 ตัวหรือหาทั้งหมด ผลที่ได้ไม่ต่างกันนั้นคืออ็อบเจคของ jQuery แต่สำหรับวนิลาแล้ว querySelector จะให้ค่ามาเป็น Element ส่วน querySelectorAll จะให้ค่าเป็น Array of Elements นะ

Nested Element

การหา element ภายใน element อีกทีหนึ่ง

//jQuery
let container = $('#container')
container.find('.box')

//Vanilla
let container = document.querySelector('.container')
container.querySelector('.box')

Action on Elements

สำหรับการสั่ง action อะไรบางอย่างกับ element ที่ได้มา ถ้าเลือกออกมาแค่ 1 element อันนี้ไม่ยาก

//jQuery
$(".box").first().doSomething()

//Vanilla
document.querySelector(".box").doSomething()

ปัญหาจะเกิดขึ้นเมื่อเรา select all กับ element ทุกตัว ซึ่งเรื่องนี้น่าจะง่ายกว่าถ้าเราใช้ jQuery เพราะการสั่ง action ของ jQuery นั้นจะวนลูปทำให้กับ element ทุกตัวอยู่แล้ว

//jQuery
$(".box").doSomething()

//Vanilla
document.querySelectorAll(".box").forEach(box => { 
    /* doSomething */ 
})

แต่สำหรับวนิลานั้น ค่าที่ได้ออกมาเป็น Array แปลว่าถ้าเราอยากสั่งให้มันทำอะไรให้ครบทุกตัว เราจะต้องวนลูปเอาเอง

เช่นสมมุติว่าเราอยาก hide ทุกอย่างที่มีคลาส box ถ้าเป็น jQuery ก็สั่งง่ายๆ เลยคือ .hide() แต่ถ้าเป็นวนิลาก็วนลูปยาวไป แล้วก็กำหนดที่ style ให้ display=none แทน

//jQuery
$(".box").hide()

//Vanilla
document.querySelectorAll(".box").forEach(box => { 
    box.style.display = "none" 
})

DOM Traversal

การวิ่งขึ้นๆ ลงๆ ใน DOM (โครงสร้างชั้นของ HTML ที่อยู่ในรูปแบบของ Tree) อันนี้ไม่ต่างกันมากเท่าไหร่ แค่คำสั่งของ jQuery จะสั้น/เขียนกระชับกว่านิดหน่อย

//jQuery
box.next()
box.prev()
box.parent()
box.children()
box.siblings()

//Vanilla
box.nextElementSibling
box.previousElementSibling
box.parentElement
box.children
box.parentNode.children.filter(el => el !== box) 

ตัวที่วนิลาไม่มีคือ siblings() ซึ่งเป็นคำสั่งเอาไว้เลือก Element ที่อยู่เลเวลเดียวกับเราทั้งหมด (แต่ไม่รวม Element ตัวเอง) ดังนั้นเลยต้องประยุกต์นิดหน่อยโดยวิ่งขึ้นไปที่ parent แล้วเลือก child ทุกตัวที่ไม่ใช่ตัวมันเองแทน

Event Handling

การกำหนด Event Listener แทบจะไม่ต่างกันแล้ว เพราะตอนหลังวนิลาก็คำสั่ง addEventListener ให้ใช้งานแล้ว

//jQuery
ele.click(function(event){})
ele.on('click', function(event){})
ele.on('mouseenter', function(event){})
ele.on('keyup', function(event){})
ele.off('click', function(event){})

//Vanilla
ele.addEventListener('click', (e) => {})
ele.addEventListener('mouseenter', (e) => {})
ele.addEventListener('keyup', (e) => {})
ele.removeEventListener('click', (e) => {})

Delegate

การดีลีเกตคือการกำหนด Event Listener ที่ Element ชั้นนอกแทนที่จะกำหนดที่ตัวมันเองตามปกติ

เช่นหากเราต้องการสร้าง Listener ที่ทำงานเมื่อกด <li> ในโครงสร้างแบบนี้

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>

เราก็ไปกำหนดให้ Listener อยู่ที่ <ul> แทน

สาเหตุที่ต้องทำแบบนี้มักจะเกิดกับกรณีที่ <li> นั้นอาจจะโดนสร้างแบบ dynamic โดย script ส่วนอื่น ทำให้อาจจะมี li เกิดขึ้นมาใหม่ได้เรื่อยๆ การไล่สั่งให้มัน Listener ทีละครั้งๆ ไปเป็นอะไรที่จัดการยากกมา เลยไปสั่งที่ ul แทน (แล้วพอเกิด Event ก็ให้ ul ส่งไปบอก li ภายในอีกที)

สำหรับวนิลาไม่มีคำสั่งให้ทำดีลีเกตได้เหมือน jQuery ดังนั้นก็ต้องประยุกต์เอาอีกแล้ว (ฮา)

//jQuery
container.on('click', '.box', function(event){
    //...
})

//Vanilla
container.addEventListener('click', function(event){
  if(event.target.matches('.box')){
      //...
  }
})

Create Event

อันนี้เป็นการสร้าง Event ขึ้นมาเอง .. ไม่ต่างกันมากนัก

//jQuery
ele.trigger('click')

//Vanilla
ele.dispatchEvent(new Event('click'))

Styling

การเซ็ต CSS ให้กับ Element

ของ jQuery จะใช้คำสั่ง .css() แต่วนิลาสามารถเซ็ตลงไปได้ผ่าน properties ที่ชื่อว่า .style

//jQuery
ele.css('color', '#000')
ele.css({
  'color': '#000',
  'background': '#FFF',
})

//Vanilla
ele.style.color = '#000'
ele.style.background = '#FFF'
ele.style.cssText = 'color: #000, background: #FFF'

บางครั้ง jQuery ก็มีฟังก์ชันที่จริงๆ เบื้องหลักก็ไปเซ็ตค่า css นั่นแหละแบบนี้ด้วยนะ

//jQuery
box.hide()
box.show()

//Vanilla
box.style.display = 'none'
box.style.display = 'block'

Document Ready

ในการรันสคริป บางทีเราต้องการจะให้หน้าเพจโหลดเสร็จซะก่อน สำหรับ jQuery นั้นเราใช้คำสั่ง ready() หรือส่งฟังก์ชันเข้าไปเลยก็ได้ โค้ดส่วนนี้จะทำงานเหมือน document ทั้งหมดโหลดเสร็จแล้ว

สำหรับ vanilla นั้นไม่มีคำสั่งสำเร็จขนาดนั้น แต่ถ้าอยากได้ก็เขียนได้ ... แต่เอาจริงๆ ส่วนใหญ่เราจะใช้วิธีเรียกฟังก์ชันที่ท้ายๆ ของหน้าเพจมากกว่า

//jQuery
$(document).ready(function(){ 
  /*...*/ 
})

$(function(){ 
  /*...*/ 
})

//Vanilla
(function(callback){
  if (document.readyState != "loading") callback()
  else document.addEventListener("DOMContentLoaded", callback)
})(function(){
   /*...*/
})

Class Properties

สำหรับสำหรับจัดการ class ซึ่งตอนนี้ฝั่ง vanilla ก็ทำได้ทั้งหมดเหมือน jQuery แล้วผ่าน properties ชื่อ classList

//jQuery
box.addClass('active focus')
box.removeClass('active')
box.toggleClass('active')
box.hasClass('active')

//Vanilla
box.classList.add('active', 'focus')
box.classList.remove('active')
box.classList.toggle('active')
box.classList.replace('active', 'blur')
box.classList.contains('active')

Create Virtual Element

Virtual Element เป็นการสร้าง Element HTML ขึ้นมาในฝั่ง JavaScript ซึ่งสามารถเอาไปปะให้แสดงผลใน document ได้ ซึ่งหลายๆ เฟรมเวิร์คตอนนี้ก็ใช้วิธีนี้แหละ ในการควบคุมการทำงานระหว่างสคริปกับ HTML

สำหรับ jQuery การสร้าง Virtual Element แค่เติม <> ครอบชื่อแท็กลงไปก็พอแล้ว ซึ่งเราจะได้ element ออกมา สามารถเอาไปใช้งานได้เลย

ทางฝั่ง vanilla ตอนนี้วิธีสร้าง Virtual Element ก็ไม่ยากล่ะ เพราะมีคำสั่ง createElement ให้ใช้

//jQuery
let div = $('<div>')
div.text('Hello World!')
div.html('Hello World!')

//Vanilla
let div = document.createElement('div')
div.textContent = 'Hello World!'
div.innerHTML = 'Hello World!'

DOM Manipulate

การจัดการ DOM อันนี้เราคิดว่าคำสั่งทางฝั่ง vanilla อ่านเข้าใจง่ายกว่า

คือแบ่งเป็น 2 เรื่อง การแก้ไข DOM ภายใน กับ ภายนอก (replace)

//jQuery
el.replaceWith('x')
el.html('x')

//Vanilla
el.outerHTML = 'x'
el.innserHTML = 'x'

ส่วนการเพิ่ม element เข้าไป ก็มี 2 คำสั่งคือเพิ่มต่อท้าย กับเพิ่มแทรกเข้าไปเป็นตัวแรก ซึ่งฝั่ง vanilla นั้นไม่มีคำสั่งเพิ่มแบบแทรกเข้าไปเป็นตัวแรก ต้องหา child ตัวแรกให้ได้ก่อน แล้วใช้ insertBefore ต่ออีกที

//jQuery
ul.append($('<li>'))
ul.prepend($('<li>'))

//Vanilla
ul.appendChild(document.createElement('li'))
ul.insertBefore(document.createElement('li'), parent.firstChild)

เรื่องการลบทิ้ง, การแทรกในตำแหน่งต่างๆ, และการ clone อันนี้ไม่ยากเท่าไหร่

//jQuery
el.remove()

//Vanilla
el.parentNode.removeChild(el)
//jQuery
$(target).after(element)
$(target).before(element)

//Vanilla
target.insertAdjacentElement('afterend', element)
target.insertAdjacentElement('beforebegin', element)
//jQuery
$(el).clone()

//Vanilla
el.cloneNode(true)

Attribute

เรื่องการจัดการ attribute ฝั่ง jQuery จะพิเศษหน่อยตรงมี data() ให้ใช้งานด้วย

//jQuery
el.prop('data-x')
el.attr('data-x')
el.data('x')

//Vanilla
el['data-x']
//jQuery
el.removeAttr('data-x')

//Vanilla
el.removeAttribute('data-x')

Effect

สำหรับ jQuery นั้นมีฟังก์ชันที่ทำให้เราโชว์/ซ่อน element แบบมีอนิเมชันได้ด้วย เช่น fadeOut, fadeIn, slideUp, slideDown (ซึ่งเบื้องหลังเป็นการสั่งเปลี่ยนค่าพวกความสูงหรือ opacity ด้วยลูปเอานะ)

สำหรับการเขียนแบบใหม่ ตอนนี้เราไม่นิยมให้ JavaScript เป็นคนจัดการอนิเมชันแล้ว แต่โยนไปเป็นหน้าที่ของ css แทน เช่นการใช้ transition

ฝั่ง JavaScript มีหน้าที่แค่ระบุคลาสของ css เข้าไปเท่านั้นพอ

//jQuery
$(el).fadeOut()

//Vanilla
el.classList.add('hide')
el.classList.remove('show')
/* With this CSS */
.show {
  opacity: 1;
}
.hide {
  opacity: 0;
  transition: opacity 400ms;
}

จริงๆ น่าจะยังมีอีกหลายคำสั่ง

Vue 3 เพิ่งเปิดตัวมาเมื่อเดือนที่แล้ว ซึ่งมาพร้อมกับฟีเจอร์ใหม่ๆ และสิ่งที่เปลี่ยนแปลงไป เรามาดูกันดีกว่า

เขียนใหม่ด้วย TypeScript

ภาษา JavaScript นั้นไม่มี Type ของตัวแปรทำให้เวลาเขียนโปรแกรมมีโอกาสเกิดข้อผิลพลาดเยอะ ดังนั้นการเขียนงานโปรเจคใหญ่ๆ คนเลยนิยมเปลี่ยนไปใช้ TypeScript แทน (ถ้ายังไม่รู้จัก TypeScript อ่านต่อได้ที่นี่)

สำหรับ Vue 3.0 นี้ก็เป็นการเขียนใหม่ด้วย TypeScript แทน แต่เวลาเราเอามาใช้งาน เราสามารถเลือกได้ว่าจะใช้แบบ JavaScript ตามปกติ หรือจะใช้แบบ TypeScript ก็ได้

interface Book {
  title: string
  author: string
  year: number
}

const Component = defineComponent({
  data() {
    return {
      book: {
        title: 'Vue 3 Guide',
        author: 'Vue Team',
        year: 2020
      } as Book
    }
  }
})

อ่านเรื่อง TypeScript ต่อได้ที่นี่

JSX

เป็นฟีเจอร์ที่เอามาจาก React นั้นคือแทนที่จะใช้ Hyperscript แบบนี้

Vue.h(
  Vue.resolveComponent('anchored-heading'),
  {
    level: 1
  },
  [Vue.h('span', 'Hello'), ' world!']
)

Vue.h ย่อมาจาก Hyperscript ซึ่งหมายถึง script ที่เอาไว้สร้าง HTML structures นั่นเอง

การเขียน JSX ได้จะทำให้เราเขียน HTML ลงไปในโค้ด JavaScript ตรงๆ ได้เลย

import AnchoredHeading from './AnchoredHeading.vue'

const app = createApp({
  render() {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
    )
  }
})

app.mount('#demo')

อ่านเรื่อง JSX ต่อได้ที่นี่

API ที่เปลี่ยนไปใน Vue 3

ในหัวข้อนี้ขอยกตัวอย่างโค้ดข้างล่างนี่ ซึ่งรันได้ปกติใน Vue 2

<div id="app">
    <h1>{{ title }}</h1>
    <my-button @whenclick="changeTitleText"></my-button>
</div>

<script src="https://unpkg.com/[email protected]"></script>
<script>
Vue.component('my-button', {
    template: `
        <button @click="handleClick">Click Me!</button>
    `,
    methods: {
        handleClick(){
            this.$emit('whenclick')
        }
    },
})

let app = new Vue({
    el: '#app',
    data: {
        title: 'นี่คือ Title แบบ Vue 2',
    },
    methods: {
        changeTitleText(){
            this.title = 'โค้ดนี้ทำงานได้ใน Vue 2'
        }
    },
})
</script>

See the Pen
Vue3-change-1
by Ta Tanapoj Chaivanichanan (@tanapoj)
on CodePen.

ในตัวอย่างนี้มีการสร้าง component ชื่อ my-button ขึ้นมาหนึ่งตัว แล้วตั้งค่าไว้ว่าถ้ามีการคลิก ให้เปลี่ยนข้อความ title ซะ

ทีนี้ ถ้าเราเปลี่ยนไปใช้ Vue 3 ตัวนี้แทน https://unpkg.com/[email protected] เราจะพบว่าโค้ดของเรารันไม่ได้ไปซะแล้ว

สาเหตุก็เพราะว่า API การเรียกใช้งานมีการเปลี่ยนแปลง

วิธี createApp

จากเดิมใน Vue 2 ถ้าเราจะสร้าง Application ขึ้นมา ก็จะใช้วิธีการ new Vue ขึ้นมาตรงๆ เลย

const app = new Vue({
    el: '#app',
    //TODO
})

แต่สำหรับ Vue 3 จะเปลี่ยนไปใช้คำสั่ง createApp แทน (ไม่สามารถสร้างเองได้แล้ว ต้องสร้างผ่าน factory function ที่ Vue เตรียมไว้ให้เท่านั้น)

const app = Vue.createApp({
    //TODO
})

app.mount('#app')

หรือ

import { createApp } from 'vue'

const app = createApp({
    //TODO
})

app.mount('#app')

อีกเรื่องหนึ่งคือการ mount แอพเข้ากับ html โดยใช้ el นั้นโดนตัดออกไปแล้ว

ไปใช้วิธีการ mount ด้วยเมธอด .mount() หลังจากสร้างแอพไปแล้วแทน ซึ่งเป็นการแยกส่วนโลจิคออกจากการกำหนดส่วนแสดงผล (HTML) ออกจากกัน ทำให้ตอนนี้เราสามารถสร้างแอพเปล่าๆ โดยยังไม่ต้อง mount ได้แล้ว ถ้าอยากเอาไปทำเทสเพิ่มก็ทำได้ง่ายขึ้น

data ต้องเป็น method เท่านั้น

ปกติ data ของ Vue 2 นั้นสามารถกำหนดเป็น object หรือ function ที่รีเทิร์น object กลับมาแบบไหนก็ได้

new Vue({
    data: {
        title: 'นี่คือ Title แบบ Vue 2',
    },
})

แต่สำหรับ Vue 3 ถูกกำหนดว่า data จะต้องเป็น function เท่านั้น

Vue.createApp({
    data(){
        return {
            title: 'นี่คือ Title แบบ Vue 2',
        }
    },
})

Component ผูกกับ app

ใน Vue 2 เวลาเราสร้างคอมโพเนนท์ เราจะสั่งผ่านตัวแปร Vue ที่เป็นระดับ global แปลว่าคอมโพเนนท์นี้สามารถเรียกใช้จาก Application ของ Vue ตัวไหนก็ได้

Vue.component('my-button', {
    template: `
        <button @click="handleClick">Click Me!</button>
    `,
    methods: {
        handleClick(){
            this.$emit('whenclick')
        }
    },
})

แต่สำหรับ Vue 3 แล้วการจะสร้างคอมโพเนนท์จะต้องสร้างให้แอพตัวใดตัวหนึ่งไปเลย

const app = Vue.createApp({
    //TODO
})

app.component('my-button', {
    emits: ['whenclick'], //optional
    template: `
        <button @click="handleClick">Click Me!</button>
    `,
    methods: {
        handleClick(){
            this.$emit('whenclick')
        }
    },
})

emits เป็น properties ที่จะกำหนด หรือไม่กำหนดก็ได้ หน้าที่ของมันคือเอาไว้เก็บชื่อเมธอดทั้งหมดที่คอมฑโพเนนท์ตัวนี้ทำได้ (มันก็คือ $emit อะไรได้บ้างนั่นแหละ) แต่การใส่ใน emits นั้นจะทำให้เวลาเรามาอ่านว่าคอมโพเนนท์ตัวนี้มีอะไรให้เรียกใช้ได้บ้างก็จะดูจากตรงนี้ได้เลย ไม่ต้องเสียเวลาไปไล่โค้ดข้างในอีกที

See the Pen
OJNGLEV
by Ta Tanapoj Chaivanichanan (@tanapoj)
on CodePen.

Vue Router ต้องสร้างด้วย createRouter()

เร้าเตอร์ก็เป็นอีกตัวที่มีการเปลี่ยนรูปแบบการเขียน

import Vue from 'vue'
import VueRouter from 'vue-router'

import App from './App.vue'
import FirstPage from './pages/FirstPage.vue'
import Page from './pages/FirstPage.vue'

Vue.use(VueRouter)

new Vue({
    render: (h) => h(App),
    router: new VueRouter({
        mode: 'history',
        routers: [
            { path: '/', component: FirstPage },
            { path: '/second', component: SecondPage },
        ],
    }),
}).$mount('#app')

โดยการสร้าง router จะต้องสร้างแยกตั้งหากด้วยคำสั่ง createRouter()

รวมถึงโหมด history ที่ตอนแรกเป็นค่าคอนฟิก, ใน Vue 3 ก็แยกออกมาเป็น createWebHistory() แล้วเหมือนกัน

import {createApp} from 'vue'
import {createRouter, createWebHistory} from 'vue-router'

import App from './App.vue'
import FirstPage from './pages/FirstPage.vue'
import Page from './pages/FirstPage.vue'

const router = createRouter({
    history: createWebHistory(),
    routers: [
        { path: '/', component: FirstPage },
        { path: '/second', component: SecondPage },
    ],
})

const app = createApp(App)
app.use(router)

router.isReady().then(() => {
    app.mount('#app')
})

Vuex ต้องสร้าง store ด้วย createStore()

คล้ายๆ กับข้อเมื่อกี้คือถ้าในโค้ดเรามีการใช้ Vuex การจะสร้าง store ขึ้นมาแทนที่จะ new Vuex.Store ตรงๆ ก็จะเปลี่ยนไปสร้างผ่าน createStore() แทน

import Vue from 'vue'
import Vuex from 'vuex'

import App from './App.vue'

const store = new Vuex.Store({
    //...
})

new Vue({
    store: store,
    render: (h) => h(App),
}).$mount('#app')

แบบนี้

import {createApp} from 'vue'
import {createStore} from 'vuex'

import App from './App.vue'

const store = createStore({
    //...
})

const app = createApp(App)
app.use(store)
app.mount('#app')

พวกค่าต่างๆ ของ store เช่น state, mutations, getters, actions ก็เซ็ตเหมือนเดิมทุกอย่าง ไม่มีอะไรเปลี่ยนแปลง

...

โดยรวมแล้ว Vue 3 มีการเปลี่ยน API ให้แยกส่วนกันมากขึ้น ทำให้โครงสร้างโค้ดถูกแบ่งเป็นโมดูลๆ จัดการและเทสง่ายขึ้น

Fragment

สำหรับใช้ที่เคยใช้ React มาก่อน Fragment ใน Vue ก็ใช้คอนเซ็ปเดียวกันนั่นแหละ

ตามปกติแล้วเวลาเราสร้าง template จำเป็นจะต้องมี root element 1 ตัวครอบทุกอย่างเอาไว้ ซึ่งหลายๆ ครั้งมันก็ไม่สะดวกเอาซะเลย

<!-- Layout.vue -->
<template>
  <div>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

สำหรับ Vue 3 นั้นอนุญาตให้เราสร้าง root element หลายตัวใน template ได้แล้ว

<!-- Layout.vue -->
<template>
  <header>...</header>
  <main v-bind="$attrs">...</main>
  <footer>...</footer>
</template>

Teleport Component

teleport เป็นฟีเจอร์ที่ทำให้คอมโพเนนท์บางส่วนที่เราสร้างขึ้นมาใน Application สามารถ "เทเลฟอร์ท" หรือ "วาร์ป" ออกไปข้างนอกได้!

ตัวอย่างที่เข้าใจง่ายที่สุดน่าจะเป็น Modal หรือการที่มีกล่องเหรือเฟรมอะไรสักอย่างเด้งขึ้นมาซ้อนทับ content ของเราเป็นอีกเลเยอร์หนึ่ง

สำหรับ Vue เวอร์ชันก่อนๆ ที่ต้องเขียนทุกอย่างอยู่ในขอบเขตของ Application เท่านั้น

ถ้าเราจะสร้าง Modal ขึ้นมาก็จะต้องสร้างไว้ข้างในแอพนั้นแหละ

แต่สำหรับ Vue 3 เราสามารถกำหนดสิ่งที่เรียกว่า teleport เพื่อบอกว่าโค้ดตรงนี้นะ เราจะส่งมันออกไปแสดงผลข้างนอกแอพ!

จากเดิมเป็นโค้ดแบบนี้

const app = Vue.createApp({});

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Open full screen modal!
    </button>

    <div v-if="modalOpen" class="modal">
      <div>
        I'm a modal! 
        <button @click="modalOpen = false">
          Close
        </button>
      </div>
    </div>
  `,
  data() {
    return { 
      modalOpen: false
    }
  }
})

ให้เราเพิ่ม <teleport to="body"> ลงไปครอบคอมโพเนนท์ส่วนที่ต้องการส่งออกไปข้างนอก (เช่นเคสนี้ส่งไปแสดงผลที่ชั้น body)

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Open full screen modal! (With teleport!)
    </button>

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal! 
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
      </div>
    </teleport>
  `,
  data() {
    return { 
      modalOpen: false
    }
  }
})

See the Pen
gOPNvjR
by Vue (@Vue)
on CodePen.


จริงๆ ยังมีอีกหลายฟีเจอร์ที่ Vue 3 มีการเพิ่มฝเปลี่ยนแปลง แต่อันนี้เลือกมาเฉพาะๆ ตัวที่คิดว่าสำคัญ

ป.ล. Vue 3 นั้นออกเวอร์ชัน 3.0.0 เรียบร้อยแล้ว แต่เฟรมเวิร์คหลัๆ บางตัวก็ยังไม่เสร็จดี เช่น router หรือ Vuex และสำหรับการติดตั้งจะต้องใช้ npm i [email protected] หากต้องการจะใช้เวอร์ชัน 3 (ถ้าใช้ npm i vue จะได้เวอร์ชัน 2) ซึ่งการเปลี่ยนแปลงให้ vue 3 เป็นตัวหลักแทนน่าจะดำเนินการเสร็จภายในสิ้นปี 2020 นี้

อ่านเพิ่มเติมได้ที่ Release Note นะ

Node.js กับ TypeScript

Node.js เป็นหนึ่งในเฟรมเวิร์คยอดนิยมสำหรับเขียนโปรแกรมฝั่ง Backend แต่เพราะมันสร้างมาตั้งแต่ปี 2009 ยุคที่ JavaScript ยังเป็นเวอร์ชัน ES5 อยู่เลย

โดยดีฟอลต์แล้ว Node.js เลยไม่ซัพพอร์ท TypeScript เลย ไม่เหมือนกับ Deno ที่เพิ่งสร้างขึ้นมาโดยซัพพอร์ท TypeScript เป็นค่า default ตั้งแต่แรก

เพื่อชีวิตที่ดีกว่า มาเซ็ตโปรเจค Node.js + Express ให้ใช้ TypeScript ได้กันเถอะ!

Step 1: ติดตั้ง TypeScript

สำหรับใครที่มี TypeScript อยู่แล้วข้ามสเต็ปนี้ไปได้เลยนะ

แต่ถ้าใครยังไม่มี ดูวิธีติดตั้งแบบง่ายๆ ได้ที่บทความ สอนวิธีเซ็ตโปรเจค TypeScript / มาใส่ไทป์ให้ JavaScript เพื่อลดความผิดพลาดในการเขียนโค้ดกันดีกว่า

Step 2: เริ่มสร้างโปรเจค

ปกติเราจะเขียนโค้ด Express หน้าตาประมาณนี้

const express = require('express')

const app = express()

app.get('/', (req, res) => {
    res.send('Hello World!')
})

app.listen(3000)

สมมุติว่าเซฟเป็นไฟล์ชื่อ app.js ละกัน วิธีที่จะเปลี่ยนมันเป็น TypeScript อย่างแรกเลยคือเปลี่ยนนามสกุลให้กลายเป็น app.ts

Step 3: @types

ปัญหาเมื่อเรานำโค้ด JavaScript ธรรมดามาเปลี่ยนเป็น TypeScript นั่นคือการที่ไทป์หายไป!

TypeScript Compiler หรือ tsc ไม่มีทางรู้ได้เลยว่าโค้ดที่เขียนๆ ขึ้นมาใน JavaScript แต่ละตัวมีไทป์เป็นอะไรบ้าง

ดังนั้นเลยต้องมีการ define ให้ TypeScript รู้จัก ซึ่งการทำแบบนั้นไม่ต้องถึงขั้นเขียนโค้ดใหม่ทั้งหมด แต่แค่เพิ่ม @types เข้าไปก็พอแล้ว!

ส่วนการติดตั้งก็ไม่ยาก มี npm ใช้ npm, มี yarn ใช้ yarn

//npm
npm i @types/express --save-dev

//yarn
yarn add @types/express --dev

ข้อสังเกตคือ library ยอดนิยมส่วนใหญ่จะมีการทำ types ไว้ให้อยู่แล้ว โดยชื่อของแพกเกจไทป์ส่วนใหญ่จะชื่อเดียวกับชื่อแพกเกจหลัก แต่เติม @types/ ไว้ข้างหน้า

จากนั้นเราก็กลับไปแก้โค้ดของเราอีกที

สำหรับ TypeScript นั้นจะต้องกำหนดไทป์ให้ตัวแปรทุกตัว เช่น Application, Request, Response ซึ่ง import มาจาก express ได้ตรงๆ เลย

import express, { Application, Request, Response } from 'express'

const app: Application = express()

app.get('/', (req: Request, res: Response) => {
    res.send('Hello World!')
})

app.listen(3000)

เวลาจะใช้งานก็ต้องคอมไพล์ให้ออกมาเป็น .js ซะก่อนนะ

Step 4: Watch File

จากสเต็ปที่แล้ว จะพบว่าระหว่างการ develop จะมีขั้นตอนเยอะมากในการรัน

  • ต้องคอมไพล์ TypeScript ด้วยคำสั่ง tsc ก่อน
  • รัน Node ด้วยคำสั่ง node
  • แล้วถ้ามีการแก้ไข้โค้ดอะไร ก็ต้อง kill process แล้วเริ่มคอมไพล์ตั้งแต่ TypeScript อีกรอบหนึ่งเลย
  • ซึ่งเป็นอะไรที่น่ารำคาญและทำให้ Productivity ต่ำมากๆ

ดังนั้นในสเต็ปสุดท้ายนี้เรามาดูวิธีการเซ็ตคำสั่ง ให้มัน watch file ให้เรากันดีกว่า

การ watch file หมายความว่า ให้ node คอยดูถ้าไฟล์ source code ของโปรแกรมมีการเปลี่ยนแปลง (เช่นมีการ save โค้ดตัวใหม่เข้ามา) ให้เริ่มรันโปรแกรมใหม่ด้วยตัวมันเอง เราจะได้ไม่ต้องมาคอยสั่งทุกครั้ง

ตามปกติถ้าเราเขียน Node.js ธรรมดา เราอาจจะใช้แพ็กเกจ เช่น nodemon

nodemon app.js

แต่ถ้าเราเขียนโค้ดในไฟล์ TypeScript อยู่ เราก็ต้องสั่งให้มัน watch file ด้วยอีกต่อหนึ่ง

tsc --watch

ซึ่งถ้าใช้วิธีนี้จะต้องเปิด Terminal ไว้ 2 อัน อันนึงรัน nodemon อีกอันรัน tsc นะ

หรือเราอาจจะใช้ ts-node แทนก็ได้ (อ่านวิธีใช้ ts-node และวิธีเซ็ต structure ของโปรเจคได้ที่ บทความสอนวิธีเซ็ตโปรเจค TypeScript)

nodemon --watch 'src/**/*.ts' --ignore 'src/**/*.spec.ts' --exec 'ts-node' src/app.ts

นั่นคือเราสั่งให้ nodemon คอยดูไฟล์ทั้งหมดในโฟลเดอร์ src ที่เป็นนามสกุล .ts ถ้าไฟล์มีการเปลี่ยนแปลง (มีการแก้ไขหรือมีการเซฟใหม่) ให้รันคำสั่ง ts-node ใหม่ทันที

หรือถ้าไม่อยากเขียน command ยาวๆ ก็ไปเขียน config ในไฟล์ nodemon.json แทนก็ได้

แบบนี้

{
  "watch": ["src"],
  "ext": "ts",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "ts-node ./src/app.ts" 
  #or 
  "exec": "npx ts-node ./src/app.ts"
}

ใช้ ts-node-dev

หรือใช้อีกวิธีคือลงแพ็กเกจ ts-node-dev

# npm
yarn add ts-node-dev --dev

# yarn
npm i ts-node-dev --save-dev

แล้วก็รันด้วยคำสั่ง

ts-node-dev app.ts

จากที่เราได้รู้จักโลกของ UX UI ไปใน Section ที่แล้ว ใน Section นี้ เราจะมาเข้าใจให้ลึกขึ้นอีกนิด สำหรับ UX UI Design โดยมีคำกล่าวสั้นๆว่า

UX คือ นามธรรม
UI คือ รูปธรรม

และในพื้นฐานแรก ที่เราจะมาเริ่มเรียนรู้คือ UI Design


เคลียร์กันชัดๆ UI Design คืออะไร?

นิยามของ UI Design คือการออกแบบสิ่งที่เชื่อมประสานระหว่างผู้ใช้กับสิ่งใดสิ่งหนึ่ง

ตัวอย่าง :: หากมีไอศกรีมอยู่ 1 ถ้วย ตัวกลางระหว่างตัวคุณกับไอศกรีมคือช้อน ซึ่งในที่นี้ ช้อนเปรียบเสมือน UI ตรงที่ถ้าไอศกรีมถ้วยนี้เป็นทรงเตี้ยช้อนที่ถูกออกแบบมาจะต้องเป็นทรงสั้น หรือถ้าไอศกรีมถ้วยนี้เป็นแบบทรงสูงช้อนก็จะต้องถูกออกแบบมาให้เป็นทรงยาว ดังนี้ การออกแบบที่ดี คือจะต้องออกแบบช้อนให้สะดวกสบายต่อการตักไอศกรีมในถ้วยทรงต่างๆนั้น

ซึ่งในกรณีของ Mobile Application หรือ Website นี้ สิ่งที่เป็นตัวเชื่อมประสานระหว่างคุณกับ Content (เนื้อหาข้อความต่างๆ โดยจะรวมไปถึง ภาพ, วีดีโอ และอื่นๆ) ก็คือ หน้าWebsite / Application

ดังนั้น การออกแบบ UI Design ของ Website หรือ Mobile Application ก็คือการสร้างเส้นทางตัวเชื่อมประสานให้กับผู้ใช้งานเพื่อไปสู่เป้าหมายที่ตั้งไว้ได้สำเร็จ


เคลียร์กันชัดๆ UX Design คืออะไร?

UX Design เป็นสิ่งที่จับต้องไม่ได้ นิยามของ UX Design คือการออกแบบประสบการณ์ของผู้ใช้งาน ซึ่งประสบการณ์เป็นเรื่องของความรู้สึก การที่เราจะเกิดประสบการณ์ต้องเกิดจากการรับรู้ควบคู่ไปกับความรู้สึก

ตัวอย่าง :: ถ้าคุณต้องการดู Content ที่เป็นภาพยนต์ และมี Website เป็นตัวกลางเชื่อมประสาน (หรือก็คือ UI) ดังนั้น UX ก็คือความรู้สึกที่เกิดขึ้น เป็นความรู้สึกหลังจากที่ได้ดูภาพยนต์ จะกลายเป็นประสบการณ์ที่ทำให้เกิดความรู้สึกว่าชอบหรือไม่ชอบ ดีหรือไม่ดี อยากกลับมาใช้งานหรือไม่อยาก

ดังนั้น ประสบการณ์ที่ได้กินไอศกรีมผ่านช้อน หรือความรู้สึกที่ได้ชมภาพยนต์ผ่าน Website ก็คือประสบการณ์ที่ถูกออกแบบขึ้นมา

การออกแบบ UX Design ของ Website และ Mobile Application จะสร้างประสบการณ์ การจดจำ ความรู้สึกที่ดี และนำพาให้ผู้ใช้งานกลับมาใช้งานอีกครั้ง ซึ่งสิ่งเหล่านี้คือ เป้าหมายที่แท้จริงของผู้ออกแบบ


องค์ประกอบที่ต้องเรียนรู้สำหรับการออกแบบ

ในด้านของ UX Design จะเรียนรู้กระบวนการหรือแนวคิดที่เป็นเครื่องมือต่างๆ

  • Design Thinking :: เป็นเครื่องมือหรือกระบวนการที่ให้ความสำคัญกับการทำความเข้าใจผู้ใช้

  • Service Design :: เป็นกระบวนการที่เน้นการออกแบบสำหรับบริการเป็นหลัก โดยมีคอนเซ็ปว่า Don't speak just doing

  • Design Sprint :: เป็นกระบวนการออกแบบที่ได้ถูกพัฒนาขึ้น จุดเด่นคือเน้นความเร็วเป็นสำคัญ โดยจะจบได้ใน 5 วัน แต่มีประเด็นที่สำคัญคือ Design Sprint นั้นจะใช้กับผลิตภัณฑ์หรือบริการที่ออกสู่ตลาดแล้วเท่านั้น

  • User Centered Design :: เป็นแนวคิดในการออกแบบที่มุ่งเน้นให้ความสำคัญกับผู้ใช้งานเป็นหลัก โดยการคิดแก้ปัญหาจะมุ่งเน้นไปที่ผู้ใช้งานเป็นสำคัญ

ในด้านของ UI Design จะเน้นโครงสร้างและองค์ประกอบของการออกแบบ

  • Information Design :: เป็นการออกแบบข้อมูล ที่เน้นการนำเสนอข้อมูลที่สร้างความน่าสนใจ สร้างความเข้าใจ และเกิดการเรียงลำดับของข้อมูลที่ถูกต้อง

  • Interaction Design :: เป็นกระบวนการการออกแบบที่เน้นการตอบโต้หรือการตอบสนองที่ถูกต้อง และนำทางผู้ใช้ไปยังเป้าหมายที่ต้องการ

  • Information Architect :: เป็นเรื่องของการจัดกลุ่มข้อมูล การเรียงลำดับข้อมูล การจัดโครงสร้างของข้อมูล ให้มีความถูกต้องและเข้าใจได้ง่ายเพื่อให้ผู้ใช้งานรู้ลำดับก่อน-หลังการเข้าถึงข้อมูลได้

  • Visual Design :: เป็นการออกแบบที่เน้นการนำเสนอภาพที่มีองค์ประกอบต่างๆ เช่น การจัดเลเอ้าท์, การใช้สี, การใช้ตัวอักษร, การใช้โครงสร้าง, การใช้รูปทรง และรวมไปถึงการใช้พื้นที่ว่างในการนำเสนอภาพให้ผู้ใช้งานได้เข้าใจ

  • Human Computer Interaction :: เป็นศาสตร์การออกแบบที่เฉพาะเจาะจงโดยพูดถึงความสัมพันธ์ระหว่างมนุษย์กับ Computer เมื่อสองสิ่งนี้ต้องมาทำงานร่วมกัน จะมีเอกลักษณ์เฉพาะบางอย่าง ทั้งในเรื่องของความยืดหยุ่น ความหลากหลายการปรับเปลี่ยน โดยปัจจุบันยังมีการพัฒนาอย่างไม่หยุดยั้ง


กรณีตัวอย่างการออกแบบ UX UI สำหรับ Mobile Application

การเรียนรู้ที่ดีที่สุด คือการเรียนรู้จากงานที่สำเร็จเสร็จสิ้นแล้ว

ปัจจุบันมีผู้ใช้งาน Mobile Application เยอะกว่า Website ผ่าน PC Computer มากขึ้น 5 เท่า!!

โดยองค์ประกอบของการออกแบบ Mobile App คือ

  • การออกแบบข้อมูล
  • การออกแบบการตอบสนองหรือการตอบโต้
  • การออกแบบรูปทรง สี และตัวอักษร
    ขึ้นอยู่กับเป้าหมายของผู้ใช้งานเป็นสำคัญ และเนื้อหาที่เราต้องการนำเสนอ

ตัวอย่างการออกแบบ UI ของ Mobile App

ตัวอย่างที่ 1 "Facebook Application" :: การออกแบบหน้าแรกของ Facebook เน้นการเล่าเรื่องราวต่างๆ โดยจะแบ่งออกเป็น 3 ส่วน ดังนี้

ตัวอย่างที่ 2 "NTC Application" :: ซึ่งเป็น App แนะนำวิธีการออกกำลังกายที่มีการออกแบบการจัดหมวดหมู่ที่ดี เพราะเป็น Application ที่มีข้อมูลเยอะมาก ทำให้ต้องแยกข้อมูลให้ง่ายและสะดวกต่อการเข้าถึงของผู้ใช้งาน ดังนี้

ตัวอย่างที่ 3 "TED Application" :: เป็น Application ที่นำเสนอข้อมูลเป็นคลิปวีดีโอต่างๆของงาน TED ที่มีคนหลากหลายอาชีพ มาเล่าประสบการณ์ต่างๆให้ฟัง

ตัวอย่างที่ 4 "Unsplash Application" :: เป็น Application ที่นำเสนอในเรื่องของรูปภาพ ดังนั้นจึงใช้การนำเสนอที่เน้นรูปภาพเป็นหลัก มีการลดรายละเอียดที่เป็นส่วนของข้อความออกไป

ตัวอย่างที่ 5 "Line Man Application" :: นำเสนอด้วยข้อมูลจำนวนมาก เน้นข้อความและย่อรูปภาพให้เล็กลง เน้นการนำเสนอเนื้อหา และ Content เยอะๆ พร้อมๆกัน เพื่อให้ผู้ใช้เห็นบริการทั้งหมดที่มี โดยเราจะเห็นดีไซน์แบบนี้ได้ใน Application ที่เกี่ยวกับ E-Commerce


กรณีตัวอย่างการออกแบบ UX UI สำหรับ Website

สิ่งสำคัญของการออกแบบ Website คือ การออกแบบจุดที่ผู้ใช้ควรสนใจ ดังนั้นการออกแบบ Website ยังคงให้ความสำคัญกับเป้าหมายผู้ใช้งาน และเนื้อหาในการนำเสนอ เป็นสิ่งสำคัญหลัก

ตัวอย่างการออกแบบ UI ของ Website

ตัวอย่างที่ 1 "Facebook" :: เน้นส่วนที่สำคัญตรงกลาง

ตัวอย่างที่ 2 "Netflix" :: นำเสนอด้วยภาพตัวอย่างภาพยนต์ขนาดเล็กจำนวนมากพร้อมๆกัน เพื่อให้ผู้ใช้เห็นข้อมูลได้เยอะ และเลือกดูได้ตามความสนใจ

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


กรณีตัวอย่างการเรียนรู้การออกแบบ UX UI จากความผิดพลาด

  • เกิดจากการที่เราให้ความสำคัญกับข้อมูลไม่ถูกต้อง
  • ไม่สามารถเรียงลำดับความสำคัญของข้อมูลได้

หนึ่งในคำถามที่เว็บโปรแกรมเมอร์มือใหม่ถามกันเยอะมากจนน่าจัดเก็บไว้เป็น FAQ เลยก็คือ

"จะทำยังไงให้เราเรียกใช้ฟังก์ชันภาษา PHP จากสคริป JavaScript ได้"

เช่นแบบนี้...

<button onclick="<?php functionInPhp1(); ?>">
    คลิกฉันสิ!
</button>

หรือแบบนี้...

function functionInJs(){
    let x = <?php functionInPhp2(); ?>
}

คำตอบคือ ด้วยการทำงานของเว็บที่ทำงานบนโปรโตคอล http นั้น...มันทำไม่ได้!! (แต่มีวิธีแก้ ถ้าอยากทำจริงๆ อ่านต่อไป!)

จริงๆ แล้วไม่จำกัดว่าต้องเป็นภาษา PHP เท่านั้นนะ เคสนี้เป็นกับทุกภาษาฝั่งเซิร์ฟเวอร์เลย

เพื่อจะตอบว่าทำไมมันทำไม่ได้ เราจะต้องอธิบายการทำงานของ http กันซะก่อน

http และ Stateless

http เป็นโปรโตคอลมาตราฐานสำหรับการเขียนเว็บ ซึ่งการทำงานของมันสรุปง่ายๆ ได้ดังนี้

โปรโตคอล (Protocol) = กฎที่ตั้งขึ้นมาเพื่อให้คอมพิวเตอร์ในเน็ตเวิร์คสามารถคุยกันด้วยภาษาเดียวกัน ไม่งั้นมันจะคุยกันไม่รู้เรื่อง

  • Client ส่งคำขอหรือ request ไปหา Server
  • เมื่อ Server ได้รับ request แล้วก็จะรันสคริปที่เตรียมไว้ เพื่อหาว่าจะตอบอะไรกลับไปให้ Client, ในสเต็ปนี้ อาจจะมีการเชื่อมต่อเพื่อขอข้อมูลจาก database ด้วย
  • หลังจาก Server คิดเสร็จ ก็จะส่งคำตอบ (อยู่ในรูปแบบของ String) กลับไปให้ Client [มาถึงตรงนี้ถือว่า Server ทำงานเสร็จแล้ว!]
  • Client รับข้อมูลไป (ส่วนใหญ่จะอยู่ในรูปแบบ html) ก็เอาไปแสดงผล แต่ถ้ามีการแนบสคริปมาด้วย ก็จะจัดการรันสคริปตัวนั้นซะในสเต็ปนี้
  • จบจ้า!

สำหรับฝั่ง Client นั้นไม่ค่อยมีปัญหาเพราะมีแค่ภาษาเดียวเท่านั้นที่ครอบครองอยู่ นั่นคือ JavaScript แต่ฝั่ง Server นี่มีหลายภาษามากๆ เช่น PHP, Node.js. Go, .NET

แต่ไม่ว่าฝั่งเซิร์ฟเวอร์จะเขียนด้วยภาษาอะไร หน้าที่ของมันคือส่ง HTML, CSS, JS กลับมาให้ Client เพื่อโปรเซสต่อ

จะเห็นว่า http เป็นโปรโตคอลง่ายๆ ถามมา-ตอบกลับ แล้วก็จบงาน

ซึ่งเราเรียกการทำงานแบบนี้ว่า "Stateless" คือทำงานเป็นรอบ พอเรียกใช้งานและทำงานนั้นเสร็จแล้ว ก็จบการทำงาน (เว็บส่วนใหญ่ทำงานแบบนี้)

ตรงข้ามกับ "Stateful" ที่ตัวโปรแกรมรันค้างอยู่ตลอดเวลา (ให้นึกถึงแอพพลิเคชันมือถือหรือโปรแกรมที่รันใน Desktop)

Server ทำงานก่อน Client เสมอ!

เราอธิบายให้ฟังแล้วว่า http นั้นเริ่มทำงานที่ Server รันเพื่อโปรเซสข้อมูลที่จะส่งกลับมาให้ Client ก่อน ... แล้วพอ Client ได้รับข้อมูล (ซึ่งอาจจะมี JavaScript แนบติดมาให้ด้วย) ก็จะเอาสคริปตัวนั้นมารันต่อ

แสดงว่าในจังหวะที่ Client เริ่มทำงานนั้น ... โค้ดฝั่ง Server ทำงานเสร็จไปแล้ว !

นี่เลยเป็นเหตุผลว่าทำไมเราไม่สามารถทำให้ JavaScript เรียกให้ PHP ทำงานได้

เช่น

<?php
    function registerMeeting(){
        //ต้องการบันทึกว่าเข้าร่วมประชุมแล้ว
    }

    function getOnlines(){
        //ต้องการดึงรายชื่อคนที่กำลังออนไลน์อยู่ในขณะนี้
    }
?>

<button onclick="<?php registerMeeting(); ?>">
    ลงชื่อเข้าประชุม
</button>

<script>
    function getWhoOnlineNow(){
        let onlines = <?php getOnlines(); ?>
    }
</script>

ในโค้ดตัวอย่างนี้เรามีฟังก์ชันในภาษา PHP อยู่ 2 ตัว คือ registerMeeting() และ getOnlines()

จากนั้นเราไปเขียนโค้ด JavaScript เพื่อเรียกใช้งานฟังก์ชัน PHP ทั้งสองตัวนี้ต่อ

แต่ปัญหาก็คือ PHP เป็น Server-Side-Script มันทำงานก่อนเสมอ (ไม่ต้องรอให้ JavaScript เรียกหรอก ฉันทำงานเองตอนนี้เลย!)

แต่ด้วยโลจิคแล้ว เราต้องการให้ฟังก์ชันพวกนี้ทำงานเมื่อเรามี event เท่านั้น (เชื่อ ปุ่มโดน onclick หรือฟังก์ชัน JavaScript ทำงาน)

ดังนั้นโค้ดนี้เลยผิด!

Asynchronous คือทางแก้

หากต้องการให้ JavaScript เรียกให้ PHP ทำงานได้ มีอยู่วิธีเดียวนั้นคือใช้เทคนิคการเขียนโปรแกรมแบบ Asynchronous

หรือถ้าจะเรียกด้วยชื่อแบบดังเดิมคือเทคนิคที่เรียกว่า "Ajax" (Asynchronous JavaScript and XML) ... แต่ปัจจุบันไม่ค่อยเรียกชื่อนี้กันแล้วนะ และเราก็ไม่ได้ใช้แค่ XML เป็นตัวกลางส่งข้อมูลกันอีกตัวไป มีการใช้ JSON เพิ่มเข้ามาด้วย

ในบทความนี้จะไม่สอน Ajax แบบละเอียดนะ ถ้าอยากรู้ตามไปอ่านได้ที่ Ajax คืออะไร แล้วมันใช้ยังไง?

สำหรับ PHP ...

ให้แยกโค้ดส่วนที่ต้องการให้ JavaScript เรียกใช้งานได้ออกมาอยู่อีกไฟล์หนึ่ง เช่นกรณีนี้ตั้งชื่อไฟล์ว่า async-handler.php

<?php
//FILE: async-handler.php

function registerMeeting(){
    //ต้องการบันทึกว่าเข้าร่วมประชุมแล้ว
}

function getOnlines(){
    //ต้องการดึงรายชื่อคนที่กำลังออนไลน์อยู่ในขณะนี้
}

switch($_GET['action']){
    case 'register-meeting': 
        registerMeeting(); 
        break;
    case 'get-online-user': 
        getOnlines(); 
        break;
}

และเนื่องจากเรามี action ที่ต้องการให้ทำงานมากกว่า 1 ตัว ก็เลยใช้ query-string ชื่อว่า action เป็นตัวแยกว่า request ครั้งนี้ต้องการให้ฟังก์ชันไหนทำงาน (ตรงนี้ตั้งชื่อว่าอะไรก็ได้นะ แล้วแต่เลย)

<button onclick="registerMeeting()">
    ลงชื่อเข้าประชุม
</button>

<script>
    function registerMeeting(){
        fetch('/async-handler.php?action=register-meeting')
    }

    function getWhoOnlineNow(){
        fetch('/async-handler.php?action=get-online-user')
        .then(onlines => {
            ...
        })
    }
</script>

จากนั้น ในฝั่ง JavaScript ก็ใช้การ request กลับไปยังไฟล์ PHP async-handler.php ที่เตรียมไว้อีกรอบ จะใช้คำสั่ง fetch() หรือไลบรารี่ Axios หรือถ้าเก่าหน่อยก็ใช้ $.ajax() ของ jQuery ก็ตามสะดวกเลย

สรุป

การจะให้ JavaScript เรียกคำสั่ง PHP ตรงๆ นั้นทำไม่ได้ เพราะกว่า JavaScript จะเริ่มทำงาน PHP ก็ทำงานจนเสร็จไปก่อนแล้ว

แต่มีวิธีการแก้ทางคือสร้าง request ขึ้นมาอีกครั้งเพื่อเรียกไปหา Server ให้ปลุก PHP ขึ้นมาทำงานอีกรอบหนึ่ง

คือต้อง request 2 ครั้ง

  • ครั้งแรก - ให้ Server ส่ง HTML, CSS, JS มาให้ก่อน
  • ครั้งที่สอง - JavaScript สร้าง request อีกครั้ง เรียกไปยังไฟล์ PHP ที่เตรียมเป็น asynchronous handler เอาไว้

วันนี้ผมจะมาแนะนำและวิธีการใช้งานเบื้องต้นของ plugin ที่น่าสนใจตัวหนึ่งที่มีชื่อว่า
"Google Maps for Flutter"
โดย plugin ตัวนี้จะให้ Google Maps ที่เป็น Widget มาให้เราได้เปิดใช้งานแผนที่ของกูเกิ้ล

ขั้นตอนการติดตั้ง

อันดับแรก เราต้องทำการขอ API Key ที่ลิ้งค์ https://cloud.google.com/maps-platform/
เมื่อเข้ามาหน้าเว็บไซต์แล้วให้เข้าไปที่ Console (ตรงขวามุมบนของหน้าจอ)

สร้าง Project ของเราขึ้นมาก่อน

เมื่อทำการสร้างเสร็จแล้วให้เปิดแท็บด้านขวามือ แล้วเลือกเมนูที่ชื่อว่า Google Maps

Google Maps จะมี​ API ให้ใช้หลายตัว เราสามารถเข้าไป Enable เพื่อที่จะเรียกใช้ API เหล่านั้นได้
แต่ตอนนี้เราจะเลือก Enable Maps SDK for Android / IOS เพื่อที่จะเอา API Key ไปเรียกใช้งาน API เหล่านี้

ต่อมาให้เข้าไปที่เมนู Credentials เพื่อที่จะสร้าง API Key เอาไปแปะในโค้ดให้เชื่อมต่อกับ Google Cloud Platform
กดเลือก Create Credentials -> API Key

เราก็จะได้ API Key มาเท่านี้ก็จะสามารถเชื่อมต่อกับ Google Map Api ได้

มาในส่วนของการเขียนโค้ดกันบ้าง

สำหรับ Android ให้เข้าไปเพิ่ม API Key ที่ไฟล์ AndroidManifest.xml ในโฟลเดอร์
Android (android/app/src/main/AndroidManifest.xml)

.
└── android
    └── app
        └── src
            └── main
                └── AndroidManifest.xml

ให้เพิ่ม เพื่อขอ permission กับ เพื่อเพิ่ม API KEY เข้าไป

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fluttergooglemap">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="fluttergooglemap"
        android:icon="@mipmap/ic_launcher">

        <meta-data android:name="com.google.android.geo.API_KEY"
            android:value="YOUR API KEY"/>

        <activity
            android:name=".MainActivity"
            ...

สำหรับ IOS เข้าไปที่ไฟล์ AppDelegate.swift (ios/Runner/AppDelegate.swift)

.
└── ios
    └── Runner
        ├── AppDelegate.swift
        └── Info.plist

เพิ่ม API KEY เข้าไป GMSServices.provideAPIKey("YOUR KEY HERE")

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
  GMSServices.provideAPIKey("YOUR API KEY")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

และเข้าไปที่ไฟล์ info.plist เพิ่มคำส่ัง เพื่อขอ permission

<dict>
     <key>NSLocationWhenInUseUsageDescription</key>
          <string>This app needs your location to test the location feature of the Google Maps plugin.</string>
          <key>io.flutter.embedded_views_preview</key>
          <true/>
     ...

เข้าไปที่ไฟล์ pubspec.yaml
ติดตั้ง google_maps_flutter: ^0.5.28+1 (เวอร์ช้่นล่าสุดเมื่อ May 21, 2020)

dependencies:
  flutter:
    sdk: flutter

  google_maps_flutter: ^0.5.28+1

  ...

import Libray

import 'package:google_maps_flutter/google_maps_flutter.dart';

สร้างตัวแปรประเภท GoogleMapController คือ class ที่เอาไว้สร้าง Future อีกที เอาไว้ควบคุมการทำงาน

class _MyHomePageState extends State<MyHomePage> {

  Completer<GoogleMapController> _controller = Completer();

Library จะมี Widget มาให้ชื่อว่า GoogleMap()

ทดลองพิมพ์ตามโค้ดด้านล่างนี้

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: LatLng(13.757429, 100.502465), //กำหนดพิกัดเริ่มต้นบนแผนที่
          zoom: 15, //กำหนดระยะการซูม สามารถกำหนดค่าได้ 0-20
        ),
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
      ),
    );
  }

เริ่มต้น GoogleMap จะต้อง initialCameraPosition เสมอ โดยกำหนดพิกัดจุดเริ่มต้นสถานที่จะให้ GoogleMap แสดงขึ้นมา แล้วลอง Run

เพียงเท่านี้ก็จะสามารถเปิดกูเกิ้ลแมพได้ บทความนี้จะเป็นการติดตั้งเบื้องต้น และบทความต่อไปจะเป็นการใช้งานเบื้องต้นเกี่ยวกับ GoogleMap, Polyline, Marker

หากเปรียบเทียบการออกแบบ UX UI เป็นประตู ดังนั้น Designer คือผู้ที่ต้องแก้ไขปัญหา เพื่อให้ตอบโจทย์ผู้ใช้ ไม่ใช่คำนึงแค่ความสวยงามเท่านั้น โดยใน Sec.0 นี้ เราจะมองภาพง่ายๆแบบใกล้ตัว เช่น การออกแบบประตู ซึ่งการออกแบบ เราจะมีพื้นฐานด้วยกัน 4 อย่าง ดังนี้


พื้นฐานการออกแบบมี 4 อย่าง คือ

  • การออกแบบหน้าตา (User Interface Design) :: หน้าตาของประตู ส่วนใหญ่จะเป็นรูปสี่เหลี่ยม เพื่อง่ายต่อการใช้งาน ง่ายต่อการติดตั้ง แต่บางกรณีก็จะมีรูปทรงต่างๆ เช่น ทรงกลม แต่ก็ต้องยอมรับว่า รูปทรงง่ายๆอย่างสี่เหลี่ยม เป็นรูปทรงที่นิยมใช้กันมากที่สุด

ดังนั้นการออกแบบหน้าตาก็คือ องค์ประกอบ, ขนาด, รูปแบบ และ สิ่งที่ผู้ใช้สัมผัส

  • การออกแบบการสื่อสาร (Information Design) :: สีของประตู ถ้าอยู่ในบ้านจะให้ความรู้สึกสบาย โดยส่วนใหญ่มักใช้สีอ่อนๆ หรือสีธรรมชาติ และหากเป็นประตูที่มีความหมายเฉพาะเจาะจง ก็จะใช้สีสัน ข้อความ หรือรูปภาพติดที่ประตู เพื่อให้เห็นได้อย่างง่ายดาย เช่น ประตูหนีไฟที่มีสีแดง

ดังนั้นการออกแบบการสื่อสารก็คือ สี, ข้อความ และ สิ่งที่ต้องการสื่อกับผู้ใช้

  • การออกแบบการตอบสนอง (Interaction Design) :: การออกแบบว่าการหมุนลูกบิดเพื่อเปิดประตู หรือการผลักประตูเพื่อให้เปิดออก และดันกลับเข้ามาเพื่อให้ประตูปิด

ดังนั้นการออกแบบการตอบสนองก็คือ ผู้ใช้งานต้องสามารถใช้งานตามความเข้าใจได้อย่างถูกต้อง

  • การออกแบบประสบการณ์ (User Experience Design) :: เป็นสิ่งที่ไม่มีรูปร่าง เป็นเรื่องของความรู้สึก ความพอใจและการเข้าใจ

โดยทั้งหมดนี้จะต้องสัมพันกันทั้งหมด เมื่อต้องการออกแบบหน้าตา เราก็จำเป็นจะต้องรู้และเข้าใจ การออกแบบการสื่อสาร และเมื่อเรา ต้องการออกแบบการสื่อสาร เราต้องคำนึงถึง การออกแบบการตอบสนอง และสุดท้ายทั้งหมดจะเข้าไปเกี่ยวพันกับ การออกแบบประสบการณ์


การออกแบบ UX UI สำหรับเครื่องคอมพิวเตอร์

ออกแบบมาจากเทคโนโลยีเก่าๆ เช่น หน้าจอคอมพิวเตอร์ ถูกออกแบบดัดแปลงมาจากหน้าจอทีวี และแป้นพิมพ์ ก็ถูกออกแบบมาจากแป้นพิมพ์ดีด ซึ่งที่กล่าวมานี้คือการลดการเรียนรู้ของผู้ใช้งาน ไม่ต้องทำความเข้าใจใหม่ ทำให้รู้สึกง่าย และรับรู้การใช้งานได้อย่างรวดเร็ว และคอมพิวเตอร์ จะประกอบไปด้วยเทคโนโลยีด้วยกัน 2 แบบ ดังนี้

  • เทคโนโลยี AI :: ตัวอย่างเช่น Siri หรือ Google Home เป็นต้น ซึ่งเทคโนโลยีนี้ จะใช้เสียงเป็นตัวประสาน สามารถสั่งงานได้ด้วยเสียง หรือเรียกว่า Voice User Interface

  • เทคโนโลยี VR/AR :: โดย VR จะเน้นภาพเสมือนจริง แต่ไม่ใช่ของจริง สื่อสารด้วยภาพเสมือนจริงแบบ 3มิติ โดยมีอุปกรณ์เสริม สำหรับสร้างประสบการณ์เพิ่ม และ AR คือการเอาสิ่งที่ถูกสร้างขึ้นในโลกของดิจิตอลมาซ้อนทับกับโลกแห่งความจริง เพื่อให้ภาพปรากฎขึ้น เสมือนว่าสิ่งๆนั้นเกิดขึ้นจริง


การออกแบบ UX UI สำหรับSmart Phone

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

  • การออกแบบหน้าตา (User Interface Design) :: รูปร่างและขนาดที่จับถนัดมือ รองรับการใช้งานแบบมือเดียว

  • การออกแบบการสื่อสาร (Information Design) :: มีปุ่มน้อย เน้นการใช้ระบบสัมผัส

  • การออกแบบการตอบสนอง (Interaction Design) :: ออกแบบอย่างหลากหลาย มีการตอบสนองหลากหลาย เช่น การสั่นหรือใช้เสียงเพื่อแจ้งเตือน และการปลดล็อกด้วยการสแกนลายนิ้วมือ หรือใบหน้า เป็นต้น

  • การออกแบบประสบการณ์ (User Experience Design) :: Smart Phoneถือเป็นประสบการณ์ใหม่ และแตกต่างจากคอมพิวเตอร์ได้เป็นอย่างดี สามารถพกพาไปไหนก็ได้ ง่าย และ สะดวกสบาย


การออกแบบ UX UI สำหรับโปรแกรมคอมพิวเตอร์

หลักการออกแบบของMobile App / Websiteนั้น มีหลักการดังนี้

  • การออกแบบหน้าตา (User Interface Design) :: ต้องคำนึงถึงขนาดของหน้าจอแสดงผล ซึ่งมีความหลากหลาย ต้องให้ความสำคัญของการจัดวาง รูปทรง โครงสร้าง สี ซึ่งจะส่งผลต่อการใช้งาน เช่น ส่วนใดไว้ดู ส่วนใดไว้พิมพ์ หรือส่วนใดที่กดได้

  • การออกแบบการสื่อสาร (Information Design) :: ต้องคำนึงถึงการใช้ภาพ การใช้ข้อความ และการใช้คำเพื่อการสื่อสาร ต้องให้ผู้ใช้เข้าใจว่าต้องทำอะไร

  • การออกแบบการตอบสนอง (Interaction Design) :: ต้องคำนึงการตอบสนองที่ถูกต้อง ให้ผู้ใช้เข้าใจว่าปุ่มนี้ใช้สำหรับทำอะไร และผลลัพธ์ที่เกิดขึ้นก็ต้องถูกต้อง

  • การออกแบบประสบการณ์ (User Experience Design) :: ต้องคำนึงถึงประสบการณ์ทั้งความรู้สึก ความคิด การตัดสินใจ การลงมือทำ เช่น พอเข้ามาแล้ว ผู้ใช้สามารถรู้หรือไม่ว่าแอพนี้ใช้ทำอะไร เพื่ออะไร ระหว่างการใช้งานยุ่งยากหรือไม่ ไม่ใช่แค่สวยงามหรือเข้าใจง่าย แต่ต้องได้คุณค่า และผลลัพธ์ที่ดี


ประโยชน์ของการออกแบบ UX UI ที่ดี

ประโยชน์ของการออกแบบ UX UI ที่ดี จะแบ่งออกเป็น 2 อย่างคือ

  • ถูกต้อง :: โดยจะดูที่การใช้งาน ว่าง่ายหรือไม่ สะดวกหรือไม่ ประสบความสำเร็จหรือไม่ ถึงขั้นตอนไหนแล้ว หรือถ้ากรณีที่มีปัญหา จะแก้ไขอย่างไร เมื่อใช้งานเสร็จผู้ใช้ได้ผลลัพธ์ตามที่ต้องการหรือไม่

  • ถูกใจ :: โดยจะดูความพึงพอใจ ความสุขของผู้ใช้ ประสบกาณ์ที่ได้ เมื่อใช้แล้วอยากกลับมาใช้ซ้ำหรือไม่ สร้างความกังวลหรือเกิดความกังวลกับผู้ใช้มากน้อยแค่ไหน

สรุปสั้นๆคือ ประโยชน์ของ UX UI ที่ดีคือ

  • สร้างกระบวนการการทำงานที่ถูกต้อง เพื่อให้เกิดผลลัพธ์ที่ดี
  • สร้างความถูกใจเพื่อให้เกิดความจดจำและอยากกลับไปใช้งานซ้ำ

โทษของการออกแบบ UX UI ที่ไม่ดี

  • เสียเวลา :: ใช้เวลามากในการเรียนรู้ หรือเข้าใจกับสิ่งที่ออกแบบ

  • เสียแรง / เสียกำลังสมอง :: ถ้าการออกแบบไม่ดีจะทำให้ ผู้ใช้งานต้องคิดซับซ้อน ต้องคาดเดา วิเคราะห์มากขึ้น และสูญเสียพลังสมอง ซึ่งจะส่งผลต่ออารมณ์ และความรู้สึก ทำให้รู้สึกไม่ดี และไม่อยากกลับมาใช้ซ้ำ

  • เสียโอกาสและทรัพย์สิน :: การสื่อสารที่ผิดหรือไม่ชัดเจน จะทำให้เกิดความผิดพลาด รู้สึกไม่ปลอดภัย และผู้ใช้จะไม่กลับมาใช้ซ้ำอีก

  • เสียความรู้สึก :: เมื่อผู้ใช้รู้สึกเสียความรู้สึกอย่างต่อเนื่อง จะทำให้ผู้ใช้ตัดสินใจไม่ใช้งานแอพหรือเว็บไซต์ของเรา


ทำไมต้องเรียนรู้การออกแบบ UX UI Design

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


และตอนนี้เราก็ได้รู้เรื่องราวคร่าวๆของ UX UI ไปบ้างแล้ว และในSectionต่อไป เราจะมาเริ่มเรียนรู้ UX/UI Design แบบจริงจังกันค่ะ!

วิธีสร้างปุ่ม Login Facebook บน WordPress ด้วยปลั๊กอิน Nextend Social Login Plugin

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

ต้องยอมรับว่าการ Login ด้วย Social นั้นเป็นสิ่งจำเป็นขั้นพื้นฐานไปแล้วสำหรับเว็บไซต์ปัจจุบันนี้ เพราะไม่ต้องเสียเวลากรอกแบบฟอร์มการสมัครสมาชิกของเว็บไซต์ ที่บางเว็บก็ต้องกรอกข้อมูลเยอะมาก จะดีกว่ามัย ถ้าเเว็บของเราสามารถ Login ผ่าน Social account ได้เลย เพราะฉะนั้นบทความนี้จะมาจะมาสอนวิธีการ Login Facebook ด้วยปลั๊กอิน Nextend Social Login ที่เป็นปลั๊กอินฟรี ติดตั้งง่าย และใช้งานได้จริง

  1. โหลด Plugin Nextend Social Login โหลดที่ https://th.wordpress.org/plugins/nextend-facebook-connect/
    และปิดใช้งาน

  2. หลังจากกดใช้งานเรียบร้อยแล้ว ไปที่เมนู settings > Nextend Social Login > กด Getting Started ที่ใต้รูป Facebook

  3. สร้าง Facebook App ไปที่ https://developers.facebook.com/apps/ คลิกที่ "เพิ่มแอพใหม่"

เลือก “จัดการผสานธุรกิจ”

  1. ใส่รายละเอียด ตั้งชื่อ App และใส่ email คลิก สร้าง ID ของแอพ

  1. คลิกที่ ตั้งค่า ใต้ “การเข้าสู้ระบบ Facebook”

  1. เลือก www สำหรับทำ login บน เว็บไซต์

  1. ใส่ link เว็บไซต์ กด save และคลิก ดำเนินการต่อ

  2. ไปที่เมนู การตั้งค่า > ข้อมูลพื้นฐาน

  3. ใส่รายละเอียดข้อมูลของเว็บไซต์

  4. เลื่อนลงมาข้างล่างส่วน เว็บไซต์ ให้ใส่ link เว็บไซต์ และ คลิกที่ บันทึกการเปลี่ยนแปลง

  1. กลับไปที่หน้าตั้งค่าปลั๊กอิน Nextend Social บนเว็บของเรา ไปที่หัวข้อ Getting Started ดูหัวข้อที่ 13 ให้คัดลอก URL นั้น

  2. คลิกที่

  3. กลับไปที่ App Facebook ไปที่เมนู การเข้าสู่ระบบ Facebook > การตั้งค่า

  1. ตั้งค่า OAuth ตามนี้พร้อมใส่ link ที่ copy มาจากข้อ 11 มาใส่ และ กด บันทึกการเปลี่ยนแปลง

  2. กลับไปที่เมนู “ข้อมูลพื้นฐาน”

  1. คลิกเปิดใช้งาน App

  2. Copy ในส่วนของ ID ของแอพ และ ข้อมูลลับของแอพ

  3. ใส่ที่เว็บของเรา

  4. คลิกที่ Verify Settings

  1. จะได้แบบนี้ แสดงว่าเสร็จสมบูรณ์เเล้ว !!

  1. ทำการบันทึก

ภาษา JavaScript นั้นเป็นภาษาที่เริ่มเขียนได้ไม่ยาก ยิ่งถ้าใครเขียนภาษาสาย Structure/OOP เช่น C/C++, C#, Java มาก่อน อาจจะชอบในความเป็น Dynamic Type เราสร้างตัวแปรแล้วเก็บค่าอะไรก็ได้ ทำให้มีความคล่องตัวในการเขียนมากๆ ก่อนที่พอเขียนไปเรื่อยๆ เราจะพบความแปลกของมัน ยิ่งเขียนไปนานๆ เราก็พบว่ามันเป็นภาษาที่ทำให้เกิดบั๊กได้ง่ายมาก และเป็นหนึ่งในภาษาที่น่าปวดหัวที่สุดที่คนโหวตๆ กันเลย ด้วยเหตุผลแรกคือการที่ตัวแปรไม่มี Type นั่นเอง (อีกเหตุผลหนึ่งคือส่วนใหญ่ของคนที่เปลี่ยนมาเขียน JavaScript เคยชินกับภาษาแนว OOP มาก่อน พอมาเขียนภาษาแนว Functional เลยงงหน่อยๆ)

หลักการคือคอมไพล์ออกมาเป็น JavaScript

เอาจริงๆ ตัวภาษา TypeScript เอาไปรันอะไรไม่ได้เลย

แต่เราใช้วิธีการคอมไพล์ไฟล์ .ts ให้ออกมาเป็นไฟล์ .js อีกที หน้าที่ของมันมีแค่นั้นเอง

หรือบางกรณีถ้าเราเขียน React เราอาจจะรู้จักกับ .jsx สำหรับ TypeScript ก็มีไฟล์นามสกุล .tsx ให้ใช้เช่นกัน

Installation มาติดตั้ง TypeScript กัน

สำหรับ TypeScript Compiler นั้นสามารถติดตั้งได้ทั้งผ่าน npm หรือ yarn ด้วยคำสั่งประมาณนี้

npm install -g typescript

พอติดตั้งแล้วเราสามารถใช้คำสั่ง tsc สำหรับคอมไพล์ไฟล์ TypeScript ได้เลย

วิธีนี้เป็นการติดตั้งแบบ global คือติดตั้งครั้งเดียว ใช้ได้ทุกที่เลย แต่ถ้าอยากติดตั้งแต่ในโปรเจคๆ ไปก็สามารถทำได้ด้วย

npm install typescript --save-dev

หรือจะใช้งานผ่าน npx ก็ได้เช่นกัน

npx tsc

flag --save-dev หรือตัวย่อ -D ใส้ไว้เพื่อระบุว่าเราใช้แพ็กเกจนี้เฉพาะตอน develop เท่านั้น เมื่อไหร่ที่จะ build โปรเจคเอาไปใช้จริง ไม่ต้องเอาติดไปด้วยนะ เพราะอย่างที่บอกคือ TypeScript นั้นตัวมันไม่ได้เอาไปรัน แต่คอมไพล์เป็น JavaScript เมื่อไหร่ที่เขียนเสร็จแล้ว ก็ไม่ต้องใช้ต่อแล้ว

คอมไพล์ไฟล์ TypeScript

เริ่มแรกลองมาสร้างไฟล์ TypeScript กันดีกว่า

//app.ts

function sum(n: number): number {
    let sum: number = 0
    for(let i = 0; i < n; i++){
        sum += i
    }
    return sum
}

const total: number = sum(100)

console.log(total)

เขียนโค้ดนี้แล้วเซฟเป็นไฟล์ชื่อ app.ts ไว้ จะเห็นว่า syntax ทั้งหมดนั้นเหมือนกัน JavaScript ทุกอย่าง ที่เพิ่มเติมมาก็แค่หลังชื่อตัวแปร ตอนที่ประกาศตัวแปรและ parameter เราจะต้องกำหนดชนิดตัวแปรลงไปด้วย โดยใช้ตัว : ตามแพทเทิร์นนี้

variable-name: type

ไทป์ที่ใช้ได้ก็เช่น

let decimal: number = 6
let hex: number = 0xf00d
let binary: number = 0b1010
let octal: number = 0o744

let color: string = "blue"

let isDone: boolean = false

สำหรับตัวแปรประเภท Array นั้นประกาศได้ 2 แบบคือ

let list: number[] = [1, 2, 3]
let list: Array<number> = [1, 2, 3]

ส่วนตัวแปรแบบ Object นั้นจะต้องสร้าง interface เพื่อกำหนดไทป์ขึ้นมาก่อน

interface Point {
    x: number
    y: number
}

let myPoint: Point = { x: 10, y: 20 }

สำหรับ TypeScript นั้น เราไม่สามารถสร้างตัวแปรแบบไม่กำหนดไทป์ได้ แต่ถ้าเราไม่รู้ไทป์จริงๆ เราสามารถประกาศไทป์ any ได้ (แต่ก็ไม่แนะนำให้ทำนะ)


จากนั้นเราใช้คำสั่ง tsc ในการคอมไพล์ไฟล์ TypeScript ของเรา

tsc app.ts

หลังจากคอมไพล์เสร็จแล้ว เราจะได้อีกไฟล์เพิ่มมาในชื่อเดียวกันแต่เป็นนามสกุล .js แทน

เมื่อเราเอามาเปิดดูก็จะพบว่ามันมีหน้าตาคล้ายๆ กับโค้ด TypeScript ที่เราเขียนไป แต่ชนิดตัวแปรที่เคยประกาศจะหายไป และอาจจะมีอะไรอีกนิดหน่อยด้วย (เช่นมี ; เพิ่มเข้ามาให้ จากที่เคยใช้ let, const ก็เปลี่ยนเป็น var) ซึ่งโดยร่วมไม่ได้ทำให้โลจิคเปลี่ยนไปนะ

// app.js

function sum(n) {
    var sum = 0;
    for (var i = 0; i < n; i++) {
        sum += i;
    }
    return sum;
}
var total = sum(100);
console.log(total);

ซึ่งจุดนี้อาจจะทำให้คนที่เพิ่งเริ่มใช้ TypeScript สงสัยว่าถ้ามันแค่ตัด Type ทิ้งไป แล้วทำไมเราไม่เขียน JavaScript ตรงๆ ไปเลยล่ะ?

เช่นผลคือ TypeScript สามารถช่วยเราในส่วนของการเช็ก Compiler-Time Error ได้

เช่นโค้ดนี้

เราประกาศฟังก์ชันที่ต้องรับ number แต่เวลาเรียกใช้งาน ดันใส่ string เข้าไป ถ้าเป็น JavaScript จะสามารถนำไปรันได้ (แต่ผลออกมาจะเกิด Runtime-Error หรือมีโลจิคผิดพลาดตรงไหนจะไม่สามารถรู้ได้เลย)

แต่สำหรับ TypeScript นั้นจะแจ้งออกมาตั้งแต่ตอนสั่งคอมไพล์เลยว่าเรามีการเขียนผิดพลาดเกิดขึ้นนะ และ Editor ส่วนใหญ่ก็สามารถแสดงผลข้อผิดพลาดพวกนี้แบบ real time ได้อยู่แล้วด้วย ทำให้ไม่ต้องเสียเวลาพิมพ์ไปซะนาน แต่พอเอามารันเพิ่งจะรู้ว่าพิมพ์ผิดไป

เริ่มสร้างโปรเจคกันเถอะ

ในโปรเจคขนาดใหญ่ แน่นอนว่าเราไม่ได้มีแค่ไฟล์ sourcecode แค่ไฟล์สองไฟล์ แต่อาจจะมีเป็นร้อยๆ ไฟล์ การจะมานั่งคอมไพล์ TypeScript ทีละไฟล์นั้นทำไม่ได้แน่นอน (จริงๆ ก็ทำได้ แต่ใครจะทำนะ !)

การสร้างโปรเจคเลยเป็นทางเลือกที่ดีกว่า เช่นเราอาจจะสร้างโฟลเดอร์ /src สำหรับเอาไว้เก็บไฟล์ .ts ทั้งหมด และสร้างโฟลเดอร์ /dist หรือ distribute เอาไว้เก็บไฟล์ JavaScript

.
├── src
│   ├── app1.ts
│   ├── app2.ts
│   ├── app3.ts
│   └── ...
└── dist
    ├── app1.js
    ├── app2.js
    ├── app3.js
    └── ...

ซึ่งสำหรับ TypeScript เราสามารถใช้คำสั่งนี้

tsc --init

เพื่อกำหนดว่าโฟลเดอร์นั้นจะเป็นโปรเจค TypeScript ได้ หลังจากสั่งคำสั่งนั้นแล้ว เราจะได้ไฟล์ tsconfig.json มา ภายในไฟล์จะมี config สำหรับโปรเจคอยู่ หน้าตาประมาณนี้

รายละเอียดการเซ็ตค่า tsconfig สามารถดูได้ที่เว็บไซต์ของ TypeScript

ปกติการตั้งค่าหลักๆ ก็จะใช้ของที่ TypeScript สร้างมาให้ ยกเว้นบางตัวที่เราต้องเซ็ตเอง เช่น

{
  "compilerOptions": {

    // สั่งให้คอมไพล์ออกมาเป็น JavaScript ES6
    "target": "es6",

    // ชื่อโหลเดอร์ที่ output ไฟล์ JavaScript ที่คอมไพล์แล้ว
    "outDir": "./dist",

    // ชื่อโฟลเดอร์ sourcecode ไฟล์ TypeScript
    "rootDir": "./src",

    // หากใช้งานกับ React คือมีไฟล์ .tsx ให้คอมไพล์เป็น .jsx ด้วย
    "jsx": "react",

    // หากใช้กับ node
    "moduleResolution": "node",
  },

  // กำหนดขอบเขตของไฟล์ที่จะให้คอมไพล์
  // เช่น ทุกไฟล์ที่นามสกุล .ts ในโฟลเดอร์ไหนก็ได้ใต้ /src
  "include": [
      "src/**/*.ts"
  ],

  // กำหนดไฟล์และโฟลเดอร์ที่ไม่ต้องคอมไพล์ 
  // เช่นโฟลเดอร์ node_modules หรือไฟล์ spec
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}

จากนั้นเวลาเราจะคอมไพล์ ก็สั่งแค่

tsc

มันก็จะเป็นการคอมไพล์ทุกไฟล์ในทั้งโฟลเดอร์ /src/ ที่เรากำหนดไว้

แต่นี่ก็เป็นการคอมไพล์แค่ครั้งเดียว ถ้าเรามีการแก้ไขไฟล์ TypeScript เพิ่ม เราก็ต้องสั่งคอมไพล์อีกรอบ ถ้าไม่อยากจะมาสั่งคอมไพล์ทุกรอบที่แก้ไขโค้ด เราสามารถสั่งคอมไพล์แบบ watch ได้

tsc --watch

คำสั่งนี้จะเป็นการสั่งให้คอมไพล์พร้อมกับ watch หรือคอยดูไฟล์ทุกไฟล์เอาไว้ด้วย ถ้ามีการเปลี่ยนแปลงให้คอมไพล์ใหม่ทันทีโดยเราไม่ต้องสั่งอีกรอบ