为啥我在 nuxt 中的“仅限客户端”组件抱怨“未定义窗口”?
Posted
技术标签:
【中文标题】为啥我在 nuxt 中的“仅限客户端”组件抱怨“未定义窗口”?【英文标题】:Why is my `client-only` component in nuxt complaining that `window is not defined`?为什么我在 nuxt 中的“仅限客户端”组件抱怨“未定义窗口”? 【发布时间】:2020-04-08 08:56:59 【问题描述】:我有 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
,
//...
【问题讨论】:
组件不是这里的问题,而是包含不适合 s-s-r 的传单。<client-only>
所做的只是阻止 s-s-r 期间的渲染,而不是阻止包含脚本。
我明白了。有什么办法可以为这个组件使用像nuxt-leaflet
only 这样的插件?
不,因为即使使用动态导入,它仍然会在 s-s-r 期间被转译。我会假设图书馆的包含必须在有条件的情况下发生
【参考方案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: () => process.client ? import('@/components/Map') : null,
您应该从模式为“客户端”的插件中注册地图组件。 nuxtjs.org/guide/plugins#client-or-server-side-only
@idmean 我希望它仅在必要时加载。插件在全球范围内加载 AFAIK。
地图组件有一个导出默认值。你也知道如何导入被破坏的组件吗?导入功能是否提供此功能?【参考方案2】:
<client-only>
组件并没有像你想象的那样做。是的,它会跳过在服务器端渲染你的组件,但它仍然会被执行!
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 上动态加载它,由于这种自动性,它仍然会在服务器端加载它。
我找到了以下两种解决方案:
将“组件”文件夹重命名为其他名称以停止自动导入,然后使用上述解决方案 (process.client)。
(以及更好的选择 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. 使用 s-s-r 2. 不抛出与缺少标记图像/阴影相关的错误 3.确保传单仅在需要的地方加载(意味着不需要插件) 4.允许自定义图标设置等 5.允许一些插件(它们很痛苦,出于某种原因,我认为你可以将它们添加为插件。结果将它们添加到插件会破坏传单的本地导入并强制它与 vendor.js 捆绑)
将您的模板包装在<client-only></client-only>
中
<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
【讨论】:
以上是关于为啥我在 nuxt 中的“仅限客户端”组件抱怨“未定义窗口”?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 nuxt-auth-next 中进行 google 授权后用户未通过身份验证?
将 Jest 与 Nuxt 组件一起使用时无法读取未定义的属性 $loading
从直接链接导航时,Nuxt.js `this.$route.params.id` 未定义