如何存储/存储JavaScript事件并在以后重用它?

时间:2018-05-22 06:57:15

标签: javascript events local-storage web-storage

来自https://developers.google.com/web/fundamentals/app-install-banners/#trigger-m68

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  // Stash the event so it can be triggered later.
  deferredPrompt = e;
});

这段代码很好,但我想稍后在另一个地方触发藏匿事件。为了执行该操作,我需要将事件存储在变量中,而不是存储在其他地方。

问题:如何使用其方法存储事件?

我尝试使用对象的序列化/反序列化进行本地存储:

> localStorage.setItem('stashed-event', JSON.stringify(e))
>
> JSON.parse(localStorage.getItem('stashed-event'))

但是这种方法并没有按预期工作,因为它只存储键值并丢失所有事件方法。

6 个答案:

答案 0 :(得分:9)

您无法以这种方式存储活动。您想要存储一个对象。只有可序列化的属性才能存储在这样的对象中。函数在JavaScript中不可序列化。函数不能用多种语言序列化。

从根本上说,这基本上是因为当您反序列化对象时,其签名可能会发生变化。如果您曾在java中编程,则在读取序列化对象并尝试重建对象时,这类似于反序列化错误。因为对象的方法函数的主体可以在将对象写入某个存储器然后稍后读取之间进行更改,所以方法不可序列化。这是因为在序列化对象时,它不会序列化定义方法的接口定义。它只存储数据。

当您序列化为json字符串时,它会丢弃函数。

不是存储事件,而是将事件中的有用信息存储在对象中(或者让stringify隐式删除并直接使用事件)。

您使用的存储方法取决于您问题中未提及的内容。例如应该存储多长时间,是否应该在您网站的原点之外提供,通常存储多少数据,是否存储多个对象等等。根据您问题中提供的有限信息,你可能只使用localStorage或内存数组。

如果您发现需要存储数百个对象,那么indexedDB将开始更合适。但是,只选择不同的存储介质对于是否可以存储功能没有任何影响。你不能存储功能。

答案 1 :(得分:8)

一旦I / O 2018提到有关A2HS活动的处理是从现在开始驱动的开发人员,就有很多关于此的讨论。这也在official doc中捕获,并从中受到启发,有一个beautiful article详细解释了如何实现这种情况。虽然我建议阅读完整的文章以正确理解A2HS流程的更新动态,但请随时跳转到“新添加到主屏幕流程”部分以满足您的要求。

简而言之,请按照以下步骤操作:

  1. beforeinstallprompt事件处理程序范围之外创建一个变量。
  2. 在上述处理程序中保存对beforeinstallprompt事件对象的引用。
  3. 稍后使用此按钮可根据需要触发添加到主屏幕提示。
  4. 该文章包含完整的代码片段,您可以参考/重复使用。

    编辑:我再次阅读了您的问题,并意识到您可能会特别寻找的一个重要方面,即“在其他地方”使用它。如果这意味着您指的是在不同的页面上使用它,那么我的建议是将事件对象存储在:

    1. IndexedDB,它是“对象存储”的集合,您可以将对象放入其中。 缺点 - 可能存在浏览器兼容性限制。此外,还可能导致大量嵌套回调。
    2. 或者您可以选择使用不需要序列化的“进程内缓存”(应用程序的堆内存)。 缺点 - 但不能在多台服务器之间共享。
    3. 除此之外,我现在无法预见到免费解决方案。但是会尝试解决它并可能更新线程。

答案 2 :(得分:4)

几次阅读你的问题后,又回答了几个问题,

问题:如何使用其方法存储任何javascript对象?

答案:没有。

<强>然而,

Josh正确explained您可以从活动中提取并存储所有可序列化的属性,例如数据。

我会在以后的任何地方添加可以创建一个事件 以某种方式 相同数据,这个新事件将包含所有方法Event有,但现在可能没有用。

显然,即使在您的数据中序列化,也会在创建新事件时覆盖timeStampisTrusted等属性。

您错过/需要的是EventTarget event.target 属性的值, 当document.body永远卸载或者序列化事件对象时,引用永远丢失。

但是如果它还活着,或者你知道event.target应该是什么,比如DOM元素或任何Object 你可以引用,从你重新创建事件的地方(哪里?),只要将事件发送到该对象,如果它侦听到event.type, 你的全新活动至少应该被听到。

来自MDN的简单示例 EventTarget,或参见EventTarget.dispatchEvent

extensive answercegfault的评论:评估和文字源代码......可以是&lt; script&gt; 文字源代码&lt; / script&gt; ...您的脚本是否应生成(String)脚本。如果不是,你可能会更好地向后退到你的脚本创建事件中出现的不可序列化的东西的位置,并考虑重新创建这些东西,而不是事件。

答案 3 :(得分:2)

