Nuxt.js教程

Nuxt.js简介

什么是Nuxt.js

  • Nuxt.js是一个给予Vuejs的通用应用框架
  • 通过对客户端/服务端基础框架的抽象组织,Nuxt.js主要关注的是应用的UI渲染
  • Nuxt.js的目标是创建一个灵活的应用框架,你可以基于它初始化项目的基础代码架构,或者在已有的Node.js项目中使用Nuxt.js。其中预设了利用Vue.js开发服务端渲染的应用所需的各种配置
  • 做为框架Nuxt.js为客户端/服务端这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载,中间件支持,布局支持等

为什么使用Nuxt.js

主要是实现服务端的渲染(SSR),利用Nuxt.js服务端渲染能力来解决Vue项目的seo的问题。

什么是客户端渲染(SSR)

  1. 浏览器通过ajax想服务端java servlet发送http请求
  2. 服务端获取接口的数据封装成json,然后返回给浏览器
  3. 浏览器拿到json数据进行渲染html页面,生成dom元素,然后将页面展示给用户

客户端渲染的特点

  • xxxxxxxxxx  - job_name: nginx-exporter   scrape_interval: 30s   scrape_timeout: 30s   static_configs:     - targets:       - ‘x.x.x.x:9145’       labels:         instance: nginx-master         group: “nginx-master”     - targets:       - ‘x.x.x.x:9145’       labels:         instance: nginx-slave         group: “nginx-slave”   metrics_path: ‘metrics’ # nginx exporter默认的metrics路径 http://172.22.16.123:9145/metrics   scheme: ‘http’yaml
  • 浏览器负责发送请求获取服务端数据,然后渲染html页面

什么是服务端渲染(SSR)

  1. 浏览器想服务端java servlet发送http请求数据接口
  2. 服务端servlet会生成html页面,响应给浏览器
  3. 浏览器将接受的html页面展示给用户

服务端渲染的特点

  • 在服务端生成html页面
  • 浏览器只负责展示html页面

为什么使用服务器渲染

  • 更好的SEO,搜索引擎或爬虫工具可以直接查看渲染的页面
  • 更快的加载时间,因为服务端只需要返回加载好的html页面,所以响应时间更快

Vue.js如何实现SSR

  1. 基于官方VUE SSR指南文档的官方方案,官方方案具有更直接的控制应用程序的结构,更深入底层,更加灵活,同时在使用官方方案的过程中对VUE SSR有更深入的了解
  2. Vue.js通用框架nuxt提供了平滑的开箱即用的体验,它建立在同等Vue.js技术栈之上。但抽象出更多模版,并提供了一些额外的功能,例如静态网站的生成。通过nuxt的规则可以快速实现Vue SSR

Nuxt.js工作原理

  1. 浏览器发送http请求到Node.js服务端
  2. 部署在Node.js的应用Nuxt.js接受到浏览器的请求,他会去请求后台接口
  3. 后台接口服务端响应json数据,Nuxt.js获取数据后进行服务端渲染html
  4. Nuxt.js将html页面响应给浏览器
  5. 浏览器将接受到的html页面进行展示

image-20231113113737185

环境搭建

全局安装create-nuxt-app

npm install -g create-nuxt-app
$create-nuxt-app nuxt-demo

create-nuxt-app v5.0.0
✨ Generating Nuxt.js project in nuxt-demo
? Project name: nuxt-demo
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Template engine: HTML
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? acaiblog
? Version control system: Git

🎉 Successfully created project nuxt-demo

启动项目

cd nuxt-demo
npm run dev

修改端口

export default {
mode: 'universal', //代表SSR
server: {
port: 8080,
host: '0.0.0.0'
},
}

目录结构

路径 描述
.nuxt 执行npm run dev命令后编译的目录文件
assets 用于组织未编译的静态资源如,LESS、SASS或JavaScript
components 用于组织应用的Vue.js组件,Nuxt.js不会扩展增强该目录下Vue.js组件,这些组件不会像页面组件有asyncData方法的特性
layouts 用于组织应用的布局组件
middleware 用于存放应用的中间件
pages 用于组织应用的路由和视图
.vue 自动生成对应的路由配置
plugins 用于组织那些需要在根vue.js应用实例化之前需要运行的javas cript插件
static 静态文件目录,此类文件不会被Nuxt.js调用webpack进行构建编译处理。服务器启动的时候该目录下文件会映射到应用根目录
store 用于组织应用的Vuex状态树文件,Nuxt.js继承了Vuex状态树的相关配置功能。在store目录下创建index.js激活这些配置
editconfig 用于指定编辑器编写项目的代码风格
nuxt.config.js 用于组织nuxt.js应用自定义配置,可以覆盖默认的配置
package.json 用于描述应用依赖关系和对外暴漏的接口配置

