使用 Vue.js 和 Nuxt.js 进行无服务器端渲染
使用 Nuxt.js 在 AWS Lambda 和 AWS API Gateway 上创建服务器端渲染的 Vue.js 应用的示例项目。可以轻松与您自己的 API 或第三方 API(例如无头 CMS、电商或无服务器架构)集成。
完整教程在这里。
这标题确实有点拗口。别被它吓到。归根结底,其实很简单。
我们想要两全其美。既能提升服务器端渲染带来的 SEO 性能,又能保持单页应用的速度。所有这些都在 AWS Lambda 的无服务器环境中免费托管。
以下是我们将要构建的内容的简要概述,以便您快速上手。您可以直接跳到您最感兴趣的步骤。别介意我让您带着负罪感读完整个内容……*带着负罪感的眼神* 😐
注意:如果您需要进一步参考或错过任何步骤,我们将要编写的代码已经在 GitHub 上,请随时查看。
好吧,首先要说的是。我们想要一个超快的单页应用。但这通常需要付出代价。糟糕的 SEO 功能。这可不行,所以我们还想让应用支持服务器端渲染。听起来很简单。我们来用一下Nuxt.js ,它是一个用于创建通用Vue.js应用的框架,并将其配置为服务器端渲染我们的页面。
为了实现这一点,我们需要启动一个简单的 Express 服务器,并配置 Nuxt 渲染器以通过 Express 提供文件服务。其实做起来比听起来简单得多。
然而,这里的关键点是“服务器”这个词。呃,我们不喜欢提这个词。那么,我们需要做什么呢?好吧,把整个应用程序部署到AWS Lambda上!毕竟,它只是一个很小的 Node.js 实例。
但这引发了一个担忧。如果一切都出了严重问题,该如何监控和调试呢?我通常会在单独的选项卡中打开Tracetest,以实时测试我所有的无服务器资源。
呼,既然已经解决了这个问题,那就让我们开始吧!
与往常一样,我们从无聊的部分开始,设置项目并安装依赖项。
为了使无服务器开发不再是绝对的折磨,请继续安装无服务器框架。
$ npm i -g serverless
注意: 如果您使用的是 Linux 或 Mac,则可能需要以 的形式运行该命令sudo
。
一旦在您的计算机上全局安装,您就可以从终端的任何位置使用这些命令。但是,为了与您的 AWS 账户通信,您需要配置一个 IAM 用户。点击此处查看说明,然后返回并使用提供的密钥运行以下命令。
$ serverless config credentials \
--provider aws \
--key xxxxxxxxxxxxxx \
--secret xxxxxxxxxxxxxx
现在,你的 Serverless 安装已经知道在运行任何终端命令时要连接到哪个账户了。让我们开始实际操作吧。
创建一个新目录来存放您的无服务器应用服务。在那里启动一个终端。现在,您可以创建新服务了。
您可能会问,什么是服务?可以把它看作一个项目。但实际上并非如此。服务是定义 AWS Lambda 函数、触发这些函数的事件以及它们所需的任何 AWS 基础设施资源的地方,所有这些都包含在一个名为serverless.yml的文件中。
返回终端类型:
$ serverless create --template aws-nodejs --path serverless-side-rendering-vue-nuxt
create 命令会创建一个新的service。震惊!但有趣的部分来了。我们需要为函数选择一个运行时。这被称为template。传入 templateaws-nodejs
会将运行时设置为 Node.js。这正是我们想要的。path会为服务创建一个文件夹。
在终端中切换到 serverless-side-rendering-vue-nuxt 文件夹。里面应该有三个文件,但现在,我们先初始化 npm。
$ npm init -y
创建文件后package.json
,您可以安装一些依赖项。
$ npm i axios nuxt express serverless-http serverless-apigw-binary
这些是我们的生产依赖项,稍后我会更详细地解释它们的作用。除此之外,我们还需要一个开发依赖项。这个依赖项可以让我们把域名绑定到我们的端点。太棒了!
$ npm i --save-dev serverless-domain-manager
现在,你package.json
看起来应该是这样的。
{
"name": "serverless-side-rendering-vue-nuxt",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": { // <= add these scripts
"dev": "nuxt",
"deploy": "nuxt build && sls deploy"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.18.0",
"express": "^4.16.3",
"nuxt": "^1.4.2",
"serverless-apigw-binary": "^0.4.4",
"serverless-http": "^1.6.0"
},
"devDependencies": {
"serverless-domain-manager": "^2.6.0"
}
}
我们还需要添加两个脚本,一个用于nuxt
在本地开发机器上运行,另一个用于构建和部署应用程序。您可以scripts
在package.json
.
接下来,让我们在代码编辑器中打开项目。查看serverless.yml文件,它包含此服务的所有配置设置。在这里,您可以指定常规配置设置和每个函数的配置。您的 serverless.yml 将充满样板代码和注释。您可以随意删除所有内容并粘贴此文件。
# serverless.yml
service: serverless-side-rendering-vue-nuxt
provider:
name: aws
runtime: nodejs8.10
stage: ${self:custom.secrets.NODE_ENV}
region: eu-central-1
environment:
NODE_ENV: ${self:custom.secrets.NODE_ENV}
functions:
nuxt:
handler: index.nuxt
events:
- http: ANY /
- http: ANY /{proxy+}
plugins:
- serverless-apigw-binary
- serverless-domain-manager
custom:
secrets: ${file(secrets.json)}
apigwBinary:
types:
- '*/*'
customDomain:
domainName: ${self:custom.secrets.DOMAIN}
basePath: ''
stage: ${self:custom.secrets.NODE_ENV}
createRoute53Record: true
该functions
属性列出了服务中的所有函数。我们只需要一个函数,因为它将运行 Nuxt 渲染。它的工作原理是启动一个小型 Express 应用,将 Nuxt 渲染器中间件连接到 Express 路由器,并将该应用传递给serverless-http模块。反过来,这会将整个 Express 应用捆绑成一个 lambda 函数,并将其绑定到 API 网关端点。在 functions 属性下,您可以看到一个nuxt函数,它将拥有一个nuxt
在index.js
文件中指定的处理程序。API 网关会将所有请求代理到内部 Express 路由器,该路由器会告诉 Nuxt 渲染器渲染我们的 Vue.js 页面。哇,听起来很复杂!但其实并不复杂。一旦我们开始编写代码,您就会发现它实际上有多么简单。
我们还添加了两个插件,serverless-apigw-binary
用于让更多 mime 类型通过 API 网关,以及serverless-domain-manager
让我们毫不费力地将域名连接到我们的端点。
custom
我们在底部还有一个部分。该secrets
属性用于安全地将环境变量加载到我们的服务中。稍后可以使用它们来引用${self:custom.secrets.<environment_var>}
它们,实际值保存在一个名为的简单文件中secrets.json
。
除此之外,我们还让 API 网关二进制插件知道我们想要让所有类型通过,并为我们的端点设置一个自定义域。
配置就是这样,让我们添加secrets.json
文件。
我们都知道,将私钥推送到 GitHub 会害死小企鹅。千万别这么做。使用无服务器框架处理这个问题很简单。只需添加一个 secrets.json
文件,然后粘贴以下内容即可。
{
"NODE_ENV": "dev",
"DOMAIN": "vue-ssr.your-domain.com"
}
现在,只需更改这些值,您就可以将不同的环境部署到不同的阶段和域。太酷了。
我们将使用 Nuxt.js 构建通用的 Vue.js 应用。这意味着什么?简单来说,它只是一个服务器端渲染的单页应用。这意味着你无需担心 SEO,因为它会在 JavaScript 代码发送到客户端之前进行渲染。而且,一旦 JavaScript 代码在客户端加载完成,它就不会再次请求文件,而是将其缓存起来。速度更快!我太喜欢了。
让我们开始吧。
为了使 Nuxt 正常工作,我们需要一个nuxt.config.js
文件来添加我们的构建配置。
// nuxt.config.js
module.exports = {
mode: 'universal',
head: {
title: 'Vue Nuxt Test',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
]
},
build: {
vendor: ['axios'],
publicPath: `/${require('./secrets.json').NODE_ENV}/_nuxt/` // <= add the path to the cached files
},
srcDir: 'client/',
performance: {
gzip: false
},
router: {
base: `/`
},
dev: false
}
您可以看到,我们需要secrets.js文件才能将我们的阶段加载publicPath
到静态文件的 URL 中。一旦我们在下面的 Express 路由器中添加路由,您就会明白为什么这很重要。另外,检查srcDir
,它指定了我们客户端文件所在文件夹的名称。
添加完成后,创建另一个名为nuxt.js的文件。这真的很直观,我知道。
// nuxt.js
const express = require('express')
const app = express()
const { Nuxt } = require('nuxt')
const path = require('path')
app.use('/_nuxt', express.static(path.join(__dirname, '.nuxt', 'dist')))
const config = require('./nuxt.config.js')
const nuxt = new Nuxt(config)
app.use(nuxt.render)
module.exports = app
这很简单。我们抓取 Express 和 Nuxt,创建一个静态路由,并将express.static
Nuxt 将创建的 JavaScript 打包目录传递给它。这里的路径是,但由于 API 网关将阶段作为后缀添加,我们需要在上面提到的文件/_nuxt
中指定它。publicPath
nuxt.config.js
nuxt.render
一旦配置加载完成,除了将中间件传递给 Express 应用程序之外,无需再做任何事情。
现在,需要将应用连接到serverless-http
lambda 函数并将其导出。创建一个index.js
文件并将其粘贴进去。
// index.js
const sls = require('serverless-http')
const binaryMimeTypes = require('./binaryMimeTypes')
const nuxt = require('./nuxt')
module.exports.nuxt = sls(nuxt, {
binary: binaryMimeTypes
})
如你所见,我们还需要创建binaryMimeTypes.js
一个文件来保存所有想要启用的 MIME 类型。它只是一个简单的数组,我们会将其传入serverless-http
模块。
// binaryMimeTypes.js
module.exports = [
'application/javascript',
'application/json',
'application/octet-stream',
'application/xml',
'font/eot',
'font/opentype',
'font/otf',
'image/jpeg',
'image/png',
'image/svg+xml',
'text/comma-separated-values',
'text/css',
'text/html',
'text/javascript',
'text/plain',
'text/text',
'text/xml'
]
太好了,Nuxt 设置就到这里。让我们开始编写客户端代码吧!
在项目根目录中创建一个新文件夹并命名client
。向上滚动时,我们会将文件srcDir
中的设置nuxt.config.js
为指向名为 的目录client
。
在此client
文件夹中,再创建三个文件夹,分别名为components
、、、layouts
。pages
进入layouts
文件夹后,创建一个名为 的新文件default.vue
,然后将其粘贴进去。
// client/layouts/default.vue
<template>
<div>
<navbar/>
<nuxt/>
</div>
</template>
<script>
import navbar from "~/components/navbar";
export default {
components: { navbar }
};
</script>
默认视图将包含来自 Nuxt 的<navbar/>
组件和<nuxt/>
渲染内容的组件。
现在将navbar.vue
文件添加到文件夹中components
。
// client/components/navbar.vue
<template>
<nav class="nav">
<ul>
<li>
<nuxt-link to="/">Home</nuxt-link>
</li>
<li>
<nuxt-link to="/dogs">Dogs</nuxt-link>
</li>
<li>
<nuxt-link to="/dogs/shepherd">Only Shepherds</nuxt-link>
</li>
</ul>
</nav>
</template>
这是一个非常简单的导航,用于在一些可爱的狗狗之间导航。一旦我们在pages
文件夹中添加内容,它就会变得有意义。
在pages
文件夹中创建一个index.vue
文件并添加以下代码。
// client/pages/index.vue
<template>
<div>
<h1>This is the Front Page.</h1>
<h3>Random dog of the day:</h3>
<img :src="dog.url" alt="">
</div>
</template>
<script>
import axios from "axios";
export default {
async asyncData({ params }) {
const { data } = await axios.get(
"https://api.thedogapi.com/v1/images/search?limit=1"
);
return { dog: data[0] };
}
};
</script>
该index.vue
文件将渲染到我们应用的根路径下。它调用了一个狗狗 API,并显示一张可爱的狗狗图片。要创建更多路由,请创建一个名为 的子文件夹,并在其中dogs
创建一个index.vue
文件和一个文件。将在路由中渲染, 将在代表路由参数的位置渲染。_breed.vue
index.vue
/dogs
_breed.vue
/dogs/:breed
:breed
将其添加到目录index.vue
中dogs
。
// client/pages/dogs/index.vue
<template>
<div>
<h1>Here you have all dogs.</h1>
<ul>
<li v-for="dog in dogs" v-bind:key="dog.id">
<img :src="dog.url" alt="">
</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
async asyncData({ params }) {
const { data } = await axios.get(
"https://api.thedogapi.com/v1/images/search?size=thumb&limit=10"
);
return { dogs: data };
},
head() {
return {
title: "Show all dogs!",
meta: [
{
hid: "description",
name: "description",
content: `Hello Dogs 👋`
}
]
};
}
};
</script>
_breed.vue
并且,文件夹中的文件中还有另一个片段dogs
。
// client/pages/dogs/_breed.vue
<template>
<div>
<h2>Dog breed: {{ breed }}</h2>
<ul>
<li v-for="dog in dogs" v-bind:key="dog.id">
<img :src="dog.url" alt="">
</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
async asyncData({ store, route }) {
const { data } = await axios.get(
"https://api.thedogapi.com/v1/images/search?size=thumb&has_breeds=true&limit=50"
);
const reg = new RegExp(route.params.breed, "g");
const filteredDogs = data.filter(dog =>
dog.breeds[0]
.name
.toLowerCase()
.match(reg)
);
return { dogs: filteredDogs, breed: route.params.breed };
},
head() {
return {
title: `${this.breed} Dog`,
meta: [
{
hid: "description",
name: "description",
content: `You are ${this.breed} hello 👋`
}
]
};
}
};
</script>
正如您在这些文件中看到的,有一个head()
函数。它将在您的页面中添加自定义字段<head>
,从而为其提供适当的 SEO 支持!
注意:如果您遇到困难,请参阅 repo 中的代码。
让我们部署它并看看它是否有效。
一开始,我们在package.json
名为 的脚本中添加了一个名为 的脚本deploy
。它将构建 Nuxt 应用程序并按照我们在 中指定的方式部署无服务器服务serverless.yml
。
您需要做的就是运行:
$ npm run deploy
您将看到终端返回一些包含应用端点的输出。但是,我们还有一件事要做。我们需要添加域名。我们已经在配置文件中添加了配置,serverless.yml
但还需要运行一个命令。
$ sls create_domain
这将创建一个 CloudFront 发行版并将其连接到您的域。请确保您已将证书添加到您的 AWS 账户。AWS 通常需要大约 20 分钟来配置新的发行版。去喝杯咖啡吧。
回来?好的,继续,再次部署。
$ npm run deploy
您仍然可以使用默认端点,但现在您已将其绑定到您的域。它应该如下所示。
太棒了,你的应用已经启动并运行了。快来试用一下吧。
所有无服务器应用程序的首要问题是其分布式特性。简而言之,想要全面了解所有正在发生的事情极其困难。更不用说当出现问题时,调试起来有多困难了。
为了平息我的恐惧,我使用了Tracetest。它提供由 OpenTelemetry 支持的端到端测试和调试。
幸好,这里有详细的文档,让新手入门变得轻而易举。请按照快速入门指南操作。不过,别忘了回来这里看看。😄
好了,应用完成了。你已经用 Nuxt.js 创建了一个服务端渲染的 Vue.js 应用,并将其托管在 AWS Lambda 上的无服务器环境中,并且添加了在用户开始抱怨之前监控和调试应用的方法。没有比这更好的了。
这是一种全新的创建快速响应式网站的思路。您无需担心任何服务器问题。只需部署代码,即可确保其正常运行。即使出现问题,Tracetest也能为您提供保障!
如果您在任何地方遇到困难,请查看 GitHub repo 以获取更多参考,如果您希望更多人在 GitHub 上看到它,请随意给它一颗星。
使用 Nuxt.js 在 AWS Lambda 和 AWS API Gateway 上创建服务器端渲染的 Vue.js 应用的示例项目。可以轻松与您自己的 API 或第三方 API(例如无头 CMS、电商或无服务器架构)集成。
完整教程在这里。
如果您想阅读我之前的一些无服务器思考,请转到我的个人资料或加入我的时事通讯!
或者,立即查看我的几篇文章:
我还强烈建议您查看有关 Nuxt.js 的这篇文章,以及有关无服务器域管理器的教程。
希望大家喜欢阅读这篇文章,就像我喜欢写这篇文章一样。如果你们喜欢,请点个小爱心,这样 dev.to 上会有更多人看到这篇教程。下次再见,保持好奇心,享受乐趣吧。
免责声明:Tracetest赞助了这篇博文。使用可观察性可以将测试创建和故障排除的时间和精力减少 80%。