英文:
Implementing singleton with vanilla JavaScript in frontend
问题
我有一个项目(纯粹的Web应用程序),我在使用Node.js路由器从后端提供HTML文件:
router.get("/", (req, res) => {
    res.sendFile(views_dir + "index.html");
});
router.get("/login", (req, res) => {
    res.sendFile(views_dir + "login.html");
});
并将JS文件作为静态文件提供app.use(express.static(path.join(__dirname, '/frontend')));,其中/frontend文件夹包含所有前端文件(包括HTML文件)。
现在,我有一个名为model.js的文件(在frontend文件夹中),我正在尝试实现一个单例,用于保存所有页面通用的数据(例如用户当前是否已登录):
const Model = (function () {
    let instance;
    function init() { 
        let isLogged = false;
        return {
            getIsLogged: function () {
                return isLogged;
            },
            setIsLogged: function (l) {
                isLogged = l;
            }
        }
    }
    return { 
        getInstance: function () {
            if (! instance) {
                instance = init();
            }
            return instance;
        }
    }
})();
但是当用户被重定向时,似乎model.js从后端再次被导入,覆盖了先前页面对模型的所有更改。
(假设我们在用户登录时将model中的isLogged变量更改为true,然后重定向到'/',模型将被刷新并覆盖)
是否有办法只使用JavaScript实现这种要求?
我认为问题出在res.sendFile(views_dir + "index.html");,它刷新了应用程序到新的状态,不会保存js文件的先前状态,是否有方法可以避免这种情况?只请求一次js文件?
英文:
I have a project ( vanilla web application ) where I serve html files from the backend using nodejs router:
router.get("/", (req, res) => {
    res.sendFile(views_dir + "index.html");
});
router.get("/login", (req, res) => {
    res.sendFile(views_dir + "login.html");
});
and serve the js files as static  app.use(express.static(path.join(__dirname, '/frontend'))); where the /frontend folder holds all the frontend files ( including the html files ).
Now, I have a file called model.js ( in frontend folder ) where I'm trying to implement a singleton that will hold data that is common to all pages ( such as if the user is currently logged or not ):
const Model = (function () {
    let instance;
    function init() { 
        let isLogged = false;
        return {
            getIsLogged: function () {
                return isLogged;
            },
            setIsLogged: function (l) {
                isLogged = l;
            }
        }
    }
    return { 
        getInstance: function () {
            if (! instance) {
                instance = init();
            }
            return instance;
        }
    }
})();
But when a user being redirected, seems like the model.js being imported all over again from the backend side, overriding all changes to the model from the previous page.
( Say we change the isLogged variable in the model to true when a user logged in, and redirecting to '/', the model being refreshed and overwritten )
Is there even a way to implement such a requirement with just JavaScript ?
I think the problem is with res.sendFile(views_dir + "index.html"); which refreshes the application to a new state and doesn't saves the previous state of the js files, is there a way to avoid this ? to request the js files just once ?
答案1
得分: 3
感谢@Jared的帮助解决了这个问题。
我使用localStorage来保存模型的当前状态,并在重定向到另一个页面时加载它。
const Model = (function () {
    let instance;
    let data = {
        isLogged: false,
    };
    function init_localStorage() {
        save_localStorage();
    }
    function load_localStorage() {
        let model = JSON.parse(localStorage.getItem('Model'));
        if (Object.keys(model).length === 0) {
            init_localStorage();
        }
        data = model;
    }
    function save_localStorage() {
        localStorage.setItem('Model', JSON.stringify(data));
    }
    function init() {
        load_localStorage();
        return {
            getIsLogged: function () {
                return data.isLogged;
            },
            setIsLogged: function (l) {
                data.isLogged = l;
            },
            saveData: function () {
                save_localStorage();
            }
        }
    }
    return {
        getInstance: function () {
            if (! instance) {
                instance = init();
            }
            return instance;
        }
    }
})();
最终,我有两个主要的函数:save... 和 load... 用于读取/保存localStorage对象中的数据。正如Jared在评论中所述,JSON无法对函数进行字符串化,因此我创建了一个名为data的对象,其中存储了模型的所有数据(例如 isLogged 变量)。
现在,每当我想要访问模型的数据时,首先获取其实例:let model = Model.getInstance();,然后可以访问从init函数中返回的方法。当我请求模型实例时,它首先检查当前实例是否尚未初始化。如果没有,它将调用load_localStorage来读取localStorage并将其保存到data变量中。
在重定向到另一个页面之前,我调用Model的实例的saveData来将数据对象保存到localStorage中,以便重定向后的页面可以读取先前保存的状态。
在HTML文件中,我像这样包含了js文件:<script src="./../model/model.js"></script>。
我确信有更好的解决方案来解决这个问题,也许有更可读的代码,但这对我来说完全可以正常工作 ![]()
编辑:
需要注意的是,sessionStorage 可能比 localStorage 更好,因为localStorage不会过期,这意味着如果您保存的内容在服务器刷新/更新时需要刷新/清除,它仍然会从localStorage加载旧数据,因为没有发生变化。
例如,我在模型中保存了一些常见的HTML部分在localStorage中,每次运行 npm start 时它不会显示我对它所做的更改,因为我是从 localStorage 而不是从服务器再次加载HTML部分的。
英文:
Figured it out thanks to @Jared
I used localStorage to save the model's current state and load it back when redirecting to another page.
const Model = (function () {
    let instance;
    let data = {
        isLogged: false,
    };
    function init_localStorage() {
        save_localStorage();
    }
    function load_localStorage() {
        let model = JSON.parse(localStorage.getItem('Model'));
        if (Object.keys(model).length === 0) {
            init_localStorage();
        }
        data = model;
    }
    function save_localStorage() {
        localStorage.setItem('Model', JSON.stringify(data));
    }
    function init() {
        load_localStorage();
        return {
            getIsLogged: function () {
                return data.isLogged;
            },
            setIsLogged: function (l) {
                data.isLogged = l;
            },
            saveData: function () {
                save_localStorage();
            }
        }
    }
    return {
        getInstance: function () {
            if (! instance) {
                instance = init();
            }
            return instance;
        }
    }
})();
Eventually, I have two main function: save... and load... which reads\saves from the localStorage object.
As Jared stated in the comments, JSON cannot stringify a function, thus I created an object called data where I store all the model's data ( for example isLogged variable ).
Now, whenever I want to access the model's data, I get its instance first: let model = Model.getInstance();
and then I can access the methods from the init function that I'm returning.
When I request the Model instance, it first checks if the current instance is not yet initialized. If not, it calls the load_localStorage which reads localStorage and saves it to data variable.
Before I redirect to another page I call saveData of Model's instance to save the data object to localStorage, so the page redirected to can read the previous saved state.
In HTML files, I included the js files like this: <script src="./../model/model.js"></script>.
I'm sure there is a better solution to the problem, and maybe a more readable code, but this got me working just fine ![]()
Edit:
It is important to note that sessionStorage might be a better solution than localStorage as localStorage doesn't doesn't expire, meaning if you save things you want to be refreshed \ wiped out when the server is refreshed\updated, it will still load old data from localStorage as nothing changed.
For example, I saved some common HTML part in the model in localStorage, and each time I ran npm start it didn't show the changes I made to it, as I'm loading the HTML part from localStorage and not again from the server.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论