【问题标题】:Why is my `client-only` component in nuxt complaining that `window is not defined`?为什么我在 nuxt 中的“仅限客户端”组件抱怨“未定义窗口”?
【发布时间】:2023-03-20 03:51:01
【问题描述】:

我有 Vue SPA,我正在尝试迁移到 nuxt。我在一个包含在<client-only> 标签中的组件中使用vue2leaflet,但仍然收到来自nuxt 的错误消息,即window is not defined

我知道我可以使用 nuxt-leaflet 或创建一个插件,但这会显着增加供应商捆绑包,我不希望这样。我只想为需要它的组件导入传单插件。有什么办法吗?

<client-only>
   <map></map>
</client-only>

还有map 组件:

<template>
  <div id="map-container">
    <l-map
      style="height: 80%; width: 100%"
      :zoom="zoom"
      :center="center"
      @update:zoom="zoomUpdated"
      @update:center="centerUpdated"
      @update:bounds="boundsUpdated"
    >
      <l-tile-layer :url="url"></l-tile-layer>
    </l-map>
  </div>
</template>

<script>
import {
  LMap,
  LTileLayer,
  LMarker,
  LFeatureGroup,
  LGeoJson,
  LPolyline,
  LPolygon,
  LControlScale
} from 'vue2-leaflet';
import { Icon } from 'leaflet';
import 'leaflet/dist/leaflet.css';

// this part resolve an issue where the markers would not appear
delete Icon.Default.prototype._getIconUrl;

