Firebase JS:为什么以及何时取消订阅 onAuthStateChanged

Firebase JS: Why and When to unsubscribe onAuthStateChanged

我在一个奇怪的网站上工作,我只需要将表单发送到 firestore 并将 pdf 文件保存到 firestore。 甚至没有 运行 任何服务器端代码,只有 html 和最终客户端上的 JS。 我的组织只会给 运行 html、CSS 和 JS 一个 apache 服务器路径。 稍后我需要阅读,但我是唯一具有读取权限的用户,所以这没什么大不了的,因为我会 运行 在我的 node.js CLI 上本地阅读。

但我期待着让我的代码尽可能安全和高效,尽管我有 60% 的时间都不知道自己在做什么。

在线阅读文档和代码后,我意识到我在执行 onAuthStateChanged() 时并没有执行 unsubscribe()。 通过搜索 JS 上的内存泄漏,我发现有人说如果我想在内存中保留一些东西,那么我不应该取消订阅。但是正如我所说,我不知道我在做什么。

这是我的一些 .js 和我的 login.html 我担心 4 件事,我来了:

  1. 我是否应该在 登出 onAuthStateChanged 结束后取消订阅()?如果有,在哪里?
  2. 是否可以在 window.onload 中设置所有这些代码?
  3. 我是否应该将此代码拆分为模块以便我可以重新使用 initializeApp() 和其他重复代码,或者不推荐这样做,因为我正在使用 CDN 导入 firebase 模块而不是使用导入语句。
  4. 我是否应该在 html 的每个页面上执行 initializeApp()?或者在登录时执行一次并强制用户通过 vanilla JS 登录将是可行的方法?
window.onload = () => {
    //Config and init fb
    var firebaseConfig = {
        //...my config 
    };
    if (!firebase.apps.length) {
        firebase.initializeApp(firebaseConfig)
    }
    const auth = firebase.auth();
    const provider = new firebase.auth.GoogleAuthProvider();
    //Get elements
    const signInBtn = document.getElementById("log-in-btn");
    //Listeners 
    signInBtn.onclick = () => auth.signInWithPopup(provider);
    //Listeners require user
    //sign-in-out 
    auth.onAuthStateChanged(user => {
        if (user) {
            // signed in
            if (!user.email.includes('@mydomain.com')) {
                auth.signOut();
                 //maybe delete the user, haven't test it
                alert('Porfavor utilice su correo institucional');
            } else {
                window.location.replace('dashboard.html');
            }
        } else {
            // not signed in you will always end up here
        }
    });
}

html 都是这样的,但我每个 html 都有一个 JS 文件,每个 JS 配置和初始化 firebase。

<head>
    <meta charset="UTF-8">
    <title>Login </title>
    <!--Project CSS-->
    <!--Boostrap 5-->
    <!--Firebase Modules-->
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-firestore.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-storage.js"></script>
    <!--Proyect Modules-->
    <script src="js/login.js" defer></script>
</head>

稍后在我的仪表板中这是我的方法

window.onload = () => {
    var firebaseConfig = {
        //my config
    };
    if (!firebase.apps.length) {
        firebase.initializeApp(firebaseConfig)
    }
    const auth = firebase.auth();
    const provider = new firebase.auth.GoogleAuthProvider();
    //Get elements
    const signedIn = document.getElementById("toggle-w-log-in");
    const signedOff = document.getElementById("toggle-w-log-off");
    const signInBtn = document.getElementById("log-in-btn");
    const signOutBtn = document.getElementById("log-off-btn");
    const uname = document.getElementById("userName");
    const userDetails = document.getElementById("userDetails");
    //Events / Listeners 
    signOutBtn.onclick = () => {
        auth.signOut();
              window.location.replace("login.html");
    };
    //Events / Listeners that require user
    //sign-in-out 
    auth.onAuthStateChanged(user => {
        if (user) {
            // signed in
            signedIn.hidden = false;
            signedOff.hidden = true;
            userDetails.innerHTML = `<b>Sesión iniciada como ${user.displayName}</b>`;
            var contentImgUser = document.getElementById("img-Us");
            contentImgUser.setAttribute("src", user.photoURL);
        } else {
            // not signed in
            signedIn.hidden = true;
            signedOff.hidden = false;
            userDetails.innerHTML = '';
                  window.location.replace("login.html");
        }
    });
}

