วันนี้จะขอแนะนำเบื้องหลังเว็บที่สร้างเสร็จในวันเดียวด้วย Flutter for Web และนำเสนอข้อดีข้อเสียของ Framework ตัวนี้ ณ เวลาที่เขียน

เว็บที่สร้างก็คือ เว็บจัดอันดับ VTuber คนไทย เช็คได้ที่ URL ข้างล่างนี้เลย!

Screenshot

หน้าจอเว็บจัดอันดับ VTuber ไทย

ความเป็นมา

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

ファン数ランキング(1ページ) | バーチャルYouTuberランキング

今話題になっている「バーチャルYouTuber」の人気キャラクター一覧。YouTubeチャンネルのファン人数や再生回数の推移を調査

เมื่อสองวันก่อน เพื่อนเราแชร์ลิงค์ลิสต์รายชื่อ VTuber ในไทยที่ทำโดยแอดมินเพจแอบอัพมาให้ตามนี้

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

Mock Wireframe หน้าตาเว็บก่อนลงมือสร้าง

โครงสร้างระบบ

ผังระบบ Thai VTuber Ranking

โครงสร้างระบบเว็บ ทุกอย่างรันบน Firebase หมดเลย ไม่มี Backend สำหรับแอพเพราะไม่มีความซับซ้อน

  • Backend: Firebase Functions ใช้ Typescript เขียน ดึงข้อมูลของแต่ละแชนแนลจาก Youtube Data API มาเก็บไว้ใน Firestore
    • สร้าง https function แล้วเรียกจาก Google Cloud Scheduler ทุกๆ 8 ชั่วโมงเพื่ออัพเดตยอดซับ/วิวอัตโนมัติ
  • Frontend: Flutter Web ดึงข้อมูลจาก Firestore มาโชว์ พร้อมสร้างฟิลเตอร์ให้จัดเรียงอันดับได้ตามความสนใจ

โฮสต์เลือกใช้ Firebase Hosting ใช้คำสั่ง firebase deploy ทีเดียวจบ ง่ายดี

เหตุผลที่เลือก Flutter Web

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

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

ข้อดีของ Flutter Web

Flutter Web ทำให้นักเขียนแอพสามารถเขียนโค้ดด้วยภาษาเดียวกับแอพแล้วเผยแพร่บนเว็บ เปิดประตูสู่โลกอันอิสระเสรีแบบที่ไม่เคยเจอมาก่อนเลย กราบ

เขียนได้เร็วมากสำหรับคนที่เคยเขียนแอพด้วย Flutter มาแล้ว

ตัว prototype ฟีเจอร์ minimum ที่สุด คือการดึงข้อมูลจาก firestore มาโชว์เป็นตารางบนเว็บ เราทำเสร็จในวันเดียว ปิ๊งไอเดียตอนเช้า เสร็จตอนกลางคืนเลย เขียนด้วยโค้ดเดียวกับ native app จริงๆ จะมีแก้ก็แค่ไฟล์ index.html ใส่โค้ดสำหรับ Firebase Web บ้างแค่นั้นเอง

ไม่ต้องเสียเวลาทำความเข้าใจคอนเซ็ปต์ที่ตัวเองไม่สนใจ

การต้องทำความเข้าใจคอนเซ็ปต์ของแพลตฟอร์มอื่น และเรียนรู้ภาษาโปรแกรมมิ่งใหม่เป็นอะไรที่เหนื่อย หนักหัว และชวนสับสนมาก (List .append .push .insert .add ไม่ทราบว่าทำไมต้องใช้คำต่างกันขนาดนี้ครับ…)

คอนเซ็ปต์ที่ไม่คล่องของการเขียนเว็บเช่น routing และ security ทั้งหลายแหล่ ส่วนภาษาถ้าเป็น Vue.js ยังดีหน่อยที่เป็น JS แต่ก็หลีกเลี่ยง HTML, CSS ไม่ได้ เราเซ็งทุกครั้งที่ต้องแตะสองภาษานี้ มือมันไม่ชินกับการเขียน UI ด้วยแท็ก <> เลย…