export default {
  name: 'map',
  components: {
    LMap,
    LTileLayer,
    LMarker,
    LFeatureGroup,
    LGeoJson,
    LPolyline,
    LPolygon,
    LControlScale
  },
//...

【问题讨论】:

  • 组件不是这里的问题,而是包含不适合 ssr 的传单。 &lt;client-only&gt; 所做的只是阻止 ssr 期间的渲染,而不是阻止包含脚本。
  • 我明白了。有什么办法可以为这个组件使用像nuxt-leaflet only 这样的插件?
  • 不,因为即使使用动态导入,它仍然会在 ssr 期间被转译。我会假设图书馆的包含必须在有条件的情况下发生

标签: vue.js leaflet nuxt.js


【解决方案1】:

我找到了一种可行的方法,但我不确定如何。在父组件中,您将 import 语句移动到组件声明中。

<template>
  <client-only>
    <map/>
  </client-only>
</template>

<script>
export default {
  name: 'parent-component',
  components: {
    Map: () => if(process.client){return import('../components/Map.vue')},
  },
}
</script>

【讨论】:

  • 您提出的语法在我的情况下不被接受,但我稍微修改了一下,然后它就可以正常工作了。我将其修改为:Map: () =&gt; process.client ? import('@/components/Map') : null,
  • 您应该从模式为“客户端”的插件中注册地图组件。 nuxtjs.org/guide/plugins#client-or-server-side-only
  • @idmean 我希望它仅在必要时加载。插件在全球范围内加载 AFAIK。
  • 地图组件有一个导出默认值。你也知道如何导入被破坏的组件吗?导入功能是否提供此功能?
【解决方案2】:

&lt;client-only&gt; 组件并没有像你想象的那样做。是的,它会跳过在服务器端渲染你的组件,但它仍然会被执行!

https://deltener.com/blog/common-problems-with-the-nuxt-client-only-component/

【讨论】:

    【解决方案3】:
        <template>
          <client-only>
            <map/>
          </client-only>
        </template>
    
        <script>
          export default {
            name: 'parent-component',
            components: {
              Map: () =>
                if (process.client) {
                  return import ('../components/Map.vue')
                },
            },
          }
        </script>
    

    上述解决方案对我不起作用。

    为什么?这花了我一段时间才知道,所以我希望它可以帮助其他人。

    “问题”是 Nuxt 自动包含“组件”文件夹中的组件,因此您不必手动包含它们。这意味着即使您仅在 process.client 上动态加载它,由于这种自动性,它仍然会在服务器端加载它。

    我找到了以下两种解决方案:

    1. 将“组件”文件夹重命名为其他名称以停止自动导入,然后使用上述解决方案 (process.client)。

    2. (以及更好的选择 IMO)还有另一个功能可以延迟加载自动加载的组件。为此,请在组件名称前加上“lazy-”。这与结合将阻止组件在服务器端呈现。

    最后你的设置应该是这样的 文件:

    ./components/map.vue
    ./pages/index.html
    

    index.html:

        <template>
          <client-only>
            <lazy-map/>
          </client-only>
        </template>
        <script>
        export default {
        }
        </script>
    

    【讨论】:

      【解决方案4】:

      我在这里复制我的答案,因为这是第一个搜索此类问题的帖子,并且在我的情况下使用上述解决方案仍然导致 nuxt 崩溃或错误。

      您可以在挂载的钩子中导入插件,该钩子只能在客户端运行。所以:

      async mounted() {
        const MyPlugin = await import('some-vue-plugin');
        Vue.use(MyPlugin);
      }
      

      我不知道您尝试使用的具体插件,但在我的情况下,我必须在插件的默认属性上调用 Vue.use(),导致 Vue.use(MyPlugin.default)

      【讨论】:

        【解决方案5】:

        这是我在通用模式下使用 Nuxt 的方法: 这将: 1. 使用 SSR 2. 不抛出与缺少标记图像/阴影相关的错误 3.确保传单仅在需要的地方加载(意味着不需要插件) 4.允许自定义图标设置等 5.允许一些插件(它们很痛苦,出于某种原因,我认为你可以将它们添加为插件。结果将它们添加到插件会破坏传单的本地导入并强制它与 vendor.js 捆绑)

        将您的模板包装在&lt;client-only&gt;&lt;/client-only&gt;

        <script>
        let LMap, LTileLayer, LMarker, LPopup, LIcon, LControlAttribution, LControlZoom, Vue2LeafletMarkerCluster, Icon
        if (process.client) {
          require("leaflet");
          ({
            LMap,
            LTileLayer,
            LMarker,
            LPopup,
            LIcon,
            LControlAttribution,
            LControlZoom,
          } = require("vue2-leaflet/dist/vue2-leaflet.min"));
          ({
            Icon
          } = require("leaflet"));
          Vue2LeafletMarkerCluster = require('vue2-leaflet-markercluster')
        
        }
        
        import "leaflet/dist/leaflet.css";
        export default {
          components: {
            "l-map": LMap,
            "l-tile-layer": LTileLayer,
            "l-marker": LMarker,
            "l-popup": LPopup,
            "l-icon": LIcon,
            "l-control-attribution": LControlAttribution,
            "l-control-zoom": LControlZoom,
            "v-marker-cluster": Vue2LeafletMarkerCluster,
           
          },
        
          mounted() {
            if (!process.server) //probably not needed but whatever
             {
              // This makes sure the common error that the images are not found is solved, and also adds the settings to it.
              delete Icon.Default.prototype._getIconUrl;
              Icon.Default.mergeOptions({
                // iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), // if you want the defaults
                // iconUrl: require('leaflet/dist/images/marker-icon.png'), if you want the defaults
                // shadowUrl: require('leaflet/dist/images/marker-shadow.png') if you want the defaults
                shadowUrl: "/icon_shadow_7.png",
                iconUrl: "/housemarkerblue1.png",
                shadowAnchor: [10, 45],
                iconAnchor: [16, 37],
                popupAnchor: [-5, -35],
                iconSize: [23, 33],
                // staticAnchor: [30,30],
              });
            }
          },
        
        并且有使用 nuxt build --modern=server --analyze 的证明 https://i.stack.imgur.com/kc6q4.png

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-15
          • 2022-09-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多