最后一个百万美元的问题:我是否为我的表格编写了这段代码?

window.onload = () => {
    var firebaseConfig = {
        //my config
    };
    if (!firebase.apps.length) {
        firebase.initializeApp(firebaseConfig)
    }
    const auth = firebase.auth();
    const provider = new firebase.auth.GoogleAuthProvider();
    //Get elements from dom by id
    //Events / Listeners 
    //sign-in-out 
    signOutBtn.onclick = () => {
        auth.signOut();
        window.location.replace("login.html");
    };
    auth.onAuthStateChanged(user => {
        if (user) {
            // signed in
            signedIn.hidden = false;
            signedOff.hidden = true;
            //display the userDetails
            //display user name in the username field
            uname.value = user.displayName;
        } else {
            // not signed in
            signedIn.hidden = true;
            signedOff.hidden = false;
            userDetails.innerHTML = '';
            window.location.replace("login.html");
        }
    });
    //save file to Firestorage then save doc to Firestore 
    //finally set a cooldown on the button 
    auth.onAuthStateChanged(user => {
        saveBtn.addEventListener("click", (event) => {
            event.preventDefault();
            //check my required files
            if (requiredFields) {
                saveBtn.disabled = true;
                //some logic with my progressbar
                var metadata = {
                    contentType: 'application/pdf'
                };
                const files = document.getElementById("myfile").files;
                var storageRef = firebase.storage().ref();
                let file = files[0];
                // Upload file and metadata to the object 'pdf/filename.pdf'
                // Listen for state changes, errors, and completion of the upload.
                uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, 
                    (snapshot) => {
                        // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
                        //some logic with the progress to fill my progressbar
                        switch (snapshot.state) {
                            case firebase.storage.TaskState.PAUSED:
                                break;
                            case firebase.storage.TaskState.RUNNING: 
                                break;
                        }
                    },
                    (error) => {
                        switch (error.code) {
                            case 'storage/unauthorized':
                                break;
                            case 'storage/canceled':
                                break;
                            case 'storage/unknown':
                                break;
                        }
                    },
                    () => {
                        // Upload completed successfully, now we can get the download URL
                        uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
                            const db = firebase.firestore().collection('mycollections');
                            const userUid = user.uid;
                            const timestamp = new Date().getTime();
                            try {
                                db.add({
                                    refEvi: downloadURL,
                                    userUid: userUid,
                                    //all my fields
                                }).then((docRef) => {
                                    artRes.innerHTML = "<b>Registro guardado con la referencia: </b>" + docRef.id;
                                });
                            } catch (error) {
                                console.log(error);
                                artRes.innerHTML = "<b>Problema al guardar registro en la base de datos.  </b>" + error;
                            } finally {
                                //a timer to enable my button a few secs after everything is done
                            }
                        });
                    }
                );
            }
        });
    });
}

是的,我只是用注释替换了一些代码,使post可读。 我的 js 中有什么地方我应该取消订阅 onAuthStateChanged 吗?

订阅身份验证状态取决于您的用例。

  • 如果您想以反应方式开发您的系统,也就是说,您的 UI 会自行更改,因为它订阅了 auth 状态(没有重新加载或重定向)。那么你应该永不退订授权状态。
  • 相反的情况(您不想订阅),您可以监听身份验证状态更改事件(提交登录表单后来自 Firebase 的响应)并对 UI 进行适当的更改。在这种情况下,您应该在每次 页面加载时检查身份验证状态。这样,您可以重新加载或重定向,因为您将检查身份验证状态。当然,您还必须知道用户何时注销。