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

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

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

เราขอแนะนำอย่างยิ่งให้ใช้ Module Bundler ในสภาพแวดล้อมในการพัฒนาซอฟต์แวร์ หากไม่ใช้ คุณจะใช้ประโยชน์หลักของ Modular API ในการลดขนาดแอปไม่ได้ คุณจะต้องใช้ npm หรือ yarn เพื่อติดตั้ง SDK

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

เกี่ยวกับไลบรารีที่มี Namespace (compat)

Firebase Web SDK มีไลบรารี 2 ประเภทดังนี้

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

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

สำหรับแอปที่ใช้ Firebase Web SDK น้อยมาก เช่น แอปที่เรียกใช้ API ของ Authentication แบบง่ายๆ เพียงครั้งเดียว การปรับโครงสร้างโค้ดที่มี Namespace เวอร์ชันเก่าโดยไม่ใช้ไลบรารี Compat อาจเป็นวิธีที่เหมาะสม หากกำลังอัปเกรดแอปดังกล่าว คุณสามารถทำตามคำแนะนำในคู่มือนี้สำหรับ "Modular API" โดยไม่ต้องใช้ไลบรารี Compat

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

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

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

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

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

npm i firebase@12.13.0

# OR

yarn add firebase@12.13.0

อัปเดตการนำเข้าเป็น Compat

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

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

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

หลัง: compat

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

ปรับโครงสร้างเป็นสไตล์แบบแยกส่วน

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

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

ตัวอย่างที่ 1: การปรับโครงสร้างฟังก์ชัน Authentication

ก่อน: compat

โค้ด Compat เหมือนกับโค้ดที่มี Namespace แต่การนำเข้ามีการเปลี่ยนแปลง

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 เหมือนกับใน Namespaced API แต่เป็นฟังก์ชันอิสระที่ใช้ auth เป็นพารามิเตอร์แรก

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

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

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

Modular API มีการเปลี่ยนแปลงที่ทำให้เกิดข้อผิดพลาดใน getRedirectResult เมื่อไม่มีการเรียกใช้การดำเนินการเปลี่ยนเส้นทาง Modular API จะแสดงผล null ซึ่งแตกต่างจาก Namespaced API ซึ่งแสดงผล UserCredential ที่มีผู้ใช้ null

ก่อน: 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

ก่อน: 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 ในตัวอย่างก่อนหน้า โปรดสังเกตว่าโค้ดสำหรับสร้างคิวรีแตกต่างกันมากใน Modular 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());
});

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

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

ก่อน:compat

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

หลัง: แบบแยกส่วน

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

ตัวอย่างที่ 3: การรวมสไตล์โค้ดที่มี Namespace และแบบแยกส่วน

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

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 แบบ Compat และแบบแยกส่วน ในขณะที่ ฟังก์ชันinitializeApp()แบบแยกส่วนจะเริ่มต้นสถานะสำหรับแบบแยกส่วนเท่านั้น

ก่อน: compat

import firebase from "firebase/compat/app"

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

หลัง: แบบแยกส่วน

import { initializeApp } from "firebase/app"

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

นำโค้ด Compat ออก

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

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

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

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

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

ข้อดีและข้อจำกัดของ SDK แบบแยกส่วน

SDK ที่แยกส่วนอย่างสมบูรณ์มีข้อดีดังต่อไปนี้เมื่อเทียบกับเวอร์ชันก่อนหน้า

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