שימוש ב-bundleers של מודולים עם Firebase

אפשר לעשות הרבה דברים עם חבילות של מודולים של JavaScript, אבל אחת מהתכונות השימושיות ביותר שלהן היא היכולת להוסיף ספריות חיצוניות לבסיס הקוד ולהשתמש בהן. חבילות המודולים קוראות את נתיבי הייבוא בקוד ומחברות (מקבצות) את הקוד הספציפי לאפליקציה עם קוד הספרייה שיובאה.

מגרסה 9 ואילך, ממשק ה-API המודולרי של JavaScript ב-Firebase מותאם לעבודה עם תכונות האופטימיזציה של חבילות המודולים, כדי לצמצם את כמות הקוד של Firebase שכלולה ב-build הסופי.

import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged, getRedirectResult } from 'firebase/auth';

const firebaseApp = initializeApp({ /* config */ });
const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => { /* check status */ });

/**
 * getRedirectResult is unused and should not be included in the code base.
 * In addition, there are many other functions within firebase/auth that are
 * not imported and therefore should not be included as well.
 */

תהליך הסרת הקוד שלא בשימוש מהספרייה נקרא 'ניעור עץ'. הסרת הקוד הזה באופן ידני תימשך זמן רב ותהיה טעונה שגיאות, אבל אפשר להשתמש ב-bundlers של מודולים כדי להסיר אותו באופן אוטומטי.

יש הרבה חבילות מודולים באיכות גבוהה בסביבת JavaScript. במדריך הזה נסביר איך משתמשים ב-Firebase עם webpack,‏ Rollup ו-esbuild.

שנתחיל?

כדי להשתמש במדריך הזה, צריך להתקין את npm בסביבת הפיתוח. npm משמש להתקנה ולניהול של יחסי תלות (ספריות). כדי להתקין את npm, צריך להתקין את Node.js, שכולל את npm באופן אוטומטי.

רוב המפתחים מגדירים את המערכת בצורה תקינה אחרי התקנת Node.js. עם זאת, יש בעיות נפוצות שמפתחים רבים נתקלים בהן כשהם מגדירים את הסביבה שלהם. אם נתקלתם בשגיאות, ודאו שהסביבה כוללת את npm CLI ושההרשאות מוגדרות בצורה נכונה, כדי שלא תצטרכו להתקין חבילות כאדמינים באמצעות הפקודה sudo.

קובץ package.json והתקנת Firebase

אחרי שתתקינו את npm, תצטרכו ליצור קובץ package.json ברמה הבסיסית (root) של הפרויקט המקומי. יוצרים את הקובץ הזה באמצעות הפקודה הבאה ב-npm:

npm init

תוצג לך אשף שבו תצטרך לספק את המידע הנדרש. לאחר יצירת הקובץ, הוא אמור להיראות כך:

{
  "name": "your-package-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {

  }
}

הקובץ הזה אחראי על הרבה דברים שונים. כדאי להכיר את הקובץ הזה אם אתם רוצים לקבל מידע נוסף על קיבוץ מודולים ועל פיתוח קוד JavaScript באופן כללי. החלק החשוב במדריך הזה הוא האובייקט "dependencies". האובייקט הזה יכיל צמד מפתח/ערך של הספרייה שהותקנה והגרסה שבה היא משתמשת.

מוסיפים יחסי תלות באמצעות הפקודה npm install או npm i.

npm i firebase

כשמריצים את npm i firebase, תהליך ההתקנה מעדכן את package.json כך ש-Firebase תופיע כתלייה:

  "dependencies": {
    "firebase": "^9.0.0"
  },

המפתח הוא שם הספרייה והערך הוא הגרסה שבה רוצים להשתמש. ערך הגרסה גמיש ויכול לקבל מגוון ערכים. התהליך הזה נקרא 'ניהול גרסאות סמנטי' או semver. מידע נוסף על semver זמין במדריך של npm בנושא ניהול גרסאות סמנטיות.

תיקיות מקור לעומת תיקיות build