ไม่ต้องเผยแพร่ผ่าน Play Store / App Store

สำหรับคนทำเว็บคงไม่เกี่ยว แต่สำหรับคนทำแอพ การเผยแพร่แอพเมื่อไรก็ได้นั้นเป็นสิ่งที่ใฝ่ฝันมานานมาก ที่ผ่านมาต้องรอผลการตรวจของ Play Store / App Store ยิ่งถ้าเป็น App Store ยิ่งปวดหัว โดน reject ด้วยเหตุผลบ้าบอ ซึ่งการทำเว็บไม่ต้องเจอข้อจำกัดแบบนี้ ทำให้ขั้นตอนการเผยแพร่รวดเร็วมากๆ

ข้อเสียของ Flutter Web

มาพูดถึงข้อเสียดีกว่า เยอะมากกกกก

default ไม่มี path ให้แต่ละหน้า

Native app ไม่มี path ให้แต่ละเพจอย่างไร Flutter Web App ก็ไม่มีอย่างนั้น อารมณ์เดียวกับ SPA (Single Page App) ที่ทุกอย่างเกิดขึ้นบนหน้า index นั่นแหละ สำหรับเว็บรวมข้อมูลแบบนี้แล้วไม่เวิร์กอย่างแรง อยากดูรายละเอียดแชนแนลในเว็บเรา แต่สร้าง link เพื่อไปดูให้ตรงๆ ไม่ได้ ก็เท่ากับแชร์ลิงค์ให้คนอื่นไม่ได้สิ 🤦‍♂️

url ทุกเพจรันอยู่บน path /#/

อนึ่ง เท่าที่อ่านดู สามารถเซ็ต route ให้แต่ละหน้าจอได้โดยไม่ยากนัก แต่ก็ต้องเขียนโค้ดรองรับ parameter แต่ละหน้าโดยเฉพาะให้อยู่ดี

ไม่รองรับคลิกกลางหรือคลิกขวา

อันนี้กุมขมับหนักมาก ถ้าสังเกตดูแต่ละ cell บนตารางที่เป็นลิงค์ไปที่ยูทูปแชนแนล สามารถคลิกซ้ายเปิดลิงค์ได้ แต่คลิกกลางเพื่อเปิดในอีกแท็บ หรือคลิกขวาเพื่อดูรายละเอียดไม่ได้เลย เป็น behavior ปกติในแอพ แต่โคตรไม่ปกติบนเว็บ 🤦‍♂️

