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)
浏览器通过ajax想服务端java servlet发送http请求
服务端获取接口的数据封装成json,然后返回给浏览器
浏览器拿到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)
浏览器想服务端java servlet发送http请求数据接口
服务端servlet会生成html页面,响应给浏览器
浏览器将接受的html页面展示给用户
服务端渲染的特点
在服务端生成html页面
浏览器只负责展示html页面
为什么使用服务器渲染
更好的SEO,搜索引擎或爬虫工具可以直接查看渲染的页面
更快的加载时间,因为服务端只需要返回加载好的html页面,所以响应时间更快
Vue.js如何实现SSR
基于官方VUE SSR指南文档的官方方案,官方方案具有更直接的控制应用程序的结构,更深入底层,更加灵活,同时在使用官方方案的过程中对VUE SSR有更深入的了解
Vue.js通用框架nuxt提供了平滑的开箱即用的体验,它建立在同等Vue.js技术栈之上。但抽象出更多模版,并提供了一些额外的功能,例如静态网站的生成。通过nuxt的规则可以快速实现Vue SSR
Nuxt.js工作原理
浏览器发送http请求到Node.js服务端
部署在Node.js的应用Nuxt.js接受到浏览器的请求,他会去请求后台接口
后台接口服务端响应json数据,Nuxt.js获取数据后进行服务端渲染html
Nuxt.js将html页面响应给浏览器
浏览器将接受到的html页面进行展示
环境搭建 全局安装create-nuxt-app
npm install -g create-nuxt-app
$create -nuxt-app nuxt-democreate-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
启动项目
修改端口
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创建测试接口,接口信息如下:
响应数据:
{ "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 : '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' } }