【问题标题】:Firebase JS: Why and When to unsubscribe onAuthStateChangedFirebase JS:为什么以及何时取消订阅 onAuthStateChanged
【发布时间】:2022-02-14 23:51:13
【问题描述】:

我在一个奇怪的网站上工作,我只需要将表单发送到 firestore 并将 pdf 文件保存到 firestorage。 甚至不运行任何服务器端代码,只在最终客户端上运行 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 模块而不是使用 import 语句。
  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
                            }
                        });
                    }
                );
            }
        });
    });
}

是的,我只是用 cmets 替换了一些代码以使这篇文章可读。 我的 js 中有什么地方我应该取消订阅 onAuthStateChanged 吗?

【问题讨论】:

    标签: javascript html firebase firebase-authentication


    【解决方案1】:

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

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

    【讨论】:

    • 好的,谢谢,我会继续订阅表单和监听器,以便在注销时重定向到登录。但仍然不确定我是否应该取消登录,因为我将在登录完成后立即重定向到仪表板。
    • 这完全取决于您如何处理页面上依赖于身份验证的内容。也许您想在它更改的确切时刻处理身份验证更改(例如在 SPA 中),或者您可能希望在每次服务器提供页面时处理它(例如在 MPA 中)
    • SPA 和 MPA 只是示例,如果需要,您实际上可以使用混合方法
    猜你喜欢
    • 1970-01-01
    • 2018-04-13
    • 2017-08-03
    • 2018-05-08
    • 2021-06-14
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多