博主今天开设Nuxt.js专栏,带您深入探索 Nuxt.js 的精髓,学习如何利用其强大功能构建出色的前端应用程序。我们将探讨其核心特点、灵活的路由系统、优化技巧以及常见问题的解决方案。无论您是想了解 Nuxt.js 的基础知识,还是希望掌握进阶技巧,本专栏都将满足您的需求。接下来让我们一起踏上 Nuxt.js 的旅程,开启一段令人兴奋的前端开发之旅!
需要了解的前置知识:
搜索引擎优化(SEO):是一种通过优化网站和内容,以提高在搜索引擎中的排名和可见性的过程。它是一种有助于网站获得更多有机(非付费)流量的策略和技术。但是采用vue.js开发的应用系统对SEO并不友好。
客户端渲染(CSR):客户端渲染是一种 Web 应用程序的渲染方式,其中页面的内容和结构主要由客户端浏览器在运行时生成和呈现,而不是在服务器端生成。在客户端渲染中,服务器主要负责提供数据和基本的 HTML、CSS 和 JavaScript 文件,然后将这些文件发送给客户端浏览器。浏览器接收到这些文件后,会解析 HTML 和 CSS,并执行 JavaScript 代码来生成动态内容和交互。
服务器渲染(SSR):服务器渲染是一种 Web 应用程序的渲染方式,其中页面的内容和结构主要在服务器端生成,然后发送给客户端浏览器进行显示。在服务器渲染中,服务器接收到客户端的请求后,会根据请求的 URL 和参数等信息,生成相应的 HTML、CSS 和 JavaScript 文件,并将这些文件作为响应返回给客户端浏览器。浏览器接收到文件后,直接显示其中的内容,无需再执行额外的操作。
客户端渲染与服务器渲染的区别:在选择服务器渲染还是客户端渲染时需要综合考虑项目需求、技术复杂性和性能要求等因素。一般来说,对于需要快速加载、对 SEO 有较高要求的网站,服务器渲染可能更适合;而对于需要复杂交互和实时更新的应用,客户端渲染可能更具优势。
Vue.js如何实现SSR:vue.js通用应用框架Nuxt,其提供了平滑的开箱即用的体验,建立在同等的vue.js技术栈之上,但抽象出很多模板,并提供了一些额外的功能,例如静态站点生成。通过NUXT可以根据约定的规则,快速的实现Vue SSR。
对于路由器在Nuxt中的工作方式,首先应了解路由器如何在Vue中的工作方式,如果你本身就不了解vue的路由工作方式上来就看nuxt的话,那么我建议你好好看看我之前讲解的文章:Vue 3 路由进阶——从基础到高级的完整指南 ,当然vue2路由讲解也在我这篇vue专栏里面,想了解的朋友欢迎大家的订阅。
在了解完vue路由工作方式之后,接下来我们就正式开始讲解如何在Nuxt应用程序中实现路由器。
目录
创建路由
基本路由
动态路由
嵌套路由
动态嵌套路由
验证路由参数
_.vue文件处理未知路由
创建自定义视图
自定义应用程序模板
创建自定义HTML头
创建自定义布局
创建自定义错误页面
创建自定义页面
创建过渡效果
利用pageTransition实现转换
利用layoutTransition实现转换
创建路由
在Nuxt中路由的过程较为简单,因为Nuxt中包含了vue-router,这意味着从技术上讲我们略过了传统vue应用程序中的安装步骤,Nuxt将扫描/pages/目录中的.vue文件树,并自动生成路由。说的简单点就是:Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。生成的文件如下:
基本路由
基本路由可以通过简单地将包含固定文件名的 .vue 文件添加至/pages/目录中而创建,此外我们还可以通过将 .vue 文件整合至不同的文件夹中来生成子路由,考察下面示例:
随后,Nuxt将生成下列路由(无需编号),component组件路径在上面通过变量进行定义:
这一类路由适用于顶级页面,如/about,/contact和/posts。然而如果每个顶级页面中包含多个子页面,并且它们会随着时间的推移而动态增加,那么应该使用动态路由处理这些子页面的路由,接下来讨论如何创建动态路由。
动态路由
当使用下划线时,Nuxt将生成动态路由。在复杂应用程序中,动态路由十分有用且不可避免,因此如果需要创建动态路由,那么仅需生成一个包含前缀下划线的 .vue文件(或目录),随后是文件名(或目录名)。考察下面示例:
随后可从Nuxt中获得下列路由,并且无需编写任何路由:
在上面的图片中你会发现名称为 users-id 的路由路径带有 :id? 参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。
动态路由适用于共享公共布局页面。
例如:如果/about和/contact具有相同的布局,那么可将上述动态路由代码中的/_test/目录视为较好的选择方案;如果/users路由下共享相同布局的子页面一样,/_id.vue文件方法是这一场景很好的选择。
当渲染子页面时,我们不希望父布局完全被子布局取代,换言之我们需要在父布局中渲染子页面,这里我们需要使用更为复杂的动态路由,即嵌套路由,如下:
嵌套路由
简单来讲,嵌套组件中生成的路由被称为嵌套路由,在某些时候我们可能打算组合嵌套于其他组件(父组件)中的组件(子组件),并希望在父组件中的特定视图中渲染这些子组件,而不是通过子组件替换父组件。
在Nuxt中可通过vue-router的子路由创建嵌套路由。如果打算定义嵌套路由的父组件,则需要创建一个与包含子视图的目录同名的vue文件。考查下列示例:
Nuxt将自动生成下列路由,可以看到Nuxt生成的路由与在vue应用程序生成的路由相同:
注意:在Nuxt中我们的父组件中包含了<nuxt-child>而在vue中我们的父组件包含了<router-view>
接下来通过<nuxt-child>组件创建一个父组件:
// pages/users.vue
<template>
<div>
<h1>Users</h1>
<nuxt-child />
</div>
</template>
这里我使用了axios发起请求,需要在nuxt.config.js进行如下配置,当你正确配置了 @nuxtjs/axios 模块之后,它会自动将 $axios 对象注入到你的组件中,你可以直接在组件中使用 $axios 对象来发起请求,而不需要再单独引入或定义 axios。
创建一个索引子组件以保存用户列表:
// pages/users/index.vue
<template>
<ul>
<li v-for="user in users" :key="user.id">
<nuxt-link :to="`users/${user.id}`">{{ user.name }}</nuxt-link>
</li>
</ul>
</template>
<script>
export default {
// asyncData 是 Nuxt.js 中的一个特殊方法,用于在加载页面之前预取数据。
async asyncData({ $axios }) {
let { data } = await $axios.get('/users');
return { users: data }
},
};
</script>
创建一个独立的子组件,其中包含子索引页面的链接:
// pages/users/_id.vue
<template>
<div v-if="user">
<h2>{{ user.name }}</h2>
<nuxt-link class="button" to="/users">Users</nuxt-link>
</div>
</template>
<script>
export default {
async asyncData({params, $axios}) {
let { data } = await $axios.get('/users/' + params.id)
return { user: data }
},
};
</script>
动态嵌套路由
前面内容分别讲述了动态路由合嵌套路由的工作,从理论和技术上讲,可将这两个选项组合在一起创建动态嵌套路由,即动态父元素(如 _topic)中包含动态子元素(如 _subTopic),对应示例如下:
nuxt将自动生成下列路由
routes: [
{
path: '/',
component: _136aafc6,
name: 'index',
},
{
path: '/:topic',
component: _232b2e28,
children: [
{
path: '',
component: _26784a16,
name: 'topic',
},
{
path: ':subTopic',
component: _4ece9572,
children: [
{
path: '',
component: _61281ad0,
name: 'topic-subTopic',
},
{
path: ':slug',
component: _5f717988,
name: 'topic-subTopic-slug',
},
],
},
],
},
],
可以看到对应的路由更加复杂 这个应用场景比较少见,但是 Nuxt.js 仍然支持:在动态路由下配置动态子路由。
创建动态路由和页面通常需要使用路由中的参数(即路由参数),以便可将其传递至所处理的动态页面中.但在处理和响应参数之前,较好的做法是对参数进行验证,因此需要我们验证路由参数:
验证路由参数
我们可以采用组件中的 validate() 方法验证动态路由中的参数,随后以替补方式处理或获取进一步的数据。这一验证的过程应返回 true 以继续后续操作,如果得到 false 那么Nuxt将终止路由并即刻抛出一个404页面错误,例如,应确保ID是一个数字,如下所示:
export default {
validate({ params }) {
// 必须是number类型
return /^\d+$/.test(params.id)
}
}
如果打算自定义404消息,则可以使用throw语句抛出一个包含Error对象的异常,如下所示:
export default {
validate({ params }) {
// 必须是number类型
let test = /^\d+$/.test(params.id)
if (test === false) {
throw new Error('User not found')
}
return true
}
}
此外,还可将 async 与 validate() 方法一起用于 await 操作:
async validate ({ params }) {
// ...
}
在validate()方法中,还可以使用 return 语句并返回 Promise,如下所示:
validate({ params }) {
return new Promise(...)
}
验证路由参数是处理无效或未知路由的一种方式,另一种方法则是使用 _.vue 文件对其进行捕捉
_.vue文件处理未知路由
除了利用validate()方法抛出通用的404页面,还可以使用_.vue文件抛出自定义的错误页面,如下
在/pages/目录中创建一个空的 _.vue 文件,如下所示:
当我们在根路由随便访问一个路径时就会跳转到该页面,如下:
如果打算在特定级别上抛出一个特定的404界面,如仅在/users路由中,则可在/users/文件夹下创建另一个 _.vue 文件,如下:
如果访问/pages/users/下的路径不对,则会调用/pages/users/_.vue进行处理,如下:
创建自定义视图
从软件的架构的角度来看,HTML标记和内容(包括元信息、图像和字体)均为应用程序的视图或表示层,在nuxt中我们可方便地创建自定义视图。
Nuxt中的视图结构包含应用程序模板、HTML头、布局和页面层,我们可以通过它们创建应用程序路由的视图。在更加复杂的应用程序中,我们可通过API中的数据填充它们;而在简单的应用程序中我们可通过手动方式直接将虚拟数据嵌入其中,如下显示了Nuxt视图的完整结构:
可以看到,Document - HTML 是 Nuxt 视图的最外层,随后依次是Layout、Page以及可选的Page Child层和Vue Component层。其中 Document - HTML 文件表示为Nuxt应用程序的应用程序模板,接下来就依次讲解这些模板:
自定义应用程序模板
Nuxt在幕后创建应用程序模板,基本上讲我们无需参与其中,然而自定义操作还是需要的,如要添加脚本或样式。默认的Nuxt HTML模板较为简单,如下所示:
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
定制化默认的 html 模板,只需要在 src 文件夹下(默认是应用根目录)创建一个 app.html 的文件,如下所示,重启应用程序可以看到,自定义应用程序HTML模板包含了替换后的Nuxt默认项
<!DOCTYPE html>
<!--[if IE 9]><html lang="en-US" class="lt-ie9 ie9" {{ HTML_ATTRS }}><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html {{ HTML_ATTRS }}><!--<![endif]-->
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
注意:我们并不直接在应用程序模板中添加或自定义这些数据,而是在Nuxt配置文件和/pages/目录的文件中执行此类操作。
创建自定义HTML头
在Nuxt应用程序中创建和自定义元数据相对简单,我们可在Nuxt配置文件 nuxt.config.js 中使用head属性,进而定义默认应用程序的<meta>标签,如下所示:
如果需要针对特定页面添加自定义标签,或者覆盖Nuxt配置文件中的默认元标签,那么可直接在对应的特定页面上使用head方法,这将返回一个JavaScript对象,该对象包含了title和meta选项的数据,如下所示:
<template>
<div>
关于我们
</div>
</template>
<script>
export default {
// 设置 head 相应局部配置
head() {
return {
title: '关于我们',
meta: [
{ hid: 'description', name: 'description', content: '关于此页面的描述' },
{ hid: 'keywords', name: 'keywords', content: '关于此页面的关键字' },
]
}
}
}
</script>
设置完成之后,我们就可以对某个单独的局部组件页面进行相应的head设置:
查看网页源代码也可以看到我们设置的某些内容和关键字,方便能够被爬虫爬到:
当然这个head的内容也可以动态的展示,类似那种新闻列表,在当前页面点击不同的新闻,head下的title和描述都会发生相应的改变,这里就不再过多赘述了,简单提一下。
创建自定义布局
布局是页面和组件的重要内容,用户可能需要在应用程序中包含多个不同的布局,所以我们可以在根目录中创建 layouts 文件夹,在文件夹下创建一个名为 default.vue 默认布局文件或者创建自己的自定义布局。比如我们这里创建一个自定义布局,如下:
这里的<nuxt />组件表示需要Nuxt导入的组件页面,该页面的布局就是要引入该布局组件的布局:
随后我们可在页面组件中使用layout属性,并将这一自定义布局赋予对应页面,如下所示:
<template>
<div>
about页面
</div>
</template>
<script>
export default {
layout: 'about'
// 或者下面比较复杂的写法
// layout (context) {
// return 'about'
// }
};
</script>
当我们访问about页面时,结果如下:
创建自定义错误页面
每个安装后的Nuxt应用程序都配备了默认的错误页面,并存储于/node_modules/目录的@nuxt中,用以显示错误信息,如404、500等,另外通过将error.vue文件添加至/layouts中,还可以实现自定义错误页面。
如果我们没有设置 _.vue文件处理未知路由和设置error.vue文件,默认的错误页面如下:
如果没有设置 _.vue 文件而是想设置自定义错误页面,需要在/layouts/目录中创建如下页面:
// layouts/error.vue
<template>
<div>
<h2 v-if="error.statusCode === 404">Page not found</h2>
<h2 v-else>An error occurred</h2>
<nuxt-link to="/">Home page</nuxt-link>
</div>
</template>
<script>
export default {
props: ['error'],
}
</script>
注意:这个error.vue错误页面是一个页面组件,即使置于/layouts/目录中但仍然被视为一个页面,如果想设置页面布局组件,需要额外创建一个:
// layouts/layout-error.vue
<template>
<div>
<h1>Error!</h1>
<nuxt/>
</div>
</template>
将layout-error简单地添加至错误页面上的layout选项上:
最终的效果如下:
创建自定义页面
页面本质上是一个Vue组件,页面与标准Vue组件的区别在于仅在Nuxt中添加的属性和函数。在渲染页面之前,我们使用这些特定的属性和函数来设置或获取函数,接下来简单介绍一下针对Nuxt页面设计的一些属性和函数:
asyncData方法:Nuxt通常在初始化页面组件之前调用该函数,这意味着每次请求一个页面时,该函数将在渲染页面之前被调用。
<template>
<div v-if="user">
<h2>{{ user.name }}</h2>
<nuxt-link class="button" to="/users">Users</nuxt-link>
</div>
</template>
<script>
export default {
async asyncData({params, $axios}) {
let { data } = await $axios.get('/users/' + params.id)
return { user: data }
},
};
</script>
fetch方法: fetch方法的工作方式与asyncData方法基本相同。类似于asyncData方法,fetch方法以异步方式加以使用。例如可以使用fetch方法设置页面组件中的数据,注意:data方法必须于fetch方法结合使用以设置数据。由于fetch方法在页面组件初始化之后被调用,因此可使用this关键字访问方法中的对象。
// pages/users/index.vue
<template>
<ul>
<li v-for="user in users" :key="user.id">
<nuxt-link :to="`users/${user.id}`">{{ user.name }}</nuxt-link>
</li>
</ul>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
users: []
};
},
async fetch() {
let { data } = await axios.get('https://jsonplaceholder.typicode.com/users');
this.users = data
},
};
</script>
head方法: 如前所述,head方法用于设置页面上的<meta>标签。此外该方法还可与/conmponents/目录中的组件结合使用。
layout属性:如前所述,layout键(或属性)用于指定页面/layout/目录中的某个布局。
loading属性:loading属性可禁用默认的加载进度条,或者设置特定页面上的自定义加载进度条,我们可以在Nuxt配置文件 nuxt.config.js 文件中配置全局默认加载组件,如下所示:
// nuxt.config.js
export default {
loading: {
color: '#000000'
}
}
利用下列代码在/pages/目录中创建index.vue页面:
// pages/users/index.vue
<template>
<div class="container">
<p>Hello {{ name }}</p>
<nuxt-link to="/about">
Go to /about
</nuxt-link>
</div>
</template>
<script>
export default {
asyncData() {
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({name:'World'})
},1000)
})
},
};
</script>
利用下列代码在/pages/目录中创建名为about.vue的另一个页面:
<template>
<div class="container">
<p>About Page</p>
<nuxt-link to="/">
Go to /
</nuxt-link>
</div>
</template>
<script>
export default {
asyncData() {
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({})
},1000)
})
},
};
</script>
在这两个页面中我们使用了setTimeout将数据响应时间延迟了一秒,因此在页面导航时,可看到在请求页面加载之前黑色的加载进度条呈现于页面的上方:
当然我们也可以在/components/目录下创建一个组件,我们可创建一个自定义加载进度条或层:
// components/loading.vue
<template>
<div v-if="loading" class="loading-page">
<p>loading...</p>
</div>
</template>
<script>
export default {
data() {
return {
loading: false
}
},
methods: {
// 路由改变时调用
start(){
this.loading = true
},
// 路由加载时调用
finish(){
this.loading = false
}
}
};
</script>
然后在nuxt.config.js中进行引入该文件:
export default {
// 加载属性
loading: '~/components/loading.vue',
}
此外我们还可以在特定的页面上配置加载行为,如果页面上的loading键为false,这将自动终止调用finish和start方法,当然我们可以在脚本中通过手动方式对其加以控制
export default {
loading: false
}
transition属性:transition属性用于指定页面的转换。我们可通过transition键使用一个字符串、对象或函数,这个在下个标题讲解动画的时候在着重讲解。
scrollToTop属性:当需要嵌套路由中的页面在渲染前于顶部启动时,可使用scrollToTop键。默认状态下当访问另一个页面时,Nuxt将滚动至顶部;但在嵌套路由中的子页面中,Nuxt仍处于相同的滚动位置(来自上一个子路由)。因此针对这些子页面,当通知Nuxt滚动至顶部时,可将scrollTop设置为true,如下所示:
// pages/users/_id.vue
export default {
scrollToTop: true
}
validate方法: 上文所述,可将validate方法视为动态路由的一个验证器。
middleware属性:该属性用于页面的中间件。赋值后的中间件将在页面渲染前执行,这里后面讲解。
创建过渡效果
在Nuxt中我们还可以进一步添加页面间的效果和转换,这也是页面中的transition属性、Nuxt配置文件中pageTransition和layoutTransition选项的用武之地。
vue提供了基于下列转换模式的转换方案:
1)in-out模式:该模式令新元素首先转换,直至结束;随后当前元素实现转换。
2)out-in模式:该模式令当前元素首先转换,直至结束;随后令新元素转换。
利用pageTransition实现转换
在Nuxt中,<transition>组件不再必需并被默认添加。因此我们仅需在特定页面的/assets/目录或<style>中创建转换,对此可在Nuxt配置文件中使用pageTransition属性设置页面转换的默认属性,转换属性的默认值如下:
{
name: 'page'
mode: 'out-in'
}
默认状态下,Nuxt采用 page- 定义转换类,这一点与Vue有所不同(使用 v- 作为前缀),在Nuxt中默认的转换模式被设置为out-in,接下来讲解如何在Nuxt中实现转换:
1)在/assets/目录中创建一个transition.css文件,并向其中添加下列转换:
/* assets/css/transition.css */
.page-enter, .page-leave-to{
opacity: 0;
color: red;
}
.page-leave, .page-enter-to{
opacity: 1;
color: skyblue;
}
.page.enter-active, .page-leave-active{
transition: opacity 300ms;
}
2)想Nuxt配置文件中添加上述CSS转换资源路径
// nuxt.config.js
export default {
css: [
'~assets/css/transition.css'
],
}
3)记住,默认前缀是page-。如果打算使用不同的前缀,可在Nuxt配置文件中使用pageTransition属性修改前缀:
// nuxt.config.js
export default {
pageTransition: 'fade'
// or
pageTransition: {
name: 'fade',
mode: out-in'
}
}
4)前全部的默认类名修改为transition.css中的fade,如下:
/* assets/css/transition.css */
.fade-enter, .fade-leave-to{
opacity: 0;
color: red;
}
.fade-leave, .fade-enter-to{
opacity: 1;
color: skyblue;
}
.fade.enter-active, .fade-leave-active{
transition: opacity 300ms;
}
5)如果打算向特定页面应用不同的动画过渡效果,或覆盖某个页面的全局转换,则可在对应的页面的transition属性中进行设置,如下所示:
export default {
// pages/about.vue
transition: {
name: 'page', // 采用默认前缀
mode: 'out-in'
}
};
最终的效果如下:
利用layoutTransition实现转换
CSS转换不仅适用于页面组件,同样还适用于布局,默认的layoutTransition属性如下所示,默认布局的前缀为layout,且默认转换模式是out-in:
{
name: 'layout',
mode: 'out-in'
}
1)在/layouts/目录下创建about.vue组件如下:
<template>
<div>
<div>About Layout</div>
<nuxt />
</div>
</template>
2)将上述布局应用到pages目录下的about组件中:
<template>
<div class="container">
<p>About Page</p>
<nuxt-link to="/">
Go to /
</nuxt-link>
</div>
</template>
<script>
export default {
layout: 'about',
};
</script>
3)在/assets/目录下创建一个transition.css文件,并向其中添加下列转换:
/* assets/css/transition.css */
.layout-enter, .layout-leave-to{
opacity: 0;
color: red;
}
.layout-leave, .layout-enter-to{
opacity: 1;
color: skyblue;
}
.layout.enter-active, .layout-leave-active{
transition: opacity 300ms;
}
4)向Nuxt配置文件添加上述CSS的转换资源路径:
// nuxt.config.js
export default {
css: [
'~assets/css/transition.css'
],
}
5)默认的前缀为layout-,但是如果打算使用不同的前缀,还可在Nuxt配置文件中通过layoutTransition属性对其进行修改:
// nuxt.config.js
export default {
layoutTransition: 'fade-layout'
// or
layoutTransition: {
name: 'fade-layout',
mode: out-in'
}
}
最终呈现的效果如下:
上述两个方法讲述的CSS转换仅在两种状态(即开始状态和结束状态)间执行的动画,当需要更多的中间状态时,应使用CSS动画以便实现更多的控制,即利用开始和结束状态间不同的百分比添加多个关键帧,这里就不再赘述了,感兴趣的朋友可以自行百度学习。
本文章主要讲解了Nuxt.js创建路由、视图和过渡效果的实现,下篇文件将继续讲解Nuxt.js的相关知识,关注博主不迷路,学习更多的前端知识!