在前端使用纯JavaScript实现单例模式。

huangapple go评论82阅读模式
英文:

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>

我确信有更好的解决方案来解决这个问题,也许有更可读的代码,但这对我来说完全可以正常工作 在前端使用纯JavaScript实现单例模式。

编辑:

需要注意的是,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 在前端使用纯JavaScript实现单例模式。

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.

huangapple
  • 本文由 发表于 2023年8月4日 03:23:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76831075.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定