הקוד שאתם כותבים נקרא ומעובד על ידי חבילה של מודולים, ולאחר מכן הוא יוצא כקובץ חדש או כקבוצת קבצים חדשה. חשוב להפריד בין שני סוגי הקבצים האלה. הקוד שחבילות המודול קוראות ומעבדות נקרא קוד 'מקור'. הקבצים שהם יוצרים נקראים קוד build או קוד 'dist' (הפצה).

הגדרה נפוצה בבסיס קוד היא לאחסן את קוד המקור בתיקייה בשם src ואת הקוד שנוצר בתיקייה בשם dist.

- src
 |_ index.js
 |_ animations.js
 |_ datalist.js


- dist
 |_ bundle.js

בדוגמה של מבנה הקבצים שלמעלה, נניח ש-index.js מייבא גם את animations.js וגם את datalist.js. כשכלי לאיחוד מודולים מעבד את קוד המקור, הוא יוצר את הקובץ bundle.js בתיקייה dist. bundle.js הוא שילוב של הקבצים בתיקייה src ושל כל הספריות שיובאו.

אם אתם משתמשים במערכות לניהול גרסאות של קוד מקור, כמו Git, לרוב מתעלמים מהתיקייה dist כששומרים את הקוד הזה במאגר הראשי.

נקודות כניסה

לכל חבילות המודולים יש מושג של נקודת כניסה. אפשר להתייחס לאפליקציה כעץ של קבצים. קובץ אחד מייבא קוד מקובץ אחר וכן הלאה. כלומר, קובץ אחד יהיה הבסיס של העץ. הקובץ הזה נקרא נקודת הכניסה.

נבחן שוב את הדוגמה הקודמת של מבנה הקבצים.

- src
 |_ index.js
 |_ animations.js
 |_ datalist.js


- dist
 |_ bundle.js
// src/index.js
import { animate } from './animations';
import { createList } from './datalist';

// This is not real code, but for example purposes only
const theList = createList('users/123/tasks');
theList.addEventListener('loaded', event => {
  animate(theList);
});

הקובץ src/index.js נחשב לנקודת הכניסה כי הוא מתחיל את הייבוא של כל הקוד הנדרש לאפליקציה. קובץ נקודת הכניסה הזה משמש את הכלי לאריזת המודולים כדי להתחיל את תהליך האריזה.

שימוש ב-Firebase עם webpack

אין צורך בהגדרה ספציפית לאפליקציות של Firebase ול-webpack. בקטע הזה מתוארת הגדרה כללית של webpack.

בשלב הראשון מתקינים את webpack מ-npm כחבילת צד שלישי לצורכי פיתוח.

npm i webpack webpack-cli -D

יוצרים קובץ בשם webpack.config.js ברמה הבסיסית של הפרויקט המקומי ומוסיפים את הקוד הבא.

const path = require('path');

module.exports = {
  // The entry point file described above
  entry: './src/index.js',
  // The location of the build folder described above
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  // Optional and for development only. This provides the ability to
  // map the built code back to the original source format when debugging.
  devtool: 'eval-source-map',
};

לאחר מכן, מוודאים ש-Firebase מותקן כחבילת תלות.

npm i firebase

לאחר מכן מפעילים את Firebase בבסיס הקוד. הקוד הבא מייבא ומפעיל את Firebase בקובץ נקודת כניסה, ומשתמש ב-Firestore Lite כדי לטעון מסמך 'עיר'.

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

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

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

בשלב הבא צריך להוסיף סקריפט npm כדי להריץ את ה-build של webpack. פותחים את הקובץ package.json ומוסיפים את צמד המפתח/הערך הבא לאובייקט "scripts".

  "scripts": {
    "build": "webpack --mode=development"
  },

כדי להריץ את webpack וליצור את תיקיית ה-build, מריצים את הפקודה הבאה.

npm run build

לבסוף, בודקים את תיקיית ה-build dist. הוא אמור להכיל קובץ בשם bundle.js שמכיל את האפליקציה ואת קוד התלות.

למידע נוסף על אופטימיזציה של build של webpack בסביבת הייצור, אפשר לעיין במסמכי העזרה הרשמיים בנושא הגדרת התצורה 'mode'.

שימוש ב-Firebase עם נתונים מצטברים

