import { writable } from 'svelte/store';
/** Read the current token from LocalStorage on boot */
const token = writable(localStorage.getItem('token'));
/** Store the token in LocalStorage whenever it´s updated */
token.subscribe((val) => {
if (!localStorage) return;
if (!val) {
return localStorage.removeItem('token');
}
localStorage.setItem('token', val);
});
export default token;
当我运行npm run dev
时,我仍然收到 localStorage 在 sapper 中未定义的错误
最佳答案
在 Sapper 中,您的代码将在浏览器和 SSR 的 Node 中运行。真正的Node,甚至不是像jsdom之类的模拟浏览器环境。 (不一定是“将”,事实上,它是“可能”,因为只有浏览器请求的第一个页面才会在服务器端呈现,但您仍然需要确保所有代码都支持这两种页面)。
在 Node 中,许多浏览器 API(例如 localStorage
)不可用。
在 JS 中,你不能像这样测试变量是否存在:
if (!localStorage) ...
如果该变量不存在,您将会遇到崩溃。测试变量是否存在的安全方法如下:
if (typeof localStorage === 'undefined') ...
因此,通过重写您的商店,使您的代码与浏览器 + Node 兼容:
const createBrowserTokenStore = () => {
const store = writable(localStorage.getItem('token'))
// Store the token in LocalStorage whenever it´s updated
store.subscribe((val) => {
if (!localStorage) return
if (!val) return localStorage.removeItem('token')
localStorage.setItem('token', val)
})
return store
}
// just enough to not crash in Node
const createNodeTokenStore = () => writable(null)
export const token = typeof localStorage === 'undefined'
? createNodeTokenStore()
: createBrowserTokenStore()
请注意,即使在 SSR 上下文(即 Node)中,浏览器代码也会运行。页面SSR时的具体操作顺序如下:
页面 HTML 在 Node 中呈现,组件使用
ssr: true
编译选项进行编译 - 即,仅在一次传递中呈现 HTML 字符串的组件HTML 被发送到浏览器
浏览器会按应有的样子显示您的页面
JS在后台加载并执行
您的 Svelte 组件将使用
Hydratable
选项重新运行,这意味着它们将尝试回收现有的 DOM 元素,而不是无条件地创建它们您的页面现在是交互式的,但在获取 HTML 和加载 JS 之间的短暂(或不那么短)间隔内,它已经很漂亮了
我想说的是,即使页面是由 SSR 渲染的,您的 JS 也将在浏览器中运行,并且结果将替换 SSR 过程生成的结果。如果客户端 JS 组件生成与服务器完全不同的 DOM 元素,则 Svelte 将覆盖。
这意味着,在这种情况下,只要提供最小值以使代码不会在 Node 中崩溃就可以接受。如果您可以生成接近浏览器渲染结果的结果,那当然更好。 (另一种选择是调整您的代码,以便服务器在仅浏览器的情况下呈现类似“正在加载...”的内容)。
关于svelte - 如何修复 sapper/svelte 中的 localStorage 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65592525/