布局

默认布局

当执行npm run dev命令之后会在.nuxt/views/app.template.html生成一个模版页面,{{ APP }}渲染的是pages目录下的组件。页面中表达式引用的是nuxt.config.js中的配置

<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>

自定义布局

在项目根目录下创建app.html页面,来自定义页面布局。在默认页面的基础上新增了h2导航栏。

<!DOCTYPE html>
<html {{ HTML_ATTRS}} >
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
<h2>导航栏</h2>
{{ APP }}
</body>
</html>

组件布局

像上面app.html这种方式不能在实际项目中使用,一般用vue组件化方式开发。

自定义组件布局

在layouts目录下创建.vue文件来自定义布局,然后通过页面组件pages目录下的文件中的layout属性来引用自定义布局,创建layouts/blog.vue

<template>
<div>
<!-- 头部导航-->
<div>
<h1>博客导航栏</h1>
</div>
<!-- 主体内容-->
<nuxt/>
</div>
</template>

在pages目录下的页面组件通过layout属性引用自定义布局,如pages/article.vue

<template>
<div>
<h3>主体内容-文章列表</h3>
</div>
</template>

<script>
export default {
layout(context){
console.log('context', context)
return 'blog'
}
}
</script>

使用浏览器访问:http://localhost:8080/ariticle
注意:如果layouts/blog目录下放了index.vue,直接通过layout: 'blog'是找不到这个页面的需要指定文件名index才能找到,即layout: 'blog/index',如下所示

<template>
<div>
<h3>主体内容-文章列表</h3>
</div>
</template>

<script>
export default {
layout(context){
console.log('context', context)
return 'blog/index'
}
}
</script>

重构默认组件default.vue,编辑layouts/default.vue

<template>
<div>
<!-- 头部导航-->
<div class="header">
<ul>
<li><a>首页</a></li>
<li><a>文章</a></li>
<li><a>问答</a></li>
<li><a>个人中心</a></li>
</ul>
</div>
<!-- 主体内容-->
<nuxt/>
</div>
</template>
<style scoped>
* {
margin: 0 auto;
font-size: 18px;
}
.header{
width: 100%;
height: 50px;
background-color: #ffffff;
}
.header ul{
margin-left: 80px;
}
.header li{
display: inline;
line-height: 50px;
margin-left: 60px;
}
.header a{
color: #000000;
text-decoration: none; /*去除下划线*/
}
.header a:hover{
color: aqua;
cursor: pointer;
}
</style>

浏览器访问http://localhost:8080查看默认布局变化

自定义错误页面

通过编辑layouts/error.vue文件来自定义错误页面,虽然此文件在layouts文件夹中应该把它看做是一个页面而不是布局使用。

<template>
<div>
<h1 v-if="error.statusCode === 404">
您访问的页面不存在: {{ error.message }}
</h1>
<h1 v-else>请求接口失败</h1>
</div>
</template>
<script>
export default {
props: ['error']
}
</script>

浏览器发送错误请求验证:http://localhost:8080/test

设置页面头部标签

使用head()方法设置头部标签属性,在head方法中使用this关键字获取组件的数据。编辑pages/index.vue

<template>
<div>
<h3>主体内容-首页</h3>
</div>
</template>
<script>
export default {
data(){
return{
title: "阿才的nuxt博客"
}
},
head(){
return {
bodyAttrs: { // 对应{{ BODY_ATTRS }} body标签属性
style: 'background-color: #fff'
},
title: this.title,
meta: [
// hid指定标识,防止不能覆盖父组件中的<meta name=''discription'>标签,hid: 'discription'就会覆盖父组件中name='discription'的meta标签
{hid: 'discription', name: 'discription', content: 'nuxt.js技术栈'}
]
}
}
}
</script>

插件

Nuxt.js允许你在运行Vue.js应用程序之前执行js插件,这在你使用自定义库或使用第三方库时特别有用。可以将自定义的插件注入到Vue实例(客户端)、context(服务器端)、store(Vuex)。

注入Vue实例

将内容注入Vue实例,避免重复引入在Vue原型上挂载一个函数。所有组件内都可以访问不包含服务器端。编辑plugins/vue-inspect.js

import Vue from 'vue'

Vue.prototype.$myVueFunction = string => console.log('绑定到Vue实例的方法参数', string)

编辑nuxt.config.js

