How can I fix localStorage error in sapper/svelte

我的梦境 提交于 2021-02-11 04:31:06

问题


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;

I still get an error that localStorage is undefined in sapper when I run npm run dev


回答1:


In Sapper, your code will run both in the browser, and in Node for SSR. Real Node, not even a simulated browser environment like jsdom or something. (Not necessarily "will", in fact, it's rather "may", because only the first page that is requested by the browser will be rendered server-side, but still, you need to make sure all your code supports both).

In Node, many browser API, like localStorage, are unavailable.

In JS, you can't test for the existence of a variable like this:

if (!localStorage) ...

If the variable doesn't exist, you'll get a crash with this. A safe way to test for a variable existence is like such:

if (typeof localStorage === 'undefined') ...

And so, make your code browser + Node compatible by rewriting your store something like this:

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()

Note that, even in SSR context (i.e. Node), the browser code will run. The precise order of operations when a page is SSR'd is as follow:

  • the page HTML is rendered in Node with components compiled with ssr: true compile option -- that is, components that just work to render a HTML string in a single pass

  • the HTML is sent to the browser

  • the browser display your page as it should look like

  • in the background, the JS is loaded and executed

  • your Svelte components are rerun with the hydratable option, meaning they will try to reclaim existing DOM elements, instead of creating them inconditionnally

  • your page is now interactive, but it was already good looking in the short (or not so short) interval between getting the HTML and loading the JS

Where I want to go with this, is that your JS will run in the browser, even if the page was rendered by SSR, and the result will replace what the SSR pass has produced. If the client-side JS components produce totally different DOM elements than the server, then Svelte will overwrite.

What this means is, in cases like this, just providing the minimum to let the code not crash in Node is acceptable. If you can produce a result that is close to what the browser will also render, it's better of course. (Another alternative would be to tune your code so that the server would render something like "Loading..." in browser-only cases).



来源:https://stackoverflow.com/questions/65592525/how-can-i-fix-localstorage-error-in-sapper-svelte

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!