优点:
缺点:
建议:如果注重SEO的新闻站点,非强交互的页面,建议用SSR;像后台管理页面这类强交互的应用,建议使用前端渲染。
引用官网官网的一句话:Nuxt.js是一个基于Vue.js的通用应用框架.
安装
//安装包-全局安装(已装的省略这步)
yarn add create-nuxt-app -globel
//创建项目
npm init nuxt-app 项目名
//运行项目
npm run dev
└─my-nuxt-demo
├─.nuxt // Nuxt自动生成,临时的用于编辑的文件,build
├─assets // 用于组织未编译的静态资源如LESS、SASS或JavaScript,对于不需要通过 Webpack 处理的静态资源文件,可以放置在 static 目录中
├─components // 用于自己编写的Vue组件,比如日历组件、分页组件
├─layouts // 布局目录,用于组织应用的布局组件,不可更改⭐
├─middleware // 用于存放中间件
├─node_modules
├─pages // 用于组织应用的路由及视图,Nuxt.js根据该目录结构自动生成对应的路由配置,文件名不可更改⭐
├─plugins // 用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
├─static // 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。文件夹名不可更改。⭐
└─store // 用于组织应用的Vuex 状态管理。文件夹名不可更改。⭐
├─.editorconfig // 开发工具格式配置
├─.eslintrc.js // ESLint的配置文件,用于检查代码格式
├─.gitignore // 配置git忽略文件
├─nuxt.config.js // 用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。文件名不可更改。⭐
├─package-lock.json // npm自动生成,用于帮助package的统一设置的,yarn也有相同的操作
├─package.json // npm 包管理配置文件
├─README.md
重点关注下面几个文件夹
修改pages/index.vue文件,增加hello world ,效果如下:
在pages文件夹里写一个测试页面,发现在浏览器地址里加文件名成功访问到了这个页面
同样加上文件夹也可以匹配,推荐文件夹这种写法,写文件名不是不可以,文件名容易出错.
如果文件名起名为index.vue,它默认会打开这个index.vue页面.
pages文件夹路由总结:
Nuxt.js会根据pages目录结构会自动生成对应的路由配置.
内部路由实现的原理(其实就是原始的vue路由):
假设pages文件夹目录如下:
└─pages
├─index.vue
└─user
├─index.vue
├─one.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-link to="/student"></nuxt-link>
api跳转:
this.$router.push('/student')
$route.params.id
$route.params.id这里的id对应一个加了下划线的文件夹名,也就是假设你把id换成了qita,那你的文件夹也要起名为_qita即可.
上面的路由参数传递方式是没有做类型限制的你把item.id改为item.name也可以传递字符串,那我们怎么做参数类型限制呢?
Nuxt.js 可以让你在动态路由对应的页面组件中配置一个validate方法用于校验动态路由参数的有效性。该函数有一个布尔类型的返回值,如果返回true则表示校验通过,如果返回false则表示校验未通过。
嵌套路由指的是在当前页面中再显示一个新的页面,相当于父页面还在,只是增加了一个子页面
第一步准备好父组件teacher.vue
:
第二步:同时必须创建一个相同类名的teacher文件夹,在这个teacher文件夹下面也要创建以斜杠命名的文件夹_id,在_id文件夹里创建子页面index.vue.
第三步:回到父页面teacher.vue,用nuxt-child标签写显示出口即可实现.
实现效果:
比如有这么一个需求:studdent和teacher页面都希望有一个头部导航组件,那在nuxt.js中怎么来实现呢?
第一步:
在layouts文件夹里,创建自己的公共头部导航组件,在这个头部导航里同时准备一个nuxt组件作为坑(其它页面引用的页面都会丢在这个坑里,这个坑很重要)
引用的Mynav组件内容(组件内容一般放在components文件夹里)
第二步:在需要导航的组件里添加layout属性,值就是自己在layout文件夹里定义的文件名即可
实现效果:
其实特别简单,你只需要在layout文件夹里新建一个error.vue页面即可
输入错误的网站就自动会出来
在assets文件里创建样式文件(这个跟我们平时一样)
引入进行使用(这一步比较特殊):
找到nuxt.config.js文件,在css属性数组里填入自己的路径地址即可
这样全局样式就生效啦.
其实很简单,三步走
第一步:安装
npm install element-ui -S
第二步:在plugins文件夹下面,创建ElementUI.js文件(创建地方比较特殊)
import Vue from 'vue'
import ElementUI from 'element-ui'
Vue.use(ElementUI)
第三步:在nuxt.config.js中添加配置
css: [
'element-ui/lib/theme-chalk/index.css'
],
plugins: [
//ssr:true表示这个插件只在服务器端起作用
{src: '~/plugins/ElementUI', ssr: true }
],
build: {
//防止elementUI多次打包
vendor: ['element-ui']
}
修改配置后重启就可以使用.
注意:在nuxt.js中只有created和beforeCreated这两个生命周期函数才能在服务器端进行正常使用.
下面模拟异步获取数据
秒后正常加载出来数据,但这样是有问题的,因为前端也会执行一次,我们不希望前端执行,所以很重要一点在nuxt中发异步请求不能写在created生命周期里,正确做法是写在asyncData方法里.
正确做法:
同理模拟一个promise异步,也能获取异步数据
总结:Nuxt.js 扩展了 Vue.js,增加了一个叫 asyncData 的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据。asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。所以需要注意这个函数中不能使用this
注意:常规写法如果在created钩子中写异步,是在客户端渲染的而不是在服务端.而我们的是服务器端.
使用方法:
asyncData(context, callback) {callback(null, data)}
其中,
context.route.params.xxx
可以获取参数
1. 下载json-server工具:npm i json-server -g
2. 开启服务: json-server --watch db.json --port 3301
然后准备好数据之后,在浏览器里输入对应的属性名即可访问到对应数据,非常方便
使用axios步骤:
1.安装npm install --save axios
2.使用:
import axios from 'axios'
asyncData(context, callback) {
axios.get('http://localhost:3301/in_theaters')
.then(res => {
console.log(res);
callback(null, {list: res.data})
})
}
module.exports = {
build: {
vendor: ['axios']
}
}
多个这样配置
优化axios:设置基地址和超时时间
优化后效果也是一样的.
新建目录
准备layout
使用layout
写MovieHeader公共组件
在layout/movie.vue使用上面的组件
优化样式后效果:(采用的是固定定位+flex布局)
新建movie/_type/index.vue文件,里面写如下代码
<template>
<div class="movie-type">
<!-- $route.params.type:是vue的方式获取路由参数 -->
<nuxt-link class="movie-box" :to="`/movie/${$route.params.type}/${item.id}`" v-for="item in movieList" :key="item.id">
<img :src="item.img" :alt="item.title">
<h4>电影名:{{item.title}}</h4>
<p>上映评分:{{item.rating===0?'暂无评分':item.rating}}</p>
</nuxt-link>
</div>
</template>
<script>
import axios from '~/plugins/axios'
export default {
data() {
return {
movieList: []
}
},
layout: 'movie',
asyncData(context, callback) {
//获取动态路由参数
let type = context.route.params.type
// console.log(type);//得到路由参数
axios.get(`/${type}`).then(res => {
// console.log(res);
callback(null, {
movieList: res.data
})
})
}
}
</script>
<style scoped>
.movie-type {
display: flex;
flex-direction: column;
align-items: center;
}
.movie-box{
display: flex;
flex-direction: column;
align-items: center;
margin: 20px 0;
padding: 10px 0;
width:20%;
box-shadow: 0 0 10px #bbb;
color: #000;
}
.movie-box:hover{
box-shadow: rgba(0,0,0,.3) 0 19px 60px;
}
</style>
新建movie/_type/_id/index.vue文件,里面写如下代码
<template>
<div class="movie-detail">
<div class="detail-wrapper">
<img
:src="movieDetail.img"
:alt="movieDetail.title"
>
<h1>{{movieDetail.title}}</h1>
<p>上映时间: {{movieDetail.details[0].year}}</p>
<p>电影类型: {{movieDetail.genres.join(',')}}</p>
<p>电影简介:{{movieDetail.details[0].summary}}</p>
</div>
</div>
</template>
<script>
import axios from '~/plugins/axios'
export default {
data(){
return {
movieDetail:{}
}
},
layout:'movie',
asyncData(context,callback){
axios.get(`/${context.route.params.type}/${context.route.params.id}?_embed=details`)
.then(res=>{
console.log(res);
callback(null,{
movieDetail:res.data
})
})
}
}
</script>
<style scoped>
.movie-detail{
width: 20%;
margin: 0 auto;
box-sizing: border-box;
box-shadow: 0 0 10px #bbb;
}
.detail-wrapper{
text-align: center;
}
</style>
设置全局seo
在nuxt.config.js里的head进行设置即可:
局部设置单独页面seo的方法:
效果:
最终完整效果:
最后附上源码克隆地址:
github.com:huanggengzhong/SSR.git