这个标题有点长,对吧? 什么是服务器端渲染? 它与路由和页面过渡有什么关系? Nuxt.js 是什么鬼? 很有意思的是,尽管听起来很复杂,但使用 Nuxt.js 并探索它的好处并不太难。 让我们开始吧!
服务器端渲染
您可能最近听到人们谈论服务器端渲染。 我们最近研究了 使用 React 进行服务器端渲染的一种方法。 一个特别引人注目的方面是性能优势。 当我们在服务器上渲染 HTML、CSS 和 JavaScript 时,我们通常需要解析的 JavaScript 更少,无论是在初始渲染还是后续更新时。 这篇文章 很好地深入探讨了这个主题。 我最喜欢的收获是
通过在服务器上渲染,您可以缓存数据的最终形式。
与其从服务器获取 JSON 或其他信息,解析它,然后使用 JavaScript 创建该信息的布局,不如在前面进行大量的计算,只发送我们需要的实际 HTML、CSS 和 JavaScript。 这可以为缓存、SEO 和加快应用程序和网站速度带来很多好处。
什么是 Nuxt.js?
服务器端渲染听起来很不错,但是您可能想知道它是否难以设置。 我最近一直在使用 Nuxt.js 来开发我的 Vue 应用程序,发现它非常易于使用。 需要明确的是:您无需专门使用 Nuxt.js 来进行服务器端渲染。 我只是出于很多原因喜欢这个工具。 我上个月进行了一些测试,发现 Nuxt.js 甚至比 Vue 的 PWA 模板具有更高的 lighthouse 得分,我认为这令人印象深刻。
Nuxt.js 是一个更高级别的框架,您可以使用 CLI 命令来创建通用 Vue 应用程序。 以下是部分好处(并非全部):
- 服务器端渲染
- 自动代码拆分
- 强大的路由系统
- 出色的 lighthouse 得分 🐎
- 静态文件服务
- ES6/ES7 转译
- 开发中的热重载
- 预处理器:SASS、LESS、Stylus 等
- 编写 Vue 文件来创建页面和布局!
- 我最喜欢的一个功能:轻松 为页面添加过渡效果
让我们使用一些路由来设置一个基本应用程序,亲身体验它的好处。
设置
如果您还没有安装 Vue 的 CLI,那么需要做的第一件事就是下载它。 您可以使用以下命令全局安装它
npm install -g vue-cli
# ... or ...
yarn add global vue-cli
您只需要执行一次,无需每次使用时都执行。
接下来,我们将使用 CLI 来搭建一个新项目,但我们将使用 Nuxt.js 作为模板
vue init nuxt/starter my-project
cd my-project
yarn # or... npm install
npm run dev
您将看到应用程序构建的进度,它将为您提供一个专用的开发服务器以供查看:http://127.0.0.1:3000/。 这就是您一开始将看到的内容(带有非常酷的小动画)

