在Svelte 3中,出现了"ReferenceError: document未定义"的错误。

31

我正在<script>中尝试手动创建audio,并在每次调用eventListener以替换它时添加appendChild在浏览器中一切都很正常,除了在页面加载时会出现一个非常快速的错误,但持续时间不到100毫秒。终端中也会出现错误。

ReferenceError: document is not defined
    at Object (webpack:///./src/components/Record/Component.svelte?:26:17)

似乎上述情况是在文档尚未准备好时发生的,但之后就会正常,如何解决?或者在 Svelte(Sapper)世界中销毁和重新创建组件的首选方式是什么?

4个回答

59

document在服务器上是未定义的,因此您需要在组件中对此进行保护,以便特定的代码只在浏览器中运行。

您可以使用onMount函数,在组件被渲染时仅在浏览器中运行。

<script>
  import { onMount } from 'svelte';

  onMount(() => {
    document.createElement(...);
    
    // ...
  });
</script>

12
需要转换思维方式,记住最初这一切都在服务器上。 - Mac_W
3
如果您在使用onDestroy时遇到此错误,请尝试使用onMount(() => { return () => { // on destroy code }}) - user5819540

23

Sapper可以很好地与大多数可能遇到的第三方库配合使用。然而,有时候,第三方库以一种允许其与多个不同模块加载器配合工作的方式捆绑在一起。有时,此类代码会创建对window的依赖,例如检查window.global的存在可能会这样做。

由于在Sapper这样的服务器端环境中没有window,因此仅仅导入这样的模块就可能导致导入失败,并以如下错误终止Sapper服务器:

ReferenceError: window is not defined

解决这个问题的方法是,在onMount函数内部(只在客户端调用)使用动态导入(dynamic import)来导入组件,这样你的导入代码永远不会在服务器上被调用。

<script>
    import { onMount } from 'svelte';

    let MyComponent;

    onMount(async () => {
        const module = await import('my-non-ssr-component');
        MyComponent = module.default;
    });
</script>

<svelte:component this={MyComponent} foo="bar"/>


2

感谢Rutuja的答案,我也成功让我的应用程序运行起来了。除了Rutuja的答案之外,如果有人在使用Sapper + Svelte +其他包(在我的情况下是theme.js)时遇到此问题,您可能还需要导入多个组件。这在Mozilla的文档中关于导入的部分中有描述。

When importing a default export with dynamic imports, it works a bit differently. You need to destructure and rename the "default" key from the returned object.

(async () => {
  if (somethingIsTrue) {
    const { default: myDefault, foo, bar } = await import('/modules/my-module.js');
  }
})();

针对我自己的项目(使用theme.js),我不得不使用以下逻辑(基于位于这里的svelte示例中的theme.js):

const themeState = {
    active: undefined,
    selected: undefined,
    themes: [],
}

onMount(async () => {
    const { default: Themer, auto, system } = await import('themer.js')

    themeState = {
        active: undefined,
        selected: light,
        themes: [light, dark, auto, system],
    }

    const themer = new Themer({
        debug: true,
        onUpdate: (theme) => (themeState.active = theme),
        themes: { light, dark, auto, system },
    })

    function noThemeSupport({ theme }) {
        return theme === 'system' && !themer.themeSupportCheck()
    }

    function setTheme(theme) {
        themeState.selected = theme
        themer.set(theme)
    }

    themer.set(themeState.selected)
})

0
根据最新的svelte X d3.js版本4,你可以这样做。

    let mounted = false;
    onMount(() => {
        mounted = true;
    });
    
    // add grid
    $: if(mounted) d3.selectAll('g.yAxis g.tick')
        .append("line")
        .attr("class", "gridline")
        .attr("x1", 0)
        .attr("y1", 0)
        .attr("x2", width_workable)
        .attr("y2", 0)
        .attr("stroke", "#9ca5aecf") // line color
        .attr("stroke-dasharray","4"); // make it dashed;;

另一种方法是

    $: d3.select(elem).selectAll('g.tick')
        .append("line")
        .attr("class", "gridline")
        .attr("x1", 0)
        .attr("y1", 0)
        .attr("x2", width_workable)
        .attr("y2", 0)
        .attr("stroke", "#9ca5aecf") // line color
        .attr("stroke-dasharray","4"); // make it dashed;;

如果你不想管理已挂载的状态

不会出错吗?因为它会尝试在服务器端导入d3。 - Sheece Gardazi
1
不在我的情况下 @SheeceGardazi - CircleOnCircles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接