אין צורך בהגדרה ספציפית לאפליקציות ול-Rollup של Firebase. בקטע הזה נסביר על הגדרה כללית של נכס-על.

השלב הראשון הוא התקנת Rollup ופלאגין שמשמשים למיפוי ייבוא ליחסי תלות שמותקנים באמצעות npm.

npm i rollup @rollup/plugin-node-resolve -D

יוצרים קובץ בשם rollup.config.js בתיקיית השורש של הפרויקט המקומי ומוסיפים את הקוד הבא.

import { nodeResolve } from '@rollup/plugin-node-resolve';

export default {
  // the entry point file described above
  input: 'src/index.js',
  // the output for the build folder described above
  output: {
    file: 'dist/bundle.js',
    // Optional and for development only. This provides the ability to
    // map the built code back to the original source format when debugging.
    sourcemap: 'inline',
    // Configure Rollup to convert your module code to a scoped function
    // that "immediate invokes". See the Rollup documentation for more
    // information: https://rollupjs.org/guide/en/#outputformat
    format: 'iife'
  },
  // Add the plugin to map import paths to dependencies
  // installed with npm
  plugins: [nodeResolve()]
};

לאחר מכן מפעילים את Firebase בבסיס הקוד. הקוד הבא מייבא ומפעיל את Firebase בקובץ נקודת כניסה, ומשתמש ב-Firestore Lite כדי לטעון מסמך 'עיר'.

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

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

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

השלב הבא הוא הוספת סקריפט npm כדי להריץ את ה-build של האוסף. פותחים את הקובץ package.json ומוסיפים את צמד המפתח/הערך הבא לאובייקט "scripts".

  "scripts": {
    "build": "rollup -c rollup.config.js"
  },

כדי להריץ את ה-rollup וליצור את תיקיית ה-build, מריצים את הפקודה הבאה.

npm run build

לבסוף, בודקים את תיקיית ה-build dist. הוא אמור להכיל קובץ בשם bundle.js שמכיל את האפליקציה ואת קוד התלות.

למידע נוסף על אופטימיזציה של גרסה אסופת (rollup) לצורכי ייצור, אפשר לעיין במסמכי התיעוד הרשמי שלהם בנושא יישומי פלאגין לגרסאות build לצורכי ייצור.

שימוש ב-Firebase עם esbuild

אין צורך בהגדרה ספציפית לאפליקציות Firebase ול-esbuild. בקטע הזה נסביר על הגדרה כללית של esbuild.

השלב הראשון הוא להתקין את esbuild כחבילת תלות לפיתוח.

npm i esbuild -D

יוצרים קובץ בשם esbuild.config.js ברמה הבסיסית של הפרויקט המקומי ומוסיפים את הקוד הבא.

require('esbuild').build({
  // the entry point file described above
  entryPoints: ['src/index.js'],
  // the build folder location described above
  outfile: 'dist/bundle.js',
  bundle: true,
  // Replace with the browser versions you need to target
  target: ['chrome60', 'firefox60', 'safari11', 'edge20'],
  // Optional and for development only. This provides the ability to
  // map the built code back to the original source format when debugging.
  sourcemap: 'inline',
}).catch(() => process.exit(1))

לאחר מכן מפעילים את Firebase בבסיס הקוד. הקוד הבא מייבא ומפעיל את Firebase בקובץ נקודת כניסה, ומשתמש ב-Firestore Lite כדי לטעון מסמך 'עיר'.

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

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

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

בשלב הבא צריך להוסיף סקריפט npm כדי להריץ את esbuild. פותחים את הקובץ package.json ומוסיפים את צמד המפתח/ערך הבא לאובייקט "scripts".

  "scripts": {
    "build": "node ./esbuild.config.js"
  },

לבסוף, בודקים את תיקיית ה-build dist. הוא אמור להכיל קובץ בשם bundle.js שמכיל את האפליקציה ואת קוד התלות.

למידע נוסף על אופטימיזציה של esbuild בסביבת הייצור, אפשר לעיין במסמכים הרשמיים שלהם בנושא אופטימיזציה אחרת וקידוד למינימיזציה.