lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
624 stars 34 forks source link

sessionStorage 的数据会在同一网站的多个标签页之间共享吗?这取决于标签页如何打开 #66

Open lmk123 opened 6 years ago

lmk123 commented 6 years ago

一直以来,我所以为的 sessionStorage 的生命周期是这样的:在 sessionStorage 中存储的数据会在当前浏览器的同一网站的多个标签页中共享,并在此网站的最后一个标签页被关闭后清除。注意:这是错误的。

我之所以会这么认为,是因为我写代码的时候,sessionStorage 给我的表现就是这样的。

假设我们有一个 index.html

<!-- 使用一个新标签页打开自身,并设置一个 sessionStorage -->
<a href="index.html" target="_blank" onclick="sessionStorage.setItem('j', 's')">
  open myself
</a>

接下来:

  1. 在浏览器中打开这个 index.html,我们称之为标签页 A。注意:需要用 http 协议打开!例如 http://localhost/index.html
  2. 点击页面上的链接,此时会弹出来标签页 B。
  3. 在标签页 B 中打开控制台并执行 sessionStorage.getItem('j')

控制台会输出 's',这说明标签页 A 和 B 共享了 sessionStorage 中的数据;接下来,先关闭这两个标签页,然后再打开一个标签页 C,再读取一下 j 的值,得到的是 null

这看起来跟本文一开始的说法是一致的,但今天我遇到了一个奇怪的事情……

我们给上面的步骤添加第四步

  1. 在浏览器中打开这个 index.html,我们称之为标签页 A。注意:需要用 http 协议打开!例如 http://localhost/index.html
  2. 点击页面上的链接,此时会弹出来标签页 B。
  3. 在标签页 B 中打开控制台并执行 sessionStorage.getItem('j'),得到 's'
  4. 新建一个新标签页 D,然后在地址栏内输入 http://localhost/index.html 打开同样的页面, 然后执行 sessionStorage.getItem('j')

按照我的预期,标签页 D 得到的应该还是 's',毕竟我认为 sessionStorage 的数据是在同一网站的多个标签页之间共享的。但是我错了,得到的结果是 null

发生了什么?为什么标签页 B 中得到的是 's',为什么标签页 D 中却是 null

细心的同学可能已经发现了,标签页 B 和标签页 D 之间唯一的不同就是它们被打开的方式:标签页 B 是通过在标签页 A 中点击链接打开的,但标签页 D 是在浏览器地址栏输入地址打开的。

我赶紧上 MDN 查了一下,上面是这么说的:

...data stored in sessionStorage gets cleared when the page session ends...Opening a page in a new tab or window will cause a new session to be initiated, which differs from how session cookies work.

所以现在我明白了:通过点击链接(或者用了 window.open)打开的新标签页之间是属于同一个 session 的,但新开一个标签页总是会初始化一个新的 session,即使网站是一样的,它们也不属于同一个 session。


一点题外话

这个问题是我今天在 Stack Overflow 上回答一个问题的时候发现的,在答案的例子中我使用 sessionStorage 保存了一个状态,然后测试的时候发现新开的标签页居然读不到。

这也从侧面验证了一个道理:在帮助别人的时候,自己也会从中得到提升。

所以我决定以后多多帮助别人 😂

P.S. 之所以今天会跑到 Stack Overflow 上回答问题是因为我必须有一个答案被采纳之后才够等级给别人的答案投票 😂

zhehuaxuan commented 6 years ago

题主,对于新开一个tab,然后输入网址的方式,要想共享sessionStorage应该怎么做?

kid1412621 commented 6 years ago

@myst729 还能转卖?

Zeus-Iqd commented 6 years ago

哈哈,我也遇到了这个问题。

wenshaofeng commented 5 years ago

很有帮助,感谢

terminalqo commented 5 years ago

题主,对于新开一个tab,然后输入网址的方式,要想共享sessionStorage应该怎么做?

用localstorage咯?

fakerBABA commented 5 years ago

题主,对于新开一个tab,然后输入网址的方式,要想共享sessionStorage应该怎么做?

class MemoryStorage { constructor(handler) { this.storage = {} this.handler = handler }

isEmpty() {
    const o = this.storage

    for (var i in o) {
        return false
    }
    return true
}

addSessionToStorage() {
    for (let i = 0; i < sessionStorage.length; i++) {
        const key = sessionStorage.key(i)

        this.storage[key] = sessionStorage.getItem(key)
    }
}

addStorageToSession() {
    let data = localStorage.getItem('sessionStorage')

    data = JSON.parse(data)

    for (let i in data) {
        sessionStorage.setItem(i, data[i])
    }
}

addEvent() {
    window.addEventListener('storage', event => {
        if (event.key === 'getSessionStorage') {
            this.addSessionToStorage()
            localStorage.setItem('sessionStorage', JSON.stringify(this.storage))
        } else if (event.key == 'sessionStorage' && this.isEmpty()) {
            this.addStorageToSession()
            localStorage.removeItem('sessionStorage')
        }
    })
}

}

const memory = new MemoryStorage()

export const Memory = function() { memory.addEvent()

if (memory.isEmpty()) {
    localStorage.setItem('getSessionStorage', Date.now());
}

}

export const memoryInstance = memory

上面的代码就是共享状态的 今天刚折腾出来的 核心就是window.addEventListener

wangzhijun-github commented 5 years ago

当你把B页面的seesionStorage的值改变了,你会发现A页面的sessionStorage的值没有改变

xmsz commented 4 years ago

当你把B页面的seesionStorage的值改变了,你会发现A页面的sessionStorage的值没有改变

这个确实也是很蛋疼,新开窗口实际上只是继承了,并不是共享

jellywhere commented 3 years ago

you are wrong! when you open a new tab by target, session will also be cleared !

toFrankie commented 3 years ago

简单总结:

PS:同源和同站是有区别的,同源更严格一些。

myst729 commented 3 years ago

cookie还可以设置path

toFrankie commented 3 years ago

cookie还可以设置path

对对对,CookieDomainPath 限制。我这里只是为了突出同源和同站,所以假定 Domain二级域名,Path 为 /

我去修改下

wuzhengyan2015 commented 6 months ago

简单总结:

  • localStorage - 只要是同源,不同 Tab 之间均可读写,相互影响。
  • sessionStorage - 前提还是同源,

    • 同一 Tab 的所有同源可读写且相互影响。
    • 不同 Tab 之间,读写操作独立,“互不影响”。
    • 上面打引号原因是,<a target="_blank"></a>window.open() 两种方式创建新 Tab 的初始缓存不一样。前者是全新的一个 sessionStorage 对象,且初始值为空。后者则基于原页面的 sessionStorage 拷贝一份,并作为新 Tab 的初始缓存值。
  • Cookie - 只要 DomainPath 一致情况下,不同 Tab 之间即可相互读取。Cookie 最宽松的情况是同站即可,就是说 Domain 设为二级域名、Path 设为 /

PS:同源和同站是有区别的,同源更严格一些。

试了下 <a href="" target="_blank" rel="opener">链接</a> 会携带 sessionStorage,是否携带和 rel 属性有关,window.open 同理。参考这个文章 https://juejin.cn/post/6979038859254300703