让我们看一下目前创建应用程序初始视图的内容。 我们可以转到 `pages` 目录,并在其中看到一个 `index.vue` 页面。 如果我们打开它,我们将看到创建该页面所用的所有标记。 我们还将看到它是一个 `.vue` 文件,使用单文件组件,就像任何普通的 `vue` 文件一样,带有用于 HTML 的模板标签、用于脚本的脚本标签(我们在其中导入了一个组件)以及样式标签中的一些样式。(如果您不熟悉这些,可以 在这里了解有关它们的更多信息。) 最酷的一点是,这个 `.vue` 文件不需要任何特殊设置。 它位于 `pages` 目录中,Nuxt.js 将自动将其转换为服务器端渲染的页面!
让我们创建一个新页面,并在它们之间设置一些路由。 在 `pages/index.vue` 中,删除现有的内容,并用以下内容替换它
<template>
<div class="container">
<h1>Welcome!</h1>
<p><nuxt-link to="/product">Product page</nuxt-link></p>
</div>
</template>
<style>
.container {
font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* 1 */
padding: 60px;
}
</style>
然后,我们在 pages 目录中创建一个名为 `product.vue` 的新页面,并将以下内容放入其中
<template>
<div class="container">
<h1>This is the product page</h1>
<p><nuxt-link to="/">Home page</nuxt-link></p>
</div>
</template>
您会立即看到以下内容
就这样! 🏆
我们立即拥有服务器端渲染、页面之间的路由(如果您查看 URL,您会看到它在索引页面和产品页面之间切换),甚至还有一个很酷的绿色加载器,它在顶部快速移动。 我们没有做太多工作 来实现它。
您可能已经注意到,这里有一个特殊的元素:<nuxt-link to="/">
。 这个标签可以像 a
标签一样使用,它包含一些内容,并将在我们的页面之间设置内部路由链接。 我们将使用 to="/page-title-here"
而不是 href
。
现在,让我们添加一些过渡效果。 我们将分几个阶段进行:从简单到复杂。
创建页面过渡效果
我们已经有一个非常酷的进度条,它在我们在路由时会在屏幕顶部运行,使整个过程感觉非常快。(那是一个专业的术语)。 虽然我很喜欢它,但它并不适合我们正在前进的方向,所以现在让我们把它去掉。
我们将进入我们的 `nuxt.config.js` 文件,并将以下行
/*
** Customize the progress-bar color
*/
loading: { color: '#3B8070' },
更改为
loading: false,
您还会在这个 `nuxt.config.js` 文件中注意到一些其他内容。 您会看到我们的元数据和头部标签,以及将在它们内部渲染的内容。 这是因为我们不会像在普通 CLI 构建中那样使用传统的 `index.html` 文件,Nuxt.js 将解析并构建我们的 `index.vue` 文件以及这些标签,然后在服务器上为我们渲染内容。 如果您需要添加 CSS 文件、字体或类似内容,我们将使用此 Nuxt.js 配置文件来执行此操作。
现在我们已经完成所有设置,让我们了解可以使用哪些方法来创建页面过渡效果。 为了了解我们正在插入页面的内容,我们需要回顾 Vue 中过渡组件的工作原理。 我在这里 写了一篇文章专门介绍这个主题,如果您想了解更多,可以查看它。 但是您真正需要了解的是:在幕后,Nuxt.js 将接入 Vue 的 `transition` 组件的功能,并为我们提供一些默认值和钩子来使用