right-click คลิกขวาเพื่อเปิด URL บนแท็บใหม่ไม่ได้ ฮ้าาาาาา!!?`

ค้นหาตัวอักษรภายในหน้าไม่ได้

การเซิร์ชจากเบราเซอร์ จะหาเจอเฉพาะตัวอักษรที่อยู่บนจอ แต่ถ้าอยู่นอกจอ (บน/ล่าง) จะหาไม่เจอเลย นึกถึง wiki ที่เซิร์ชข้อความภายในหน้าไม่ได้ เวลาอยากหาข้อมูลไวๆ นี่จบเห่ คงต้องเขียนฟีเจอร์เซิร์ชขึ้นมาให้อีกที 🤦‍♂️

อืดมากกกกกกกกกกกก

เว็บนี้โชว์ข้อมูลด้วย ListView ตอนแรกโชว์ข้อมูลทีละ 100 แถว แค่นั้น scroll ก็กระตุกขนาดเปิดบนแมค ลดเหลือ 20 แถวค่อยดีหน่อย ต่างจาก native app อย่างเห็นได้ชัด แย่เกินไป 🤦‍♂️

Tips แก้ปัญหาที่พบ

deploy แล้วเว็บไม่อัพเดตซักที

อันนี้เป็นเรื่องของการตั้ง cache หากใครใช้ Firebase Hosting เหมือนเรา จะพบว่าเวลา deploy version ใหม่แล้วเข้าไปที่เว็บจะไม่อัพเดตทันที เพราะ Firebase ตั้งค่าให้ cache ไฟล์ไว้ 60 นาทีเป็นมาตรฐาน วิธีแก้คือ

  1. ตั้ง cache policy ของ firebase hosting ให้เป็นตามคำตอบนี้ (ทำให้ไม่ cache ไฟล์ index.html)

  2. เปลี่ยน version ของลิงค์ไปยังไฟล์ main.dart.js ใน index.html จากนั้นก็ deploy ตามปกติ เท่านี้เวลาเข้าไปที่หน้าเว็บเรา browser ก็จะโหลดแอพเวอร์ชั่นใหม่ให้ทันทีที่เรา deploy เสร็จ รายละเอียดเพิ่มดูที่ บทความนี้ ตัวอย่าง

 <body>
    <script src="main.dart.js?version=2" type="application/javascript"></script>
  </body>

อยากใช้ Github Actions ให้ deploy อัตโนมัติ

การ deploy แอพด้วยคำสั่ง firebase deploy ใช้เวลา 2-3 นาที ระหว่างนั้นเราจะไม่กล้าแก้ไขโค้ดเท่าไรนัก ปล่อยให้เป็นหน้าที่ของ CI ทำดีกว่า เราเลยเขียน Github Actions สำหรับ deploy Flutter Web to Firebase Hosting ไว้ตามนี้

โค้ด main.yml

name: Flutter Web - Build and Deploy
on:
  push:
    branches:
      - master

jobs:
  build_and_deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@main
      - name: Install Java
        uses: actions/setup-java@v1
        with:
          java-version: '12.x'
      - name: Install Flutter action
        uses: subosito/flutter-action@v1.3.2
        with:
          channel: 'dev'
      - run: flutter config --enable-web
      - run: flutter pub get
      - run: flutter build web --release
      - name: Install Package Dependencies
        run: npm install
        working-directory: ./functions
      - name: Deploy to Firebase
        uses: w9jds/firebase-action@master
        with:
          args: deploy
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

อนึ่ง เนื่องจากเราเขียน Firebase Functions ด้วย จึงต้องรัน npm install ให้ลง packages ก่อนจะ deploy ตอนเขียนติดอยู่ตรงนี้ประมาณชั่วโมงนึง งงมากว่าทำไมรัน tslint ไม่ผ่านซักที orz

อ้างอิงจากสองบทความนี้

Flutter Web — Github Actions-Github Pages

Today We will publish and build our flutter application to the web with Github Actions and It will be hosted by the gh-pagses.

Deploy to Firebase Hosting with Github Actions

Deploy application static assets to Firebase Hosting with Github Actions. You will love automation

สรุป

Flutter Web ช่วยให้ Native App Developer อย่างเราเขียนแอพขึ้นบนเว็บได้โดยง่าย ทดลองไอเดียได้ง่ายขึ้น มีประโยชน์มากๆ แต่ ณ เวลาที่เขียนบทความนี้ ไม่สามารถนำไปทำเว็บให้องค์กรได้แน่ๆ เพราะ UX การใช้งานเหมือนกับการใช้ Native App มากเกินไป แต่เมื่อผู้ใช้เข้าผ่านเว็บ พวกเขาก็จะคาดหวัง UX ของเว็บ ซึ่งตรงนี้ Flutter Web สอบตกอย่างรุนแรง (เซิร์ชไม่ได้, คลิกขวาไม่ได้, กด back กลับมาแล้วโหลดใหม่หมด) แถมประสิทธิภาพก็ต่ำกว่าเว็บทั่วไปอย่างมาก

ก็คงต้องรอดูว่าทีมพัฒนาจะปรับปรุงออกมาจนกระทั่งใช้งานทดแทน Web Framework ได้จริงขนาดไหน ถ้าทำได้นี่เจ๋งจริง เพราะแค่คอนเซ็ปต์ตอนนี้ก็ช่วยประหยัดเวลาให้เราไม่ต้องเรียนภาษาใหม่ไปได้เยอะแล้ว เพิ่ม productivity สำหรับคนเขียนแอพได้มากๆ

กว่าจะถึงจุดนั้นก็คงยังอีกนาน สำหรับตอนนี้ ถ้าใครจะเขียนเว็บแอพจริงจัง ก็พิจารณา Framework ตัวอื่นดีกว่านะ 😅