Nuxt.js是什么?
- 一个基于Vue.js生态的第三方开源服务端渲染应用框架
- 可以帮我们轻松使用Vue.js技术构建同构应用
Nuxt.js的使用方式
- 初始新项目
- 已有的Node.js服务端项目下直接把Nuxt当作一个中间件集成到Node Web Server中
- 如果在已有的Vue项目中,则也要有一部分的代码改动,且需要对Nuxt.js非常了解
初始化一个简单的Nuxt.js项目
- mkdir nuxtjs-demo
- cd nuxtjs-demo
- npm init -y
- npm install nuxt
- 在package.json文件中配置项目启动命令如下:
"scripts": { "dev": "nuxt" }
- 根目录中创建pages目录并在pages下创建一个Vue组件index.vue
<template> <div> <h1>hello nuxt</h1> </div> </template> <script> export default { name: 'HomePage' } </script>
- 启动项目:
npm run dev
需要构建两个不同的服务 client和server
路由相关
基础路由
要在页面之间使用路由,可以使用<nuxt-link>
标签
假设pages的目录结构如下:
pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue
则Nuxt.js自动生成的路由配置如下:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}
生成的路由相关代码会在.nuxt\router.js
下
路由导航
- a标签 不推荐使用,会刷新整个页面,走的是服务端渲染的方式
- nuxt-link 组件,相当于vue-router中的router-link
- 编程式导航,通过js操作路由
动态路由
假设有以下目录结构:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue
则Nuxt.js生成的路由配置表为:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
// Nuxt会自动识别文件路径中的_id.vue,
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
会发现名称为 users-id 的路由路径带有 :id? 参数,表示该路由是可选的,可以是非空的任意值。如果想将它设置为必选的路由,需要在 users/_id
目录内创建一个index.vue
文件
嵌套路由
可以通过vue-router
的子路由创建 Nuxt.js 应用的嵌套路由。创建内嵌子路由,需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件但是需要在父组件中添加<nuxt-child />
标签用于显示子视图内容
假设文件结构如下:
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
则Nuxt.js生成的路由配置如下:
router: {
routes: [
{
path: '/users',
component: 'pages/users.vue',
children: [
{
path: '',
component: 'pages/users/index.vue',
name: 'users'
},
{
path: ':id',
component: 'pages/users/_id.vue',
name: 'users-id'
}
]
}
]
}
自定义路由配置
在项目根目录新建一个nuxt.config.js
,此文件为Nuxt.js的配置文件,可以配置router属性等
例如:
module.exports = {
router: {
base: '/app/'
}
}
则是在每个路由的路径之前加/app/
字符前缀
扩展路由配置项
可以通过extendRoutes
选项扩展自定义的路由配置项
export default {
router: {
base: '/app/',
// routes 路由配置表
// resolve 解析路由组件路径
extendRoutes (routes, resolve) {
routes.push({
name: '404',
path: '/404',
component: resolve(__dirname, 'pages/404.vue')
})
}
}
}
级新定义一个路由配置,当路由路径为404时,指向pages/404.vue
的页面
Nuxt.js视图
页面模板
这是Nuxt.js的默认页面模板配置。当然,我们可以在里面加入符合需求的其他数据
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
Layout
Nuxt.js有默认的布局结构,可以通过添加layouts/default.vue
文件来扩展应用的默认布局。一旦使用了默认的布局,则不指定布局的页面都会使用这个默认的布局,除非特殊指定。
在布局文件中需要添加<nuxt />
标签来显示页面的主体内容
如:
<template>
<div>
<header>Header 部分</header>
<!-- 页面出口,相当于子路由 -->
<nuxt />
<footer>Footer 部分</footer>
</div>
</template>
<script>
export default {
name: 'DefaultLayout'
}
</script>
<style>
</style>
可以在页面中指定布局组件,而不是使用默认的布局:
<template>
<div>
<h1>About Page</h1>
<button @click="onClick">首页</button>
</div>
</template>
<script>
export default {
name: 'AboutPage',
// 只需在layout中指定模板的文件名称即可
layout: 'foo',
data () {
return {
}
},
methods: {
onClick () {
console.log('flow')
this.$router.push('/')
}
}
}
</script>
静态文件
静态文件目录 static 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。服务器启动的时候,该目录下的文件会映射至应用的根路径/下,即调用data.json则实际调用的路径为static/data.json。
异步数据-asyncData
Nuxt.js扩展了Vue.js,增加了一个asyncData
的方法,使得我们可以在设置组件的数据之前可以异步获取或处理数据。
asyncData
方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,可以利用 asyncData
方法来获取数据,Nuxt.js 会将 asyncData
返回的数据融合组件 data 方法返回的数据一并返回给当前组件。
因为在asyncData
调用时,组件还没有初始化,所以在asyncData
中是没办法通过this来调用组件实例的。
假设data.json的文件内容为:
{
"post": [
{
"id": 1,
"title": "笔记本1",
"body": "联想笔记本1"
},{
"id": 2,
"title": "笔记本2",
"body": "联想笔记本2"
},{
"id": 3,
"title": "笔记本3",
"body": "联想笔记本3"
},{
"id": 4,
"title": "笔记本4",
"body": "联想笔记本4"
},{
"id": 5,
"title": "笔记本5",
"body": "联想笔记本5"
},{
"id": 6,
"title": "笔记本6",
"body": "联想笔记本6"
}
]
}
则一个简单的asyncData
方法如下:
<template>
<div>
<h1>hello nuxt</h1>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HomePage',
data () {
return {
foo: 'bar'
}
},
async asyncData () {
const res = await axios({
method: 'get',
// 此处要把路径写全,因为服务端会调用这个接口
// 如果不写全则会调用80端口对应的应用上
// data.json的路径不要写static/data.json,因为会自动从static中查找
url: 'http://localhost:3000/app/data.json'
})
return res.data
}
}
</script>
<style>
</style>
asyncData
的调用时机:
- 第一次进入页面,走的是服务端渲染,会调用
asyncData
- 通过路由进入页面,也会调用
asyncData
,以保证数据的正确。但通过路由调用时,应用已经由SSR改为CSR,所以此时是客户端渲染。
子组件是不会调用asyncData
的,如果子组件需要使用,可以由props传入。
当想要页面内容有利于SEO 或者是提升首屏渲染速度的时候,可以在asyncData中请求数据。如果是非异步请求或者普通数据,则正常的初始化到data即可。
asyncData
异步数据-上下文对象
asyncData中没有this,但可以使用上下文对象来获取需要的数据
<template>
<div>
<h1>Article Page</h1>
<h2>title: {{title}}</h2>
<h2>body: {{body}}</h2>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'ArticlePage',
async asyncData (context) {
const { data } = await axios({
method: 'get',
url: 'http://localhost:3000/app/data.json'
})
return data.post.find(item => item.id == context.params.id)
}
}
</script>
<style>
</style>
asyncData总结
- 基本用法:
- asyncData会将返回的数据和data合并
- 调用时机:服务端渲染期间和客户端路由更新之前
- 注意事项:
- 只能在页面组件中使用
- asyncData中没有this,因为是在组件初始化之前调用的,可以通过在asyncData中传入一个参数,通过该参数获取上下文对象中的数据