TL; DR要完成你正在做的事情,你有三个选择:

  1. 参考存储在全局值的事件中(这是大多数教程 - 就像您引用的YouTube视频一样 - 会建议您这样做)。这要求事件在与存储引用
  2. 相同的上下文(即网页)中运行
  3. 引用存储到localStorage中的事件(例如通过名称或键/值查找)时,在要执行事件的页面/上下文中,确保在执行事件
  4. 之前加载适当的函数和库
  5. [强烈建议不要]将javascript源代码存储在你的会场中,并在以后eval()[再次,请不要这样做]
  6. 正如@Josh和@SaurabhRajpal所提到的,严格来说,在JavaScript中不可能提出要求。您使用JSON.stringify(e)所做的工作可能会返回undefinednull,而MDN documentation for JSON.stringify会说:

      

    如果未定义,在转换过程中遇到函数或符号,则省略(在对象中找到它)或删除为null(在数组中找到它时)。传入时,JSON.stringify也可以返回undefined&#34; pure&#34;值如JSON.stringify(function(){})或JSON.stringify(undefined)。

    简而言之,无法将单个功能存储到localStorage(或任何其他离线存储)。要解释为什么这是不可能的,请参阅此示例:

    function foo() {
        console.log("a")
    }
    
    function bar() {
        return foo()
    }
    

    如何存储bar()以供日后使用?为了存储条形图,您必须存储foo()。当您考虑引用大型库中的函数或使用大型库(如jQuery,下划线,D3,图表库等)时,这会变得更加复杂。请记住您的计算机已将源代码解析为二进制文件,因此很难知道如何为每个可能的if,for和switch语句读取函数以确保所有保存了可能的相关函数和库。

    如果真的想要这样做,你必须编写自己的javascript解析器,而你真的不想这样做!

    那你有什么选择?首先,在同一页面上执行所有操作,并将事件的引用存储在全局值中(您在评论中链接到的YouTube视频使用此方法)。

    您的第二个选择是对事件使用引用(而不是事件本身),并确保稍后使用该引用的源代码。对于(html)示例:

    // on page #1
    <script src="path/to/my/js/library.js"></script>
    ...
    <script>
        window.addEventListener('beforeinstallprompt', (e) => {
            e.preventDefault()
            localStorage.setItem('stashed-event', 'before-install')
        })
    </script>
    
    // later, on page #2:
    <script src="path/to/my/js/library.js"></script>
    ...
    <script>
        var evt = localStorage.setItem('stashed-event', 'before-install')
        if(evt == 'before-install') {
            dosomething() // which would be a function in path/to/my/js/library.js
        }
        // another option here would be to define window listeners for all possible events
        // in your library.js file, and then simply build and trigger the event here. for
        // more about this, see: this link:
        // https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
    </script>
    

    最后,您可以存储javascript源代码,然后再使用eval()。拜托,请不要这样做。这是一种不好的做法,可能导致非常邪恶的事情。但是,如果你坚持:

    // on page #1
    window.addEventListener('beforeinstallprompt', (e) => {
        e.preventDefault()
        SomeAjaxFunction("path/to/my/js/library.js", function(responseText) {
            localStorage.setItem('stashed-event', {
                name: 'before-install',
                call: 'beforeInstallFunction()',
                src:  responseText
            })
        })
    })
    
    // later, on page #2:
    var evt = localStorage.setItem('stashed-event', 'before-install')
    if(evt) {
        console.log("triggering event " + evt.name)
        eval(evt.src)
        eval(evt.call)
    }
    

    就像我说的,这是一个非常糟糕的主意,但它是一个选择。

    恕我直言,我认为您正试图避免在以后的页面/ app /中包含库或源代码,而javascript只是不能以这种方式工作。最好在内存中传递引用,并且只对名称使用键/值存储。其他一切都是一种编码体操,以避免简单地将源代码包含在需要包含的地方。

答案 4 :(得分:1)

您可以创建一个全局常量,并在事件发生变化时更新它,而不是将其序列化并反序列化,这是一个代价高昂的过程。这就是你如何做到这一点 - 你可以创建一个窗口实例并在窗口对象中克隆事件,这样它就不会发生变异。(注意这不会在标签之间起作用)

window.addEventListener('click', (e) => {
  e.preventDefault();
  window.deferredPrompt = Object.assign(e);//Don't mutate
});

let someOtherMethod = ()=>{
  console.log(window.deferredPrompt)
}

window.setInterval(someOtherMethod, 5000);

在最后一个窗口中尝试点击5秒钟,然后在5秒后检查

答案 5 :(得分:-2)

请查看这是否有帮助。

为'beforeinstallprompt'事件定义事件侦听器

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  //do all the stufff
  console.log('Event triggered');
});

如果要手动调度事件。 创建一个新的事件变量。

var myevent = new Event('beforeinstallprompt');
window.dispatchEvent(myevent);

在控制台中输出“触发事件”。