您可以在此处看到,我们有一个钩子用于指定动画开始前的操作 enter
、动画/过渡期间的操作 enter-active
以及动画结束后的操作。 我们在元素离开时也有相同的钩子,只是在前面加上了 leave
。 我们可以创建简单的过渡,这些过渡只是在状态之间进行插值,或者我们可以将完整的 CSS 或 JavaScript 动画插入其中。
通常在 Vue 应用程序中,为了使用这个漂亮的小功能,我们会将组件或元素包装在 <transition>
中,但 Nuxt.js 会在开始时为我们提供这个功能。 我们页面的钩子将以 page
开头,这真是太好了。 我们要做的就是在页面之间创建动画,就是在 CSS 中添加一些代码,以接入这些钩子
.page-enter-active, .page-leave-active {
transition: all .25s ease-out;
}
.page-enter, .page-leave-active {
opacity: 0;
transform-origin: 50% 50%;
}
我还将在此处添加一些额外的样式,这样您就可以更容易地看到页面过渡效果
html, body {
font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* 1 */
background: #222;
color: white;
width: 100vw;
height: 100vh;
}
a, a:visited {
color: #3edada;
text-decoration: none;
}
.container {
padding: 60px;
width: 100vw;
height: 100vh;
background: #444;
}
目前,我们正在使用 CSS 过渡效果。 这只允许我们指定在两种状态之间转换过程中的操作。 我们可以通过让动画以暗示某些东西从何处而来以及去往何处的样式进行调整来创建一些更有趣的效果。 为此,我们可以将页面进入和页面离开激活类分开,但使用 CSS 动画来指定某些东西从何处而来以及去往何处,并将它们接入 .page-enter-active
和 .page-leave-active
会更加简洁
.page-enter-active {
animation: acrossIn .45s ease-out both;
}
.page-leave-active {
animation: acrossOut .65s ease-in both;
}
@keyframes acrossIn {
0% {
transform: translate3d(-100%, 0, 0);
}
100% {
transform: translate3d(0, 0, 0);
}
}
@keyframes acrossOut {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(100%, 0, 0);
}
}
我们还将为产品页面添加一些特殊样式,这样我们可以看到这两个页面之间的区别
<style scoped>
.container {
background: #222;
}
</style>
这个作用域标签非常酷,因为它只将样式应用于此页面/vue 文件。 如果您听说过 CSS 模块,您会熟悉这个概念。
我们会看到以下效果(此页面仅用于演示目的,对于典型的页面过渡来说,移动量可能太多了)
现在,假设我们有一个页面具有完全不同的交互方式。 对于这个页面,上下移动过于频繁,我们只想要一个简单的淡入淡出效果。 针对这种情况,我们需要重命名过渡钩子,以便将其与其他过渡分开。
让我们创建一个名为联系页面的新页面,并在 pages 目录中创建它。
<template>
<div class="container">
<h1>This is the contact page</h1>
<p><nuxt-link to="/">Home page</nuxt-link></p>
</div>
</template>
<script>
export default {
transition: 'fadeOpacity'
}
</script>
<style>
.fadeOpacity-enter-active, .fadeOpacity-leave-active {
transition: opacity .35s ease-out;
}
.fadeOpacity-enter, .fadeOpacity-leave-active {
opacity: 0;
}
</style>
现在我们可以实现两页之间的过渡动画。
您可以看到我们可以如何在此基础上进一步构建,并为每个页面创建更多、更流畅的 CSS 动画。但从这里开始,让我们深入我喜欢的 JavaScript 动画,并用更强大的功能创建页面过渡。
JavaScript 钩子
Vue 的 <transition>
组件提供了一些钩子,用于使用 JavaScript 动画代替 CSS。它们如下所示,每个钩子都是可选的。:css="false"
绑定让 Vue 知道我们将使用 JS 来实现此动画。
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-Leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
:css="false">
</transition>
我们还可以使用过渡模式。我很喜欢它们,因为您可以声明一个动画将等待另一个动画完成过渡后再开始过渡。我们将使用名为 out-in 的过渡模式。
我们可以用 JavaScript 和过渡模式做一些非常酷的事情,再次强调,为了演示的目的,我们会做一些比较夸张的事情,通常情况下我们会做一些更微妙的事情。
为了实现这样的效果,我运行了 yarn add gsap
,因为我将使用 GreenSock 来实现此动画。在我的 `index.vue` 页面中,我可以删除现有的 CSS 动画,并将这段代码添加到 <script>
标签中。
import { TweenMax, Back } from 'gsap'
export default {
transition: {
mode: 'out-in',
css: false,
beforeEnter (el) {
TweenMax.set(el, {
transformPerspective: 600,
perspective: 300,
transformStyle: 'preserve-3d'
})
},
enter (el, done) {
TweenMax.to(el, 1, {
rotationY: 360,
transformOrigin: '50% 50%',
ease: Back.easeOut
})
done()
},
leave (el, done) {
TweenMax.to(el, 1, {
rotationY: 0,
transformOrigin: '50% 50%',
ease: Back.easeIn
})
done()
}
}
}
所有这些演示的代码都存在于我的 Intro to Vue 仓库 中,如果您想学习 Vue 的入门材料,可以参考这些代码。
我想在这里提一点,目前 Nuxt.js 中的过渡模式存在一个 bug。这个 bug 已经修复,但还没有发布。它应该在即将发布的 1.0 版本中完全修复并更新,但在此之前,这里有一个 简单的示例演示,以及 跟踪该问题的 issue。
有了这个有效的代码和 JavaScript 钩子,我们可以开始创建更加花哨的动画,并在每个页面上创建独特的过渡效果。
如果您想查看演示的实际效果,请访问以下网站:https://nuxt-type.now.sh/,以及包含代码的仓库:https://github.com/sdras/nuxt-type
导航
在最后一个演示中,您可能已经注意到,我们在所有路由页面上都使用了相同的导航。为了创建它,我们可以进入 `layouts` 目录,我们会看到一个名为 `default.vue` 的文件。这个目录将存放所有页面的基本布局,“default” 就是,嗯,默认的布局 :)
你会立刻看到这个
<template>
<div>
<nuxt/>
</div>
</template>
特殊的 <nuxt/>
标签将是我们的 `.vue` 页面文件被插入的地方,所以为了创建导航,我们可以插入一个这样的导航组件。
<template>
<div>
<img class="moon" src="~assets/FullMoon2010.png" />
<Navigation />
<nuxt/>
</div>
</template>
<script>
import Navigation from '~components/Navigation.vue'
export default {
components: {
Navigation
}
}
</script>
我喜欢这样,因为所有东西都被很好地组织起来,区分了全局和局部需求。
然后,我在一个名为 `components` 的目录中有一个名为 Navigation 的组件(这在 Vue 应用中非常常见)。在这个文件中,你会看到一些指向不同页面的链接。
<nav>
<div class="title">
<nuxt-link to="/rufina">Rufina</nuxt-link>
<nuxt-link to="/prata">Prata</nuxt-link>
<nuxt-link exact to="/">Playfair</nuxt-link>
</div>
</nav>
你会注意到,即使它在另一个目录中,我仍然使用 <nuxt-link>
标签,路由仍然可以正常工作。但是,最后一页有一个额外的属性,即 exact 属性:<nuxt-link exact to="/">Playfair</nuxt-link>
这是因为有很多路由都匹配 `/` 目录,实际上,所有的路由都匹配。因此,如果我们指定 exact
,Nuxt 将知道我们只指的是索引页面。
更多资源
如果您想了解更多关于 Nuxt 的信息,他们的文档 写得很好,并且有很多示例可以帮助您入门。如果您想了解更多关于 Vue 的信息,我刚刚在 Frontend Masters 上开设了一门课程,所有课程材料都在这里开源,或者您可以查看我们的 Vue 指南,或者您可以访问 Vue 文档,它们写得非常棒。祝您编程愉快!
这看起来是一个非常容易上手的 js 渲染和路由方法(特别是对于已经对 Vue.js 感兴趣的人来说),但我不知道你是否会更深入地谈论部署?我们如何将它部署到服务器上?如何与 WordPress 或 Craft 等 CMS 集成以填充模板?现在大多数 CMS 都能够原生输出 JSON REST API,因此,更多关于弥合 CMS 与这些令人印象深刻的前端技术之间差距的资源将受到欢迎。
你读懂了我的心思!部署文章确实正在制作中 :) 我还有一些 OSS 项目需要完成,但我知道它们很快就会发布!
嗨 Dalton,
你提出了一个非常好的问题!这是使用这类技术栈时需要解决的难题之一。它还取决于你需要什么样的实现。很多人使用 Nuxt 来生成静态网站。你并不局限于这样做,你也可以只构建一个典型的 Vue 项目(拥有 Nuxt 的神奇功能),尽管静态生成对于很多用例来说是一个不错的选择。Nuxt 使用 Webpack + Vue 静态生成 + 一点魔法来进行生成。(另外,如果“静态”这个词让你感到困惑,请查看 https://jamstack.fullstack.org.cn/,了解我在说什么)。
无论如何…
“如何与 WordPress 或 Craft 等 CMS 集成以填充模板?”
我们希望有一个熟悉的 CMS 环境供内容编辑使用,我们还希望能够完全控制 RESTful 端点的构建,以便提供我们想要的内容,而 WordPress 满足了这些需求。很多其他解决方案也存在于这个领域,并且在这个领域中竞争激烈(例如:Contentful、Cosmic JS、DatoCMS),但它们对于我们需求的复杂性来说还不够灵活。
我们所有的数据都存储在 WordPress 中(也许你的数据存储在 Craft 中?最近我听说 Craft 很棒)。当我们进行本地开发时,我们有一些节点脚本,它们会拉取我们需要的所有数据并生成一些 JSON。然后(深呼吸)我们使用 Zeit 的 Micro 服务器启动一个本地内容服务器,并使用它通过 NuxtServerInit 钩子将数据加载到 Vuex 存储中。利用 Vuex 规范,我们为所有模板填充了所有数据需求(从现在开始,我将模板称为组件)。当我们准备进行生产部署时,我们会跳过 JSON 环节,并从 WP REST API 进行全新拉取。部署通过 Gitlab 上的 CI 管道进行,然后部署到 Amazon Web Services,在那里它通过 Cloundfront (CDN) + S3 (静态托管) 进行提供服务。
将数据引入组件的另一种方法是使用 Nuxt 环境中内置的 Fetch 组件钩子(同样,这将使用数据填充 Vuex 存储,因此类似于使用 NuxtServerInit 钩子)。还有一个与 Nuxt 配合使用的 Async Data 钩子。异步数据钩子在 Vuex 存储之外工作,它非常棒,但我喜欢使用 Vuex,并且我尝试在所有数据中使用相同的数据流模式。
我省略了很多细节,但你可以在 Google 上搜索上面提到的很多术语,找到关于所有这些主题的优秀文档。我还会在下面发布一些链接,并关注这个话题。
我们需要一些非常特殊的东西,因此整个过程有点复杂(尽管一旦你熟悉了这些工具,它就变得非常容易,而且有很多工具可以用来实现自动化),也就是说,有很多简单的解决方案可以实现更简单的部署和托管(例如:Netlify)。
顺便说一句,非常感谢 Sébastien Chopin、Alexandre Chopin 和 Pooya Parsa 为 Nuxt 付出的努力,继续加油!他们都会尽快在 Nuxt github 的 issue 中回复,并且非常乐于助人。
感谢 Sarah 的精彩文章!我期待着在 9 月份你举办的下一个网络动画研讨会上学习更多花哨的动画技巧。
如果我需要澄清任何问题,请告诉我 Dalton!
Nuxt
https://github.com/nuxt/nuxt.js/issues
https://nuxtjs.org.cn/guide
https://nuxtjs.org.cn/api
https://nuxtjs.org.cn/examples
https://nuxtjs.org.cn/guide/async-data
https://nuxtjs.org.cn/guide/vuex-store
Vue
https://vuex.vuejs.net.cn/en/intro.html
https://router.vuejs.net.cn/en/
https://vuejs.net.cn/v2/guide/
HTTP
https://github.com/mzabriskie/axios
CI/CD/Repo/Deployment
https://about.gitlab.com/features/gitlab-ci-cd/
https://aws.amazon.com/getting-started/use-cases/websites/
CMS
https://developer.wordpress.org/rest-api/
https://www.netlify.com/
https://www.datocms.com/
https://cosmicjs.com/
其他
https://github.com/zeit/micro
https://github.com/zeit/now
https://serverless.com/
https://www.graph.cool/
我写了一篇关于如何使用 Docker 设置 Node.js、Apache 和 nginx 反向代理的文章(这可能对部署这种应用程序很有帮助)。
https://medium.com/@francoisromain/setup-node-js-apache-nginx-reverse-proxy-with-docker-1f5a5cb3e71e
哇,杰森,很棒的评论。深入研究并提供了很多资源!(你应该写文章 ;))
感谢杰森,这真是很棒的信息。信息量很大,但很高兴听到有人之前已经走过这条路,而且有很多资源可用。
是的,Craft. Is. Awesome。我发现内容建模比 WP 更灵活,插件架构简单明了,文档一致且合理,界面简洁易懂,客户易于管理。我们一直在用 PHP/Twig 模板构建更传统的网站,但我们在几个项目中集成了 Element API,我想尽快在 API 之上构建整个网站。
听到你这么说真棒,达尔顿!如果你的内容模型有任何动态或复杂的特性,这些构建的 CMS 和管道部分至关重要。我稍微研究了一下 Craft,我很喜欢它的样子。
对于简单的模型和较少的数据量,我非常喜欢 Netlify 产品,特别是集成 Netlify CMS。
https://www.netlify.com/
https://github.com/netlify/netlify-cms
对于在服务器端处理某些内容(如从联系表单发送电子邮件、授权等),我尝试使用 Function-as-a-Service 产品。类似于 AWS Lambda 的东西。
我推荐的两个 FaaS 项目
https://serverless.com/
https://arc.codes/
你的导航组件需要用
<template>
标签包裹吗?这是默认的 :)
我一直都在等待最近的、看起来简单的 Nuxt.js 教程,已经等了好几个星期了。我很兴奋要深入学习它。
莎拉,文章很棒。谢谢!
莎拉,文章写得真好!一直在尝试 Nuxt 的一些东西,我真的很喜欢它。你推荐用 Node 构建一个联系表单,并将其集成到 Nuxt 项目中吗?
感谢你分享这些信息,杰森,也感谢莎拉的精彩文章。
现在,部署到专用服务器怎么样?我已经用 vue 开发了几个月了,遇到了一个非常常见但难以解决的问题,那就是 Facebook 上的社交分享和元标签。
问题是,我无法设置元标签来获取 Facebook 上动态视图的良好预览,因此我一直在研究 SSR 一段时间了。
我尝试通过 SSH 在我的专用服务器上部署 Nuxt 应用程序,但我难以弄清楚如何操作,有人知道我在哪里可以找到有关如何执行此操作的信息吗?
Nuxt 文档确实解决了部署问题,但它们只涵盖了几个用例。
嗨,Leo!
在常见问题解答中,有很多关于部署的选项。到目前为止,我一直使用 Now,所以我链接到那个,但这里还有很多不同的选项。正如之前评论中所述,我正在写一篇关于部署的完整文章,因为它值得拥有,所以如果你的用例没有被涵盖,我希望能够在下一篇文章中解决它,或者在此期间,你可以在 Nuxt 库中打开一个问题。
SSR 中的元标签略有不同 - 你会发现你需要在你的 nuxt.config.js 文件中添加它们。我还使用 Facebook Open Graph 调试器,因为我发现我的社交分享 og 链接通常需要先通过它才能生效:https://developers.facebook.com/tools/debug/ Twitter 在这方面要好一些。希望对你有帮助!
嗨,莎拉,是的,我也尝试过 Now,而且成功了,非常简单!但我们在工作场所有一个专用服务器托管,所以我需要使用它。
我会等待你的下一篇文章 :D
另外,我对这个问题做了很多谷歌搜索。我使用 WordPress 作为 REST API,因此在我的 vue 项目中,我使用异步调用来获取有关帖子的信息。获得此信息后,我会更新页面上的元标签。
但自然地,Facebook 爬虫不会等待我的异步调用运行,因此当我将链接粘贴到 Facebook 时,我无法获得帖子的正确预览。
我在 vuejs 库中的一个问题中评论了这一点,并收到了 Evan You 本人的回复!他推荐预渲染(在我的情况下不起作用,因为我有数百个帖子)或 SSR,我已经研究了很长时间了。
但 SSR 对我来说仍然是黑魔法 :P
这篇文章向你展示了如何用 Nuxt 设置 SSR,如果你阅读这篇文章并尝试一下,可能会有更多意义 :)
我只想提醒一下,如果在运行
npm run dev
时遇到任何与 ES6 语法相关的错误,请查看。我怀疑,由于最近的 OS X 更新和我在 oh-my-zsh 中的路径变量的组合,我的机器恢复到使用 Node v5.0.0。它不支持 Nuxt/Starter 代码库中发现的各种声明。TL;DR:运行
npm run dev
时遇到与 Starter 语法相关的错误?检查你正在使用的 Node 版本($ node -v
)。如果它是 v5.0.0,请调查使用 nvm 升级!没错!Nuxt 核心使用 Async/Await,因此你需要运行支持它的 Node 版本。我建议 v8+,因为它是一个 LTS 版本,并支持 Async/Await。