วันนี้จะขอแนะนำเบื้องหลังเว็บที่สร้างเสร็จในวันเดียวด้วย Flutter for Web และนำเสนอข้อดีข้อเสียของ Framework ตัวนี้ ณ เวลาที่เขียน
เว็บที่สร้างก็คือ เว็บจัดอันดับ VTuber คนไทย เช็คได้ที่ URL ข้างล่างนี้เลย!
หน้าจอเว็บจัดอันดับ VTuber ไทย
ความเป็นมา
เนื่องจากตัวเราเป็นทีมงานร่วมทำแชนแนล VTuber คนหนึ่งอยู่ เลยมีความสนใจว่าวงการ VTuber ไทยเป็นยังไง มีคนเผยแพร่/ติดตามเยอะแค่ไหน แต่ก็ไม่มีใครทำเว็บจัดอันดับ เลยไม่รู้ภาพรวมว่าเป็นยังไงกันแน่ รู้จักแต่คนดังๆ ที่บริษัทปั้นอย่าง Aisha กับ Yuchan ส่วนคนอื่นแทบจะไม่รู้เลย ในขณะที่ถ้าเป็น VTuber ญี่ปุ่น เราสามารถเช็คอันดับได้จากเว็บนี้โดยง่าย
ファン数ランキング(1ページ) | バーチャルYouTuberランキング
เมื่อสองวันก่อน เพื่อนเราแชร์ลิงค์ลิสต์รายชื่อ VTuber ในไทยที่ทำโดยแอดมินเพจแอบอัพมาให้ตามนี้
เห็นลิสต์แล้วตกตะลึงว่ามี VTuber คนไทยเกินร้อยคน เลยอยากจัดอันดับยอดซับ จะได้รู้ว่าใครเป็นสี่จอมเทพ VTuber ของไทยกันแน่ จึงเกิดเป็นไอเดียทำเว็บจัดอันดับนี้ขึ้น โดยมีวัตถุประสงค์ที่จะส่งเสริมวงการ ผ่านการสร้างพื้นที่รวบรวมข้อมูลและเผยแพร่อัพเดตข่าวสารของ VTuber ไทยโดยอัตโนมัติ
Wireframe หน้าตาเว็บก่อนลงมือสร้าง
โครงสร้างระบบ
โครงสร้างระบบเว็บ ทุกอย่างรันบน 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 เพื่อไปดูให้ตรงๆ ไม่ได้ ก็เท่ากับแชร์ลิงค์ให้คนอื่นไม่ได้สิ 🤦♂️
ทุกเพจรันอยู่บน path /#/
อนึ่ง เท่าที่อ่านดู สามารถเซ็ต route ให้แต่ละหน้าจอได้โดยไม่ยากนัก แต่ก็ต้องเขียนโค้ดรองรับ parameter แต่ละหน้าโดยเฉพาะให้อยู่ดี
ไม่รองรับคลิกกลางหรือคลิกขวา
อันนี้กุมขมับหนักมาก ถ้าสังเกตดูแต่ละ cell บนตารางที่เป็นลิงค์ไปที่ยูทูปแชนแนล สามารถคลิกซ้ายเปิดลิงค์ได้ แต่คลิกกลางเพื่อเปิดในอีกแท็บ หรือคลิกขวาเพื่อดูรายละเอียดไม่ได้เลย เป็น behavior ปกติในแอพ แต่โคตรไม่ปกติบนเว็บ 🤦♂️
คลิกขวาเพื่อเปิด URL บนแท็บใหม่ไม่ได้ ฮ้าาาาาา!!?`
ค้นหาตัวอักษรภายในหน้าไม่ได้
การเซิร์ชจากเบราเซอร์ จะหาเจอเฉพาะตัวอักษรที่อยู่บนจอ แต่ถ้าอยู่นอกจอ (บน/ล่าง) จะหาไม่เจอเลย นึกถึง wiki ที่เซิร์ชข้อความภายในหน้าไม่ได้ เวลาอยากหาข้อมูลไวๆ นี่จบเห่ คงต้องเขียนฟีเจอร์เซิร์ชขึ้นมาให้อีกที 🤦♂️
อืดมากกกกกกกกกกกก
เว็บนี้โชว์ข้อมูลด้วย ListView ตอนแรกโชว์ข้อมูลทีละ 100 แถว แค่นั้น scroll ก็กระตุกขนาดเปิดบนแมค ลดเหลือ 20 แถวค่อยดีหน่อย ต่างจาก native app อย่างเห็นได้ชัด แย่เกินไป 🤦♂️
Tips แก้ปัญหาที่พบ
deploy แล้วเว็บไม่อัพเดตซักที
อันนี้เป็นเรื่องของการตั้ง cache หากใครใช้ Firebase Hosting เหมือนเรา จะพบว่าเวลา deploy version ใหม่แล้วเข้าไปที่เว็บจะไม่อัพเดตทันที เพราะ Firebase ตั้งค่าให้ cache ไฟล์ไว้ 60 นาทีเป็นมาตรฐาน วิธีแก้คือ
-
ตั้ง cache policy ของ firebase hosting ให้เป็นตามคำตอบนี้ (ทำให้ไม่ cache ไฟล์
index.html
) -
เปลี่ยน 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
Deploy to Firebase Hosting with Github Actions
สรุป
Flutter Web ช่วยให้ Native App Developer อย่างเราเขียนแอพขึ้นบนเว็บได้โดยง่าย ทดลองไอเดียได้ง่ายขึ้น มีประโยชน์มากๆ แต่ ณ เวลาที่เขียนบทความนี้ ไม่สามารถนำไปทำเว็บให้องค์กรได้แน่ๆ เพราะ UX การใช้งานเหมือนกับการใช้ Native App มากเกินไป แต่เมื่อผู้ใช้เข้าผ่านเว็บ พวกเขาก็จะคาดหวัง UX ของเว็บ ซึ่งตรงนี้ Flutter Web สอบตกอย่างรุนแรง (เซิร์ชไม่ได้, คลิกขวาไม่ได้, กด back กลับมาแล้วโหลดใหม่หมด) แถมประสิทธิภาพก็ต่ำกว่าเว็บทั่วไปอย่างมาก
ก็คงต้องรอดูว่าทีมพัฒนาจะปรับปรุงออกมาจนกระทั่งใช้งานทดแทน Web Framework ได้จริงขนาดไหน ถ้าทำได้นี่เจ๋งจริง เพราะแค่คอนเซ็ปต์ตอนนี้ก็ช่วยประหยัดเวลาให้เราไม่ต้องเรียนภาษาใหม่ไปได้เยอะแล้ว เพิ่ม productivity สำหรับคนเขียนแอพได้มากๆ
กว่าจะถึงจุดนั้นก็คงยังอีกนาน สำหรับตอนนี้ ถ้าใครจะเขียนเว็บแอพจริงจัง ก็พิจารณา Framework ตัวอื่นดีกว่านะ 😅