เซสชั่น

บทแทรก 5.5

แปลไปแล้ว

ในบทนี้ คุณจะได้

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

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

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

    เซสชันของ Meteor

    ในเวอร์ชันนี้แอพ Microscope ได้ข้อมูลสถานะปัจจุบันของผู้ใช้งานมาจาก URL ที่กำลังเปิดใช้งานอยู่ (รวมทั้งที่อยู่ในฐานข้อมูล)

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

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

    การเปลี่ยนค่า Session

    เซสชั่นสามารถเรียกใช้ได้จากทุกๆที่ในไคลเอนต์ด้วยอ็อบเจกต์ Session โดยมีวิธีการตั้งค่าให้ session ดังนี้

     Session.set('pageTitle', 'A different title');
    
    Browser console

    คุณสามารถอ่านค่ากลับมาได้ด้วยคำสั่ง Session.get('mySessionProperty'); และเนื่องจากมันเป็นข้อมูลชนิดรีแอคทีฟ ก็หมายความว่า ถ้าคุณใส่มันไว้ในตัวช่วยเทมเพลท คุณก็จะเห็นผลลัพธ์ที่ได้จากตัวช่วยนี้มีการเปลี่ยนแปลงตามค่าของตัวแปรเซสชันที่เปลี่ยนไป

    ลองทำตามนี้ โดยเพิ่มโค้ดข้างล่างลงไปในเทมเพลตเลย์เอาท์

    <header class="navbar navbar-default" role="navigation">
        <div class="navbar-header">
            <a class="navbar-brand" href="{{pathFor 'postsList'}}">{{pageTitle}}</a>
        </div>
    </header>
    
    client/templates/application/layout.html
    Template.layout.helpers({
      pageTitle: function() { return Session.get('pageTitle'); }
    });
    
    client/templates/application/layout.js

    โค้ดในบทแทรก

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

    การรีโหลดโค้ดอัตโนมัติของ Meteor (hot code reload หรือ HCR) จะคงค่าเซสชันไว้ ดังนั้นคุณก็ควรจะเห็นข้อความ “A different title” แสดงในส่วน nav bar แต่ถ้าไม่เป็นแบบนั้น ให้คุณพิมพ์คำสั่ง Session.set() ข้างบนอีกครั้ง

    ยิ่งไปกว่านั้น ถ้าเราเปลี่ยนค่ามันอีกครั้ง (ในคอนโซลของเบราว์เซอร์) เราก็ควรจะเห็นอีกข้อความเช่นกัน

     Session.set('pageTitle', 'A brand new title');
    
    Browser console

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

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

    การเปลี่ยนแปลงที่เหมือนเดิม

    ถ้าคุณแก้ไขค่าของตัวแปรเซสชันด้วยคำสั่ง Session.set() แต่คุณยังใช้ค่าเหมือนเดิม Meteor ก็ฉลาดพอที่จะมองข้ามห่วงโซ่รีแอกทีฟนี้ และไม่เรียกใช้ฟังก์ชันโดยไม่จำเป็น

    รู้จักกับ Autorun

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

    สมมุติว่าเรามีโค้ดบางส่วนในแอพเป็นแบบนี้

    helloWorld = function() {
      alert(Session.get('message'));
    }
    

    ซึ่งเรากำลังเรียกใช้ตัวแปรเซสชันอยู่ โดยโค้ดส่วนที่เรียกใช้มันไม่ได้เป็นรีแอคทีฟ นั่นหมายความว่าเมื่อเราเปลี่ยนค่าตัวแปร เราจะไม่ได้เห็นข้อความ alert เกิดขึ้นทุกครั้ง

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

    ลองพิมพ์โค้ดนี้เข้าไปที่คอนโซลของเบราว์เซอร์ดู

     Tracker.autorun( function() { console.log('Value is: ' + Session.get('pageTitle')); } );
    Value is: A brand new title
    
    Browser console

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

     Session.set('pageTitle', 'Yet another value');
    Value is: Yet another value
    
    Browser console

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

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

    Tracker.autorun(function() {
        alert(Session.get('message'));
    });
    

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

    การรีโหลดโค้ดอัตโนมัติ

    ในระหว่างที่เรากำลังสร้าง Microscope กันนั้น เราได้ใช้คุณสมบัติหนึ่งในหลายๆอย่างของ Meteor ที่ช่วยลดเวลาการทำงานได้ นั่นก็คือ การรีโหลดอัตโนมัติ (hot code reload หรือ HCR) โดยเมื่อไรก็ตามที่เราบันทึกซอร์สโค้ด Meteor จะตรวจพบการเปลี่ยนแปลงนั้น และทำการรีสตาร์ทเซิร์ฟเวอร์ Meteor ขึ้นใหม่ แล้วบอกให้ไคลเอนต์ทำการรีโหลดหน้านั้นอีกครั้ง

    ซึ่งดูไปจะคล้ายการรีโหลดอัตโนมัติของหน้าเพจ แต่มีความแตกต่างกันในเรื่องสำคัญอยู่อย่างนึง

    เพื่อหาคำตอบเรื่องนี้ ให้เริ่มด้วยการรีเซ็ทค่าของตัวแปรเซสชันที่เราใช้มาก่อนหน้านี้

     Session.set('pageTitle', 'A brand new title');
     Session.get('pageTitle');
    'A brand new title'
    
    Browser console

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

     Session.get('pageTitle');
    'A brand new title'
    
    Browser console

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

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

    ตอนนี้ลองเช็คดูว่า จะเกิดอะไรขึ้นเมื่อเรารีเฟรชหน้าเพจด้วยตัวเอง

     Session.get('pageTitle');
    null
    
    Browser console

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

    บทเรียนสำคัญที่เราได้จากเรื่องนี้คือ

    1. พยายามเก็บข้อมูลสถานะผู้ใช้งานในเซสชัน หรือ URL อยู่เสมอ เพื่อให้ผู้ใช้ได้รับผลกระทบน้อยที่สุดเมื่อการรีโหลดอัตโนมัติเกิดขึ้น

    2. บันทึกสถานะของสิ่งที่คุณต้องการแชร์ระหว่างผู้ใช้งานด้วยกัน ไว้ใน URL

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