export default {
plugins: ['~/plugins/vue-inspect.js'],
}

这样就可以在所有Vue组件中使用该函数,编辑pages/index.vue

export default {
mounted() {
this.$myVueFunction('test')
},
}

打开浏览器console,查看打印的日志信息。

注入context

注入context的方法和注入Vue实例方式类似,编辑plugins/ctx-inspect.js

export default ({app}, inspect)=>{
app.myContextFunction = string => console.log('绑定到Context的方法参数', string)
}

编辑nuxt.config.js

export default {
plugins: ['~/plugins/vue-inspect.js', '~/plugins/ctx-inspect.js'],
}

编辑pages/index.vue

export default {
asyncData(context) {
context.app.myContextFunction('ctx!')
},
}

只在客户端执行的插件

编辑nuxt.config.js

export default {
plugins: [
{src: '~/plugins/vue-inspect.js', mode: 'client'},
{src: '~/plugins/ctx-inspect.js', mode: 'server'}
],
}

异步加载数据

Nuxt.js扩展了Vue.js增加了一个asyncData的方法,让我们可以在渲染组件之前异步获取数据。asyncData方法会在页面组件每次加载之前被调用,它可以在服务端或路由更新之前被调用。
asyncData方法被调用的时候,第一个参数context被设定为当前页面的上下文对象,你可以利用asyncData方法获取数据。Nuxs.js会讲asyncData返回的数据与data方法返回的数据一起合并后返回给当前组件。
安装@nuxtjs/axios

npm install @nuxtjs/axios

在nuxt.config.js中引入@nuxtjs/axios

export default {
modules: [
'@nuxtjs/axios'
],
}

在mock.mengxuegu.com创建测试接口,接口信息如下:

URL 请求方法
/test GET

响应数据:

{
"code": 20000,
"message": "success",
"data": {
"title": "@ctitle",
"content": "@cparagraph",
"author": "@cname"
}
}

首页请求数据接口

使用axios返回Promise

编辑pages/index.vue

<template>
<div class="container">
<h2>首页标题: {{title}}</h2>
<ul>
<li>{{ data.title }}</li>
<li>{{ data.content }}</li>
<li>{{ data.author }}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return {
title: "阿才的博客"
}
},
asyncData({$axios}) {
return $axios.$get('https://mock.mengxuegu.com/mock/6551f3e12faae115bb879f70/test').then(response =>{
console.log('response: ', response)
const data = response.data
return {data}
})
}
}
</script>

async与await

export default {
data(){
return {
title: "阿才的博客"
}
},
async asyncData({$axios}){
const response = await $axios.get('https://mock.mengxuegu.com/mock/6551f3e12faae115bb879f70/test')
console.log('response', response.data)
return { data: response.data }
}
}

Nuxt.js解决代理跨域

创建MOCK API接口

method URL data
get /api/test {"code":20000,"message":"success","data":{"title":"@ctitle","content":"@cparagraph","author":"@cname"}}

编辑nuxt.config.js

export default {
modules: [
'@nuxtjs/axios',
],
axios: {
proxy: true,
prefix: '/api'
},
proxy: {
// 将/api替换为'',然后转发到target指定的url
'/api': {
target: 'https://mock.mengxuegu.com/mock/6551f3e12faae115bb879f70',
pathRewrite: {'^api/': ''}
}
}
}

编辑pages/index.vue

export default {
asyncData({$axios}) {
return $axios.$get('/test').then(response => {
console.log('response: ', response)
const data = response.data
return {data}
})
}
}

$axios拦截器

创建plugins/interceptor.js

export default ({ store, route, redirect, $axios }) => {
$axios.onRequest( config => {
console.log('请求拦截器', config);
return config
} )
$axios.onResponse(
response => {
console.log('请求响应器', response)
return response
}
)
$axios.onError(
error => {
console.log('error.response.status', error.response.status)
}
)
}

引用拦截器,编辑nuxt.config.js,然后刷新页面验证

plugins: [
{ src: '~/plugins/vue-inspect.js', mode: 'client' },
{ src: '~/plugins/ctx-inspect.js', mode: 'server' },
'~/plugins/interceptor.js'
]

插件方式在Nuxt.js中调用API

创建api/test.js插件

import {inject} from "vue";

export default ({ $axios }, inject) => {
inject('test', data => $axios.get('/test'))
}

编辑nuxt.config.js,引入插件

plugins: [
{ src: '~/plugins/vue-inspect.js', mode: 'client' },
{ src: '~/plugins/ctx-inspect.js', mode: 'server' },
'~/plugins/interceptor.js',
'~/api/test.js'
]

