问题
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 passthe 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 inconditionnallyyour 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