Appearance
nuxt-sample-demo 案例
这里已经将下面所有的完整案例全部上传到了gitHub中,nuxt-sample-demo案例网址,根据不同的分支感受不同的案例。
代码分支说明
- 在刚才初始化二的项目的中
git init
- 创建文件
.gitignore
,将node_modules
,.nuxt
写进去
TIP
.nuxt是在启动编译的时候自动生成的代码,里面包含了客户端代码和服务端渲染的代码。
- 将当前文件提交
git commit -m '初始化nuxt.js项目'
- 创建一个新的分支
git checkout -b 02-router
,如果之后运行案例,分别使用不用的分支即可。
Nuxt.js路由
基础路由
查看官网-路由
Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。
创建pages里面创建一个user文件夹,里面创建两个文件
index.vue
和user.vue
,里面填点简单的东西现在目录中的路由结构就是下面的,所有的index文件对应的是当前文件夹下的根路径,访问
http://localhost:3000/user
和http://localhost:3000/user/user
可以找到对应的页面
可以看到打包的时候.nuxt
文件中会生成一个router.js文件,里面确实引用了vue-router,生成了路,这个文件不要手动去改。
- 将这个代码commit提交
git commit -m '02基础路由'
路由导航
这里的用法去哪里看?去Vue-Router的文档看。
- a标签(会刷新整个页面,不推荐使用)
- nuxt-link组件
- 编程式导航(js跳转路由)
- 回到
master
分支并且创建一个新的分支03-路由导航
- 在
about.vue
里面写几种导航方式
html
<template>
<div>
<h1>About page</h1>
<!-- 第一种:a 链接,刷新页面,走服务端渲染 -->
<h2>a 链接</h2>
<a href="/">首页</a>
<!-- 第二种:router-link 导航链接组件 -->
<h2>router-link</h2>
<router-link to="/">router-link跳转首页</router-link>
<!-- nuxt-link 和 router-link的用法一毛一样 -->
<nuxt-link to="/">nuxt-link跳转首页</nuxt-link>
<!-- 第三种:编程式导航 -->
<h2>编程式导航</h2>
<button @click="toHome">首页</button>
</div>
</template>
<script>
export default {
name: 'AboutPage',
methods: {
toHome () {
// 和Vue-Router的用法一毛一样
this.$router.push('/')
}
}
}
</script>
<style></style>
- 提交当前分支
git commit -m '03-路由导航'
动态路由
Vue-Router是通过自定义:id之类的定义动态路由,而nuxt直接在文件中识别前面是下划线开头的即为动态路由。
- 从master分支重新创建一个分支
git checkout -b '04-动态路由'
- pages文件夹中创建user文件夹,里面创建
_id.vue
- 在
_id.vue
文件里面写
html
<template>
<div>
<!--获取到路由中的id动态参数-->
<h1>User page {{ $route.params.id }}</h1>
</div>
</template>
<script>
export default {
name: 'UserPage'
}
</script>
<style></style>
- 在浏览器中可以看到,user后面可以不跟参数,可以跟随意的参数
- 这里还可以限制传的动态数据的格式或者值,如果不符合要求,就返回错误页面
js
export default {
name: 'UserPage',
validate({ params }) {
// 必须是number类型
return /^\d+$/.test(params.id)
}
}
- 将这个分支提交
git comnmit -m '04-动态路由'
嵌套路由
创建子路由,需要添加一个vue文件,里面要增加来显示子视图内容,同时还要创建一个同名目录来存放子视图组件。
- 从master创建一个分支
git checkout -b 05-嵌套路由
- pages目录下创建文件
users.vue
html
<template>
<div>
<h1>Users</h1>
<!-- 子路由出口 -->
<nuxt-child />
</div>
</template>
<script>
export default {
name: 'UsersPage'
}
</script>
<style></style>
- 创建同名目录users,并且创建index.vue,这个是默认子路由
html
<template>
<div>
<h1>Users 默认子路由</h1>
</div>
</template>
<script>
export default {
name: 'UsersPage'
}
</script>
<style></style>
- 访问页面
http://localhost:3000/users/
,可以看到子路由的内容在父路由上
- 在users文件夹下创建user.vue文件,并写下面的内容
html
<template>
<div>
<h1>Users - user</h1>
</div>
</template>
<script>
export default {
name: 'UserPage'
}
</script>
<style></style>
6.访问http://localhost:3000/users/user
就可以看到父路由里面嵌套了user子路由
- 把内容进行提交
git commit -m '05-嵌套路由'
路由配置
配置项 | 说明 | 类型 | 默认值 |
---|---|---|---|
base | 设置网站根路径 | String | '/' |
extendRouters | 扩展路由 | Function | - |
- 创建一个新的分支
git checkout -b 06-路由配置
- 在根目录下创建一个文件
nuxt.config.js
- 里面用
CommonJS
的语法导出配置对象,路由在router的对象中
js
module.exports = {
router: {
...
}
}
bash
- 设置base为
/abc
,访问根目录就变成了http://localhost:3000/abc/
extendRoutes
- 设置一个可以访问about.vue文件的路由地址
js
module.exports = {
router: {
base: '/abc',
// routes: 路由配置表,也是一个数组,可以动态push路由
// resolve:解析路由组件路径
extendRoutes(routes, resolve) {
routes.push({
// 设置之后访问的路由
path: '/hello',
// 设置个名称
name: 'hello',
// 设置对应的组件
component: resolve(__dirname, 'pages/about.vue')
})
}
}
}
这样,访问http://localhost:3000/abc/hello
也可以访问到about.vue
其他的有需求看文档,这里不再多说。
视图
查看官网-视图
视图-模板Document
Nuxt.js可以定制应用模板,现在默认的模板不写就是
html
<!--app.html-->
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
要是想要自定义修改,在根目录下创建app.html,就在这个基础上修改即可,里面的,就是所有页面渲染之后的出口
html
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
<!-- 渲染的内容最终会注入到这里 -->
<h1>app.html</h1>
{{ APP }}
</body>
</html>
如果看不到可以重启服务,可以看到加了h1标签,页面上也有了内容
视图-结构Layout
可以在html和页面组件之间再加一层,布局组件,可以扩展默认布局,layouts/default.vue这个是所有页面组件的父路由
- 创建一个分支
git checkout -b 08-布局组件
- 在根目录下创建
layouts/default.vue
html
<template>
<div>
<h1>Layouts/default.vue</h1>
<!-- 页面出口,类似子路由,必须有 -->
<nuxt />
</div>
</template>
<script>
export default {
name: 'LayoutDefault'
}
</script>
<style></style>
- 重启服务,可以看到页面上会有内容出现,这个比较适合项目中有相同的头部和侧边栏等布局
- 这个相当于index.vue页面有一个layout配置,这个是默认的。
html
<script>
export default {
name: 'IndexPage',
layout: 'default'
}
</script>
- 如果想要自定义的话,需要在layouts里面再添加一个user.vue,然后这里的layout设置为user
html
<!--layouts/user.vue-->
<template>
<div>
<h1>Layouts/user.vue</h1>
<!-- 页面出口,类似子路由 -->
<nuxt />
</div>
</template>
<script>
export default {
name: 'LayoutUser'
}
</script>
<style></style>
html
<!--pages/index.vue-->
<script>
export default {
name: 'IndexPage',
layout: 'user'
}
</script>
可以看到首页变成了
而这个时候about没有设置layout,所以它的父组件还是default.vue
Nuxt.js异步数据
如何在服务端渲染动态页面?
具体参考官方文档 async-data
- 创建static/data.json,将数据填进去
js
{
"posts": [
{
"id": 1,
"title":"title1",
"body":"hellohellohellohello"
},
{
"id": 2,
"title":"title2",
"body":"hellohellohellohello"
},
{
"id": 3,
"title":"title3",
"body":"hellohellohellohello"
},
{
"id": 4,
"title":"title4",
"body":"hellohellohellohello"
},
{
"id": 5,
"title":"title5",
"body":"hellohellohellohello"
}
],
"title": "我是标题"
}
- 访问
http://localhost:3000/data.json
可以看到对应数据。
TIP
static目录中的资源是可以直接被访问的,nuxt在web服务中公开的,允许公开访问不经过打包的。
- 先安装
npm i axios --save
,然后在index.vue中修改
html
<template>
<div>
<!--这里使用模板-->
<h1>{{ title }}</h1>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title}}</li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'IndexPage',
// 这里使用异步数据
async asyncData () {
console.log('asyncData')
const res = await axios({
method: 'GET',
// 这里为啥不直接写data.json,是因为服务端地址默认是http://localhost:8080,所以这里需要写全名,不然请求的不是3000端口的数据
url: 'http://localhost:3000/data.json'
})
// 这里将数据return,res.data里面就是返回的数据,这里会和下面的data进行融合
return res.data
},
data () {
return {
foo: 'bar'
}
}
}
</script>
<style></style>
可以看到页面已经渲染上去了。
那么这个asyncData是在哪一步执行的呢?
- 我们上面在asyncData的函数一开始输出了一个console,可以看到这个是在服务端执行的。
Nuxt为了我们调试方便,在控制台也进行了输出,与客户端输出内容进行了区分。也可以看出来这个是在服务端渲染执行的。
- 如果有路由导航,在路由导航跳转之前也会执行的。
把index.vue和about.vue都设置上跳到对方的路由
html
<!--index.vue-->
<nuxt-link to="/about">About</nuxt-link>
<!--about.vue-->
<nuxt-link to="/">Index</nuxt-link>
从about.vue跳转到index.vue的时候,控制台也出现了asyncData,这个还是客户端执行的,并不是服务端执行的。
- 最后将代码提交
git commit -m '09-异步数据'
总结
- 功能:让我们可以在设置组件的数据之前能异步获取或处理数据
- 基本用法
- 他会将asyncData返回的数据融合组件data方法返回数据一并给组件
- 调用时机:服务端渲染期间和客户端路由更新之前
- 注意事项
- 只能在页面组件中使用(只能在pages里面使用,components里面不能使用asyncData,可以通过组件间传值使用,这个也是服务端渲染的一部分)
- 没有this,因为他是组件初始化之前被调用的(无论是客户端渲染还是服务端渲染,都在这个之前调用,在asyncData中打印this为undefined)
上下文对象
创建一个分支
git checkout -b '10-上下文对象'
创建内容模板
2.1. 创建static文件夹将之前的data.json放进去
2.2. 创建components/subPage.vue,然后修改
html<template> <div> <h1>subPage</h1> <ul> <li v-for="item in posts" :key="item.id"> <nuxt-link :to="'/article/'+item.id">{{ item.title }}</nuxt-link> </li> </ul> </div> </template> <script> export default { name: 'subPage', props:['posts'] } </script> <style></style>
2.3. pages里面创建目录是article,有一个
_id.vue
文件html<template> <div> <h1>article page</h1> </div> </template> <script> export default { name: 'ArticlePage' } </script> <style></style>
2.4. 在index.vue中引用subPage组件
html<template> <div> <h1>{{ title }}</h1> <sub-page :posts="posts"></sub-page> </div> </template> <script> import axios from 'axios' import SubPage from '../components/subPage.vue' export default { name: 'IndexPage', components: { SubPage }, async asyncData () { const res = await axios({ method: 'GET', url: 'http://localhost:3000/data.json' }) return res.data } } </script> <style></style>
2.5. 现在的样式就是点击按钮就可以跳转到具体的内容页面上去,里面要显示对应的内容
内容模板创建完毕之后,进入
_id.vue
页面接收消息
html
<script>
import axios from 'axios'
export default {
name: 'ArticlePage',
async asyncData (context) {
const res = await axios({
method: 'GET',
url: 'http://localhost:3000/data.json'
})
// 下面不能使用this,所以不能通过这种方式获取路径
// console.log(this.$route.params.id)
}
}
</script>
这个里面不能用this,所以asyncData使用context接收上下文,下面打印一下上下文看看里面有什么?
- 里面我们找到了params里面有id,route的params里面也有id,这两个是一样的,可以直接使用,下面对页面进行修改
html
<template>
<div>
<!--这里展示数据-->
<h1>{{ article.title }}</h1>
<p>{{ article.body }}</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'ArticlePage',
async asyncData (context) {
const { data } = await axios({
method: 'GET',
url: 'http://localhost:3000/data.json'
})
// 通过上下文拿到id值
const id = Number.parseInt(context.params.id)
// 返回article就是每一个posts的元素
return {
article: data.posts.find(item => item.id === id)
}
}
}
</script>
<style></style>
- 这个时候演示就可以看到,内容发生了变化
案例完成。