อัปเกรดจาก API ที่มีเนมสเปซเป็นแอปพลิเคชันแบบโมดูล

แอปที่ใช้ Firebase Web API ที่มีเนมสเปซจากcompatไลบรารีย้อนกลับไปตั้งแต่เวอร์ชัน 8 หรือเวอร์ชันก่อนหน้า ควรพิจารณาย้ายข้อมูลไปยัง API แบบโมดูลตามวิธีการในคู่มือนี้

คำแนะนำนี้ถือว่าคุณคุ้นเคยกับ API ที่มีเนมสเปซและจะใช้ข้อดีของ Module Bundler เช่น Webpack หรือ Rollup เพื่ออัปเกรดและการพัฒนาแอปแบบโมดูลอย่างต่อเนื่อง

เราขอแนะนําอย่างยิ่งให้ใช้เครื่องมือรวมโมดูลในสภาพแวดล้อมการพัฒนา หากไม่ได้ใช้ คุณจะไม่สามารถใช้ข้อดีหลักๆ ของ API แบบโมดูลในการลดขนาดแอป คุณจะต้องมี npm หรือ yarn เพื่อติดตั้ง SDK

ขั้นตอนในการอัปเกรดในคู่มือนี้จะอิงตามเว็บแอปสมมติที่ใช้ SDK Authentication และ Cloud Firestore การทำตามตัวอย่างจะช่วยให้คุณเข้าใจแนวคิดและขั้นตอนปฏิบัติที่จำเป็นในการอัปเกรด Firebase Web SDK ที่รองรับทั้งหมด

เกี่ยวกับไลบรารีที่มีเนมสเปซ (compat)

ไลบรารีสําหรับ Firebase Web SDK มี 2 ประเภท ได้แก่

  • โมดูล - อินเทอร์เฟซ API ใหม่ที่ออกแบบมาเพื่ออำนวยความสะดวกในการนำโค้ดที่ไม่ได้ใช้ออก (Tree-shaking) เพื่อให้เว็บแอปมีขนาดเล็กและรวดเร็วที่สุด
  • มีเนมสเปซ (compat) - แพลตฟอร์ม API ที่คุ้นเคยซึ่งเข้ากันได้กับ SDK เวอร์ชันเก่าอย่างสมบูรณ์ ซึ่งช่วยให้คุณอัปเกรดได้โดยไม่ต้องเปลี่ยนโค้ด Firebase ทั้งหมดพร้อมกัน ไลบรารี Compat มีข้อดีด้านขนาดหรือประสิทธิภาพน้อยมากหรือไม่มีเลยเมื่อเทียบกับไลบรารีที่มีเนมสเปซ

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

สําหรับแอปที่มีการใช้งาน Firebase Web SDK เพียงเล็กน้อย เช่น แอปที่เรียกใช้ Authentication API เพียงอย่างเดียว ก็อาจเหมาะที่จะรีแฟกทอเรียลโค้ดที่มีเนมสเปซเก่าๆ โดยไม่ต้องใช้ไลบรารี compat หากจะอัปเกรดแอปดังกล่าว ให้ทําตามวิธีการในคู่มือนี้สําหรับ "API แบบโมดูล" โดยไม่ต้องใช้ไลบรารี compat

เกี่ยวกับกระบวนการอัปเกรด

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

  1. เพิ่มไลบรารีแบบโมดูลและไลบรารีที่เข้ากันได้ลงในแอป
  2. อัปเดตคำสั่งการนําเข้าในโค้ดเป็น compat
  3. ปรับโค้ดสำหรับผลิตภัณฑ์เดียว (เช่น Authentication) ให้เป็นรูปแบบโมดูล
  4. ไม่บังคับ: เมื่อถึงจุดนี้ ให้นำไลบรารีและโค้ด Authentication compat ออกสำหรับ Authentication เพื่อให้ได้รับประโยชน์ด้านขนาดแอปสำหรับ Authentication ก่อนดำเนินการต่อ
  5. ปรับแต่งฟังก์ชันของผลิตภัณฑ์แต่ละรายการ (เช่น Cloud Firestore, FCM ฯลฯ) ให้เป็นรูปแบบโมดูล คอมไพล์ และทดสอบจนกว่าทุกส่วนจะเสร็จสมบูรณ์
  6. อัปเดตโค้ดการเริ่มต้นเป็นรูปแบบโมดูล
  7. นำคำสั่งและโค้ดความเข้ากันได้ที่เหลือทั้งหมดออกจากแอป

ดาวน์โหลด SDK เวอร์ชันล่าสุด

