Nuxt.js特有的两个生命周期:asyncData、fetch

6,545 阅读4分钟

您好,我是沧沧凉凉,是一名前端开发者,目前在掘金知乎以及个人博客上同步发表一些学习前端时遇到的趣事和知识,欢迎关注。


最近帮同事找Nuxt项目问题时,发现Nuxt提供了2个生命周期,分别是:

  • asyncData
  • fetch

中文文档中对于这2个生命周期的介绍非常短,看完后我一头雾水,经过我的反复研究以及各种试验后,大致明白了这2个生命周期的用途和用法。

1. asyncData

asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据,Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

简单的说,就是asyncData只能用于页面组件,不能用于自定义组件,它可以返回一个对象,该对象中的值会覆盖data中对应的属性值。

并且asyncData中没有this来引用组件实例,但是它有一个context参数,大致用法如下:

<template>
  <div>{{ content }}</div>
</template>

<script lang="ts">
import Vue from "vue";

export default Vue.extend({
  name: "IndexPage",
  async asyncData({$axios}) {
    const res = await $axios.$get("http://localhost:5000");
    return {content: res.content};
  },
  data() {
    return {
      content: "",
    };
  },
});
</script>

查看网页源代码:

image-20220128161331678

2. fetch

fetch 方法用于在渲染页面前填充应用的状态树(store)数据, 与 asyncData 方法类似,不同的是它不会设置组件的数据。

可以在任何组件中使用,无论是页面组件还是自定义组件。

需要注意的是,中文文档上面的内容已经过时了,由于过时的问题,带有非常强烈的误导性:

image-20220128144729814

现在在fetch中是可以使用this获取到组件实例,并且也可以通过this去更改data中的属性。在英文文档中,官方对于fetch的介绍非常的完善,不像中文文档中是一段文字就给带过去了。

根据英文文档中,在Nuxt的2.12版本及之后,fetch的用法跟之前的版本完全不一样,而如果你在fetch中接受了一个context对象,那么它将被视为旧版的fetch,现在官方已经不推荐这么使用了。

我之前没有好好读英文文档,所以在试验的时候发现了一个坑点,直接看下面的代码吧:

async fetch() {
  const res = await fetch('http://localhost:5000').then(res=>res.json());
  // 这里赋值后界面正确渲染
  this.content = res.content;
},

我改变请求方式后:

async fetch({ $axios }) {
  const res = await $axios.$get('http://localhost:5000');
  // 这里赋值后发现界面上并没有渲染出对应的值
  this.content = res.content;
},

我把请求方式换成了Nuxt自带的axios,我发现页面上的内容没有正确的渲染出来,当时我完全没有找到原因所在。

而根据英文文档,在fetch中不能使用context,如果你使用了context参数,那么就会被视为旧版fetch。

如果你要使用context,那么推荐你使用asyncData或者匿名中间件,所以上面的请求应该这么写:

async fetch() {
  // 从this中获取$axios而不是从context中获取
  const res = await this.$axios.$get("http://localhost:5000");
  // 这里界面上就能正确的渲染出数据
  this.content = res.content;
},

查看网页源代码:

image-20220128161252804

3. 传统请求

知道asyncData和fetch大致用法后,我们就得来看看为什么要用这两个生命周期进行请求,为什么不在created、mounted这种Vue中常用的生命周期中进行网络请求。

还是上面的请求:

async mounted() {
  const res = await this.$axios.$get("http://localhost:5000");
  this.content = res.content;
},

查看网页源代码:

image-20220128153128168

可能你已经发现了区别,在mounted中进行请求,查看网页源代码的时候是看不到对应的数据,如果在网页源代码无法看到对应的数据,那么搜索引擎极大可能抓取不到这些数据。

如果你想要减小服务端的压力,比如一些对于SEO没有什么帮助的请求数据,那么你完全可以放在mounted中进行请求,因为mounted生命周期是在客户端进行运行的。

至于created这个生命周期,我实测的时候它会在服务端运行一次,然后又在客户端运行一次。

image-20220128154937239

这可能关系到Nuxt生命周期的问题,我暂时就没有深入研究,所以没有发言权,而且官方也说的是如果你想要使用传统的Vue请求方式,那么将请求放在mounted生命周期中。

4. 用户信息

使用这2个生命周期,可以让你的项目获得更好的SEO,但是值得注意的是,这2个生命周期都是在服务端中运行,而服务端是没有window对象的,也就无法使用localstorage,如果要进行用户身份的验证,那么推荐是使用cookie来存储用户的身份信息。

使用js-cookie可以很方便的操作cookie。

5. 好处

现在很多搜索引擎的爬虫已经能够解析JavaScript文件,从而获取JavaScript文件上面重要的内容。

但是异步请求的数据是没有办法获取的,所以使用asyncData和fetch将一些重要的数据放在服务端进行请求,然后服务端返给客户端的HTML上就已经获取到了这些数据,这样有2个好处:

  • 较短的白屏时间。
  • 更好的SEO。

当很多数据是通过异步进行请求的时候,打开页面经常会出现页面坍缩,获取到数据渲染完成后页面才会正常显示,虽然可能就那么一瞬间,但是对于用户来说也会造成不好的体验。

而使用服务端渲染就不会有这些问题,因为数据已经在服务端获取完毕了,也就不会出现页面短暂坍塌的情况。

6. 最后

服务端渲染带来更好的SEO的同时,对于服务器的负担也是指数倍的,比如单页面应用布置到Nginx上面可能就占几M的内存,而如果是服务端渲染的情况下可能达到几百M甚至更多,要知道,服务器的内存可是非常贵的,内存每翻一倍,价格都要翻好几倍。

至于要不要做服务端渲染,还是得根据自身的情况来定,不过遇到官网这种外包项目,如果用的Vue技术栈那么选Nuxt肯定是没错的。

客户不做SEO,你可以打包成单页面应用,如果需要SEO,那你也可以毫无压力的开启服务端渲染,多做一手准备少加班。