现象:
我敢说这是nuxt3中出现的最让人头痛的问题之一,非常难搞。
原因:被渲染组件中使用了dom原生api,但是它的代码却打包到ssr服务端环境中,众所周知服务端环境(node环境)是没有window/document/navigator这些原生js对象的。
解决办法有三种:
方法一(不推荐): 使用ClientOnly标签把该组件中存在document渲染问题的html部分包裹住
The
<ClientOnly>
component renders its slot only in client-side. To import a component only on the client, register the component in a client-side only plugin.
这种方法不是很推荐,因为这个方法太简单粗暴,严重怀疑会把本不应该做csr的代码打包进浏览器客户端js中,这样减少了服务端渲染的展现量,也加重了客户端的打包体积。
而且每个有问题的组件都要加上ClientOnly标签
方法二(不推荐)、用一个变量isClient来判断是否为浏览器环境,然后用v-if判断是否显示scoll组件,同时在onMount挂载之前通过process.client给isClient赋值。
<wrap-scroll v-if="isClient" class="recommend-content">
xxxx
interface State extends RecommendResp {
isClient: boolean | false;
}
const state = reactive<State>({
isClient: false
});
onMounted(() => {
state.isClient = process.client
});
这种方法也不是很推荐,因为增加了代码量,而且每个用到scroll组件的页面都要这样添加代码。
方法三(推荐): 从问题根源入手,在scroll组件本身,通过process.client判断是否浏览器环境,是浏览器环境才执行组件代码:
setup(props, { emit }) {
if (process.client) { // 仅在浏览器环境才执行组件代码
const rootRef = ref<HTMLDivElement>(document.createElement("div"));
const scroll = useScroll(rootRef, props, emit);
return {
rootRef,
scroll,
};
}
},
注意必须是在scroll根组件做浏览器环境判断!
在包装组件wrapper做这种判断没有用!
这样就能从根本解决问题,修改这一个地方就够了!