在组件中调用,编辑pages/index.vue

export default {
data(){
return {
title: '',
content: '',
author: ''
}
},
async asyncData({ app }) {
const response = await app.$test()
console.log('test response:', response.data.data)
return response.data.data
}
}

路由Vue-route

Nuxt.js根据pages目录结构自动生成vue-router模块配置路径,不需要额外配置路由。查看.nuxt/router.js文件,可以看到自动生成的路由配置

routes: [{
path: "/ariticle",
component: _22b3fb77,
name: "ariticle"
}, {
path: "/",
component: _9d2c518c,
name: "index"
}]

在页面之间跳转建议使用<nuxt-link>标签<nuxt-link>的作用与Vue的<route-link>一致。编辑layouts/default.vue将a标签替换为nuxt-link标签

<template>
<div>
<!-- 头部导航-->
<div class="header">
<ul>
<li><nuxt-link to="/home">首页</nuxt-link></li>
<li><nuxt-link to="/ariticle">文章</nuxt-link></li>
<li><nuxt-link to="/ariticle">问答</nuxt-link></li>
<li><nuxt-link to="/ariticle">个人中心</nuxt-link></li>
</ul>
</div>
<!-- 主体内容-->
<nuxt/>
</div>
</template>

基础路由

在pages目录下创建pages/user/index.vue

<template>
<h2>个人中心</h2>
</template>

重新运行项目会在.nuxt/router.js文件中生成user的路由信息,如下:

routes: [{
path: "/user",
component: _aad8c354,
name: "user"
}]

在layouts/default.vue中添加个人中心跳转配置,点击个人中心会调转到用户组件

<template>
<div>
<!-- 头部导航-->
<div class="header">
<ul>
<li><nuxt-link to="/user">个人中心</nuxt-link></li>
</ul>
</div>
<!-- 主体内容-->
<nuxt/>
</div>
</template>

动态路由

在Nuxt.js中定义带参数的动态路由,需要创建以下划线为前缀的文件或目录。

下划线为前缀的Vue文件

创建pages/pages/user/_id.vue

<template>
<h2>获取到的用户ID为: {{ $route.params.id }}</h2>
</template>
<script>
export default {
// 校验参数值
validate ({ params }) {
return /^\d+$/.test(params.id)
},
asyncData({ params }){
const id = params.id
console.log(`查询id=${id}`)
}
}
</script>

生成路由

routes: [{
path: "/user/:id",
component: _13fd8384,
name: "user-id"
}]

浏览器访问:http://localhost:8080/user/2

下划线为前缀的目录

创建Vue组件pages/_question/index.vue

<template>
<h2>问题列表页面: {{ $route.params.question }}</h2>
</template>

<script>
export default {
validate({ params }) {
console.log('接受的参数值为', params.question)
const arr = ['hot', 'wait', 'red']
// 必须是列表中的元素
return arr.indexOf(params.question) != -1
},
asyncData({ params }) {
console.log('查询问题列表', params.question)
}
}
</script>

编辑layouts/default.vue修改问答地址为

<li><nuxt-link to="/hot">问答</nuxt-link></li>

浏览器请求:http://192.168.207.211:8080/hot

嵌套路由

创建内嵌子路由需要添加一个Vue文件,同时添加一个与该文件同名的目录用来存放子视图组件。在父组件.vue文件中增加<nuxt-child>标签用于显示子视图内容。

中间件

什么是中间件

中间件允许你自定义一个函数运行在页面或组件渲染之前,用于权限判断,有访问权限才可以访问。每个中间件应该放在middleware目录中,文件名称为中间件名称。比如middleware/auth.js中间件的名称为auth。

创建中间件

创建middleware/auth.js

export default ({ store, redirect }) =>{
console.log('auth.js认证中间件被执行')
if(!store || !store.state.userInfo){
return redirect('/')
}
}

使用中间件

在页面组件中使用中间件

编辑pages/user/index.vue

<script>
import auth from "@/middleware/auth";

export default {
middleware: auth
}
</script>

浏览器访问:http://192.168.207.211:8080/user 重定向到 http://192.168.207.211:8080/

在默认布局中使用中间件

编辑layouts/default.vue

<script>
import auth from "@/middleware/auth";

export default {
middleware: auth
}
</script>

nuxt.config.js中使用中间件

编辑nuxt.config.js

export default {
router: {
middleware: 'auth'
}
}
文章作者: 慕容峻才
文章链接: https://www.acaiblog.top/Nuxt-js教程/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿才的博客
微信打赏
支付宝打赏