หากต้องการเริ่มต้นใช้งาน ให้ดาวน์โหลดไลบรารีแบบโมดูลและไลบรารีที่เข้ากันได้โดยใช้ npm โดยทำดังนี้

npm i firebase@11.3.0

# OR

yarn add firebase@11.3.0

อัปเดตการนําเข้าเป็น compat

หากต้องการให้โค้ดทำงานต่อไปหลังจากอัปเดต Dependency ให้เปลี่ยนคำสั่งการนําเข้าเพื่อใช้เวอร์ชัน "compat" ของการนําเข้าแต่ละรายการ เช่น

ก่อน: เวอร์ชัน 8 หรือเก่ากว่า

import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

After: compat

// compat packages are API compatible with namespaced code
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';

ปรับโครงสร้างเป็นรูปแบบโมดูล

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

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

ตัวอย่างที่ 1: การจัดระเบียบฟังก์ชัน Authentication

Before: compat

โค้ดที่เข้ากันได้จะเหมือนกับโค้ดที่มีเนมสเปซ แต่การนําเข้าจะเปลี่ยนไป

import firebase from "firebase/compat/app";
import "firebase/compat/auth";

const auth = firebase.auth();
auth.onAuthStateChanged(user => { 
  // Check for user status
});

หลัง: โมดูล

ฟังก์ชัน getAuth จะรับ firebaseApp เป็นพารามิเตอร์แรก ฟังก์ชัน onAuthStateChanged ไม่ได้เชื่อมโยงกับอินสแตนซ์ auth ดังที่ควรจะเป็นใน API ที่มีเนมสเปซ แต่เป็นฟังก์ชันอิสระที่ใช้ auth เป็นพารามิเตอร์แรก

import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => {
  // Check for user status
});

อัปเดตการจัดการวิธีการตรวจสอบสิทธิ์ getRedirectResult

มีการเปิดตัวการเปลี่ยนแปลงที่ส่งผลต่อเวอร์ชันเดิมใน getRedirectResult เมื่อไม่มีการเรียกการดำเนินการเปลี่ยนเส้นทาง API แบบโมดูลจะแสดงผลเป็น null ต่างจาก API ที่มีเนมสเปซซึ่งแสดงผลเป็น UserCredential ที่มีผู้ใช้ null

Before: compat

const result = await auth.getRedirectResult()
if (result.user === null && result.credential === null) {
  return null;
}
return result;

หลัง: โมดูล

const result = await getRedirectResult(auth);
// Provider of the access token could be Facebook, Github, etc.
if (result === null || provider.credentialFromResult(result) === null) {
  return null;
}
return result;

ตัวอย่างที่ 2: การจัดระเบียบฟังก์ชัน Cloud Firestore

Before: compat

import "firebase/compat/firestore"

const db = firebase.firestore();
db.collection("cities").where("capital", "==", true)
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

หลัง: โมดูล

ฟังก์ชัน getFirestore ใช้ firebaseApp เป็นพารามิเตอร์แรก ซึ่งแสดงผลจาก initializeApp ในตัวอย่างก่อนหน้านี้ โปรดสังเกตว่าโค้ดในการสร้างคําค้นหานั้นแตกต่างกันมากใน API แบบโมดูล ไม่มีการต่อเชื่อม และตอนนี้มีการแสดงเมธอด เช่น query หรือ where เป็นฟังก์ชันอิสระ

import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";

const db = getFirestore(firebaseApp);

const q = query(collection(db, "cities"), where("capital", "==", true));

const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
  // doc.data() is never undefined for query doc snapshots
  console.log(doc.id, " => ", doc.data());
});

อัปเดตการอ้างอิงไปยัง Firestore DocumentSnapshot.exists

API แบบโมดูลจะทําการเปลี่ยนแปลงที่ทําให้ใช้งานไม่ได้ ซึ่งก็คือมีการเปลี่ยนแปลงพร็อพเพอร์ตี้ firestore.DocumentSnapshot.exists เป็นเมธอด ฟังก์ชันการทำงานโดยพื้นฐานแล้วเหมือนกัน (การทดสอบว่าเอกสารมีอยู่หรือไม่) แต่คุณต้องปรับโค้ดให้ใช้เมธอดใหม่ดังที่แสดง

Before:compat

if (snapshot.exists) {
  console.log("the document exists");
}

หลัง: โมดูล

if (snapshot.exists()) {
  console.log("the document exists");
}

ตัวอย่างที่ 3: การรวมรูปแบบโค้ดที่มีเนมสเปซและโมดูล

การใช้ไลบรารี compat ระหว่างการอัปเกรดจะช่วยให้คุณใช้โค้ดที่มีเนมสเปซต่อไปได้ควบคู่ไปกับโค้ดที่ผ่านการรีแฟกทอริงสำหรับ API แบบโมดูล ซึ่งหมายความว่าคุณจะเก็บโค้ดที่มีเนมสเปซที่มีอยู่ของ Cloud Firestore ไว้ได้ขณะที่รีแฟกทอริง Authentication หรือโค้ด Firebase SDK อื่นๆ เป็นรูปแบบโมดูล และยังคงคอมไพล์แอปด้วยโค้ดทั้ง 2 รูปแบบได้สําเร็จ เช่นเดียวกับโค้ด API ที่มีเนมสเปซและแบบโมดูลภายในผลิตภัณฑ์ เช่น Cloud Firestore โค้ดรูปแบบใหม่และเก่าสามารถอยู่ร่วมกันได้ ตราบใดที่คุณนําเข้าแพ็กเกจที่เข้ากันได้ ดังนี้

import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import { getDoc } from 'firebase/firestore'

const docRef = firebase.firestore().doc();
getDoc(docRef);

โปรดทราบว่าแม้ว่าแอปจะคอมไพล์ได้ แต่คุณจะไม่ได้รับการลดขนาดแอปจากโค้ดแบบโมดูลจนกว่าจะนำคำสั่งและโค้ด compat ออกจากแอปโดยสมบูรณ์

อัปเดตโค้ดการเริ่มต้น

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

Before: compat

import firebase from "firebase/compat/app"

firebase.initializeApp({ /* config */ });

หลัง: โมดูล

import { initializeApp } from "firebase/app"

const firebaseApp = initializeApp({ /* config */ });

นำรหัสความเข้ากันได้ออก

หากต้องการใช้ประโยชน์จากขนาดของ API แบบโมดูล คุณควรแปลงการเรียกใช้ทั้งหมดเป็นสไตล์โมดูลที่แสดงด้านบนและนําคำสั่ง import "firebase/compat/* ทั้งหมดออกจากโค้ด เมื่อดำเนินการเสร็จแล้ว ไม่ควรมีการอ้างอิงถึงfirebase.*เนมสเปซส่วนกลางหรือโค้ดอื่นๆ ในสไตล์ API ที่มีเนมสเปซอีก

การใช้ไลบรารี compat จากหน้าต่าง

API แบบโมดูลได้รับการเพิ่มประสิทธิภาพให้ทำงานร่วมกับโมดูลแทนออบเจ็กต์ของเบราว์เซอร์ window ไลบรารีเวอร์ชันก่อนหน้านี้อนุญาตให้โหลดและจัดการ Firebase โดยใช้เนมสเปซ window.firebase เราไม่แนะนําให้ใช้วิธีนี้นับจากนี้เป็นต้นไปเนื่องจากไม่อนุญาตให้นําโค้ดที่ไม่ได้ใช้ออก อย่างไรก็ตาม SDK ของ JavaScript เวอร์ชันที่เข้ากันได้จะทํางานกับ window สําหรับนักพัฒนาแอปที่ไม่ต้องการเริ่มเส้นทางการอัปเกรดแบบโมดูลทันที

<script src="https://www.gstatic.com/firebasejs/11.3.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/11.3.0/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/11.3.0/firebase-auth-compat.js"></script>
<script>
   const firebaseApp = firebase.initializeApp({ /* Firebase config */ });
   const db = firebaseApp.firestore();
   const auth = firebaseApp.auth();
</script>

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

ประโยชน์และข้อจํากัดของ SDK แบบแยกส่วน

SDK แบบโมดูลทั้งหมดมีข้อดีเหนือกว่าเวอร์ชันก่อนหน้าดังนี้

  • SDK แบบแยกส่วนช่วยให้แอปมีขนาดเล็กลงอย่างมาก รูปแบบนี้ใช้รูปแบบโมดูล JavaScript สมัยใหม่ ซึ่งช่วยให้ใช้แนวทาง "Tree Shaking" ในการนําเข้าเฉพาะอาร์ติแฟกต์ที่แอปจําเป็น การตัดต้นไม้ด้วย SDK แบบโมดูลอาจทำให้แอปมีขนาดเล็กลง 80% เมื่อเทียบกับแอปที่เปรียบเทียบได้ซึ่งสร้างขึ้นโดยใช้ API ที่มีเนมสเปซ ทั้งนี้ขึ้นอยู่กับแอปของคุณ
  • SDK แบบโมดูลจะยังคงได้รับประโยชน์จากการพัฒนาฟีเจอร์อย่างต่อเนื่อง ในขณะที่ API ที่มีเนมสเปซจะไม่ได้รับประโยชน์ดังกล่าว