Nuxt3之渲染模式(下) | 青训营笔记

846 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

引言

我们都知道SSR是什么。Server-side Rendering 我们也知道Nuxt3是一个基于Vue3的SSR框架,我们通常使用universal-rendering模式。 但当我们初学Nuxt3的时候,我们会发现Nuxt3文档并没有对universal-rendering部分有过多的解释。 翻看很多教程,也没有很好的解释。他没有很明确的解释,哪部分是客户端渲染、哪部分是服务端渲染。(当然,仔细翻确实能找到,但是笔者画了很久)

NextJs

相应的,笔者在接触Nuxt之前,先接触了NextJs。它的文档在这方面就非常好。刚开始学,就明白,我们如果想要代码在服务端处理,直接使用getServerSideProps即可。 image.png 但是,当我们回头看Nuxt3,我们发现,好像这么重要的内容,就没有很明确的解释。反而,文档花了很长的篇幅讲了它的一些约定式路由等功能。这直接让笔者误以为它的代码除了不特殊指定,都是在服务端渲染的。直到这一件事发生。

渲染问题

image.png 笔者在开发该组件的时候并没有遇到什么问题,直到一次网站部署,发现这个组件存在一个跳变问题。这个跳变极其影响体验,因此,笔者很重视。 跳变就是,刚开始加载网页,它会显示上午好,过一会又变成下午好,然而我们编写的代码理论上不可能出现这个问题。

<script setup lang="ts">
const hours = useDateFormat(useNow(), 'HH')
const state = ref('')
if (parseInt(hours.value) >= 0 && parseInt(hours.value) <= 11)
  state.value = '早上好!'
else if (parseInt(hours.value) > 11 && parseInt(hours.value) <= 14)
  state.value = '中午好!'
else if (parseInt(hours.value) > 14 && parseInt(hours.value) <= 18)
  state.value = '下午好!'
else if (parseInt(hours.value) > 18 && parseInt(hours.value) <= 24)
  state.value = '晚上好!'
</script>

仔细分析,我们得出了一个大胆猜想。这段代码在服务端和客户端都运行了一遍。我们部署的服务器在国外,因此,程序运行得到的时区不是北京时区。我们得到的结果也是会让他跳变。这一切都说得通了。于是乎,我在stackblitz进行了测试,最终证实了这个结论。

stackblitz.com/edit/nuxt-s…

解决方案

1.

我们可以直接加一个判断,让程序,直接在服务端运行。(当然,需要获取北京时区的时间)这里,nuxt提供了process

<script setup lang="ts">
const hours = useDateFormat(useChineseNow(), 'HH')
const state = ref('')
if (process.server){
  if (parseInt(hours.value) >= 0 && parseInt(hours.value) <= 11)
  state.value = '早上好!'
  else if (parseInt(hours.value) > 11 && parseInt(hours.value) <= 14)
    state.value = '中午好!'
  else if (parseInt(hours.value) > 14 && parseInt(hours.value) <= 18)
    state.value = '下午好!'
  else if (parseInt(hours.value) > 18 && parseInt(hours.value) <= 24)
    state.value = '晚上好!'
}

</script>

2.

我们可以直接把计算的部分放入onBeforeMount生命周期中运行。但是这种方法有问题,我们其他地方都是在服务端都渲染好了,但是state只会在客户端执行。网页会直接把服务端渲染好的展示后再执行客户端的生命周期。这就导致用户会看到一段时间state为空的阶段。这还不如跳变问题呢。。。

<script setup lang="ts">
const hours = useDateFormat(useNow(), 'HH')
const state = ref('')
onBeforeMount(()=>{
  if (parseInt(hours.value) >= 0 && parseInt(hours.value) <= 11)
  state.value = '早上好!'
  else if (parseInt(hours.value) > 11 && parseInt(hours.value) <= 14)
    state.value = '中午好!'
  else if (parseInt(hours.value) > 14 && parseInt(hours.value) <= 18)
    state.value = '下午好!'
  else if (parseInt(hours.value) > 18 && parseInt(hours.value) <= 24)
    state.value = '晚上好!'
})
</script>

通用渲染

我们解决了问题,但不能知其然而不知所以然。笔者再一次翻阅全网,翻阅文档,终于理解了整件事。Nuxt3的渲染模式,不是跟Nextjs一样,有明确的函数去区分。我们重点要关注的是Nuxt3的生命周期。 de48ca.svg 我们可以从生命周期发现,Nuxt一部分在服务端渲染,一部分在客户端渲染,甚至,一部分生命阶段在服务端和客户端都执行。 由于我们使用的是Vue3 Setup,没有什么create生命阶段。 我们已经知道了,我们的setup阶段是客户端服务端共同执行的阶段。其他部分,可以参阅下面的表格。

Lifecycle Hooks

App Hooks (runtime)

HookArgumentsEnvironmentDescription
app:createdvueAppServer & ClientCalled when initial vueApp instance is created.
app:errorerrServer & ClientCalled when a fatal error occurs.
app:error:cleared{ redirect? }Server & ClientCalled when a fatal error occurs.
app:data:refreshkeys?Server & Client(internal)
vue:setup-Server & Client(internal)
vue:errorerr, target, infoServer & ClientCalled when a vue error propages to the root component. Learn More
.
app:renderedrenderContextServerCalled when SSR rendering is done.
app:redirected-ServerCalled before SSR redirection.
app:beforeMountvueAppClientCalled before mounting the app, called only on client side.
app:mountedvueAppClientCalled when Vue app is initialized and mounted in browser.
app:suspense:resolveappComponentClientOn Suspense
resolved event.
link:prefetchtoClientCalled when a is observed to be prefetched.
page:startpageComponent?ClientCalled on Suspense
pending event.
page:finishpageComponent?ClientCalled on Suspense
resolved event.
page:transition:finishpageComponent?ClientAfter page transition onAfterLeave
event.

FetchData

Nuxt告诉我们可以使用useFetch获取数据,而且这个方法即使在setup阶段执行,他也只会在服务端执行。只有我们设置server: false。 他才会在客户端执行。 image.png

引用

nuxt.com/docs/api/co… nuxt.com/docs/api/ad…