Skip to content

13. 示例 [nuxt-10]:asyncData 与加载

页面中的 [asyncData] 函数支持异步加载数据(通常为外部数据)。[nuxt] 会在 [asyncData] 函数执行完毕后才启动页面的生命周期。因此,只有在获取到外部数据后,页面才会被渲染。该函数在服务器端和客户端均会根据以下规则执行:

  • 当页面直接从服务器请求时,仅由服务器执行 [asyncData] 函数;
  • 随后,在客户端导航时,仅由客户端执行 [asyncData] 函数;

最终,客户端或服务器中仅有一方会执行该函数。此外,当客户端执行 [asyncData] 函数时,[nuxt] 会显示一个可配置的进度条。

[asyncData] 函数允许页面连同其数据一起提交给搜索引擎,从而使页面内容更具价值。

[nuxt-10] 示例最初是通过克隆 [nuxt-01] 项目创建的:

Image

仅 [page1] 页面发生变化。

13.1. [page1] 页面

[page1] 页面的代码如下:


<!-- vue n° 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigation -->
    <Navigation slot="left" />
    <!-- message-->
    <b-alert slot="right" show variant="primary"> Page 1 -- result={{ result }} </b-alert>
  </Layout>
</template>
 
<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */
 
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
 
export default {
  name: 'Page1',
  // components used
  components: {
    Layout,
    Navigation
  },
  // asynchronous data
  asyncData(context) {
    // log
    console.log('[page1 asyncData started]')
    // we make a promise
    return new Promise(function(resolve, reject) {
      // we simulate an asynchronous function
      setTimeout(function () {
        // we make the result asynchronous - a random number here
        resolve({ result: Math.floor(Math.random() * Math.floor(100)) })
        // log
        console.log('[page1 asyncData finished]')
      }, 5000)
    })
  },
 
  // life cycle
  beforeCreate() {
    console.log('[page1 beforeCreate]')
  },
  created() {
    console.log('[page1 created]')
  },
  beforeMount() {
    console.log('[page1 beforeMount]')
  },
  mounted() {
    console.log('[page1 mounted]')
  }
}
</script>
  • 第 26–39 行:[asyncData] 函数。我们已经介绍过这个函数(参见相关章节)。它会在页面生命周期开始前执行。因此,该函数内部不能使用 [this] 关键字;
  • 第 30 行:该函数必须返回 [Promise] 对象,或使用 async/await 语法;
  • 第 32–37 行:通过 5 秒的等待(第 37 行)模拟了 Promise 的异步函数;
  • 第 34 行:将异步函数的结果作为对象 {result:...} 返回。[asyncData] 函数返回的异步对象会被整合到页面的 [data] 对象中。这就是为什么即使页面未定义 [data] 对象,模板的第 7 行仍可访问 [result] 对象;

13.2. 配置 [asyncData] 进度条

当 [page1] 页面是客户端导航的目标(SPA 模式)时,客户端会执行 [asyncData] 函数,随后 [nuxt] 会显示一个进度条,并在 [asyncData] 函数返回结果后将其隐藏。通过 [nuxt.config.js] 文件中的 [loading] 属性,您可以配置此进度条:


loading: {
    color: 'blue',
    height: '5px',
    throttle: 200,
    continuous: true
},

默认情况下,[nuxt] 加载图标是一个横跨页面宽度的进度条。该进度条具有颜色和粗细。彩色线条会从 0% 逐渐增长至 100% 的长度,其增长速度取决于等待时长。

  • 第 2 行:设置进度条的颜色;
  • 第 3 行:设置进度条的宽度(以像素为单位);
  • 第 4 行:[throttle] 表示动画开始前的延迟时间(以毫秒为单位)。这可防止在 [asyncData] 函数快速返回结果时出现动画图像;
  • 第 5 行:[continuous] 设置进度条动画的行为。默认情况下,进度条会根据等待时间的长短,以快慢不一的速度从 0% 逐渐增长到 100% 的长度。若设置 [continuous:true],彩色进度条将以恒定速度从 0% 增长到 100% 的长度,然后循环重复,直到 [asyncData] 函数返回结果;

13.3. 执行

让我们启动应用程序,然后手动向服务器请求 [page1] 页面:

Image

日志如下:

Image

  • 我们可以看到,只有服务器 [1] 执行了 [asyncData] 函数,而且是在页面刷新之前执行的;

现在让我们检查服务器发送的页面(源代码):


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-10/">
  <link rel="preload" href="/nuxt-10/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-10/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-10/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-10/_nuxt/app.js" as="script">
...
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
            <h4>[nuxt-10] : asyncData et loading</h4>
              </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-10/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-10/page1" target="_self" class="nav-link active nuxt-link-active">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-10/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10"><div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-primary"> Page 1 -- result=3 </div></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>window.__NUXT__ = (function (a, b, c) {
  return {
    layout: "default", data: [{ result: 3 }], error: null, serverRendered: true,
    logs: [
      { date: new Date(1574939615256), args: ["[page1 asyncData started]"], type: a, level: b, tag: c },
      { date: new Date(1574939620263), args: ["[page1 asyncData finished]"], type: a, level: b, tag: c },
      { date: new Date(1574939620285), args: ["[page1 beforeCreate]"], type: a, level: b, tag: c },
      { date: new Date(1574939620287), args: ["[page1 created]"], type: a, level: b, tag: c }
    ]
  }
    }("log", 2, ""));</script>
  <script src="/nuxt-10/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-10/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-10/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-10/_nuxt/app.js" defer></script>
</body>
</html>
  • 第 55 行:我们可以看到,服务器向客户端发送了一个包含对象 [result:3] 的数组 [data],该对象被整合到了服务器 [page1] 页面的 [data] 对象中。为了让客户端也能执行同样的操作,从而显示与服务器相同的页面,服务器将 [result] 对象发送给了客户端。 请记住,客户端不会执行 [asyncData] 函数。它只会直接使用服务器计算出的数据;

现在,让我们通过导航菜单从 [Home] 页面导航到 [Page 1] 页面:

Image

  • 在 [1] 处,进度条出现;

5秒后,显示[第1页]页面:

Image

日志如下:

Image

我们可以看到,客户端在页面生命周期开始之前就执行了 [asyncData] 函数。