# Nuxt基础知识
Nuxt是一个Vue版本的SSR服务端渲染框架,一般SPA单页面应用在写门户网站、博客、新闻等网站时SEO都不是很友好,不利于搜索引擎优化,以前可以使用预渲染插件进行SEO优化,但效果不是很好,而Nuxt就可以很好的帮我们解决这个问题,不仅能使用Vue单页面进行开发,打包还能生成对应的多页面,使网页源码呈现出来。
项目构建模板:nuxt-app模板 (opens new window)
# 一、构建项目
Nuxt为我们提供了两种模式,一种是SPA单页面纯静态模式,另一种是Universal服务端渲染模式,我们使用Nuxt就是为了服务端渲染,所以就不需要使用SPA这种模式了。在使用Universal服务端渲染模式时,有两种打包方式供我们选择static和server,static是静态预渲染,此方式打包好后就是一个预渲染好的纯静态页面,不需要启动后端服务,直接部署即可,和Vue的预渲染插件同等效果,我们如果只是开发简单官网和一些新闻页使用此模式就满足了。server打包方式就是偏向服务端渲染,打包后是包含后端服务文件的,主要是Node.js,部署时需要启动服务才行,此方式是真正的服务端渲染。
npx create-nuxt-app <项目名>
执行后,根据提示进行选择,渲染模式Rendering mode选择Universal (SSR / SSG),打包方式Deployment target选择Static,创建好后 npm run dev 运行项目就行了。
? Project name: nuxt-app
? Programming language: JavaScript
? Package manager: Npm
? UI framework: Ant Design Vue
? Template engine: HTML
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Continuous integration: None
? Version control system: None
# 二、使用方式
Nuxt的代码基础书写方式还是和Vue一样,但是有的为了服务端渲染使用方式还是有所区别。
# 1、声明周期
Vue 组件的生命周期都可以使用, 但只有 beforeCreate 和 created 这两个方法会在 客户端和服务端被调用。其他生命周期函数仅在客户端被调用。所以获取数据一般要在beforeCreate 和 created中获取。
# 2、路由
获取当前路由和路由元和Vue一样,nuxt没有路由配置,路由是根据pages里创建的文件夹和文件自动生成的。
<template>
<nuxt-link to="/">首页</nuxt-link>
</template>
<script>
export default {
created() {
console.log(this.$route)
console.log(this.$route.params.id)
console.log(this.$router)
}
methods: {
// 动态路由
gotoPage(id) {
this.$router.push({
path: `/news/${id}`
})
}
}
}
</script>
nuxt路由使用方式:
<nuxt-link to=/">首页</nuxt-link> | <router-link to="/">首页</router-link> |
---|---|
<nuxt /> | <router-view></router-view> |
<nuxt-child> | 显示嵌套路由页面内容 |
# 3、less/sass引入
安装时一定要注意版本,默认安装的版本都会过高,需要降版处理。
npm i less less-loader -D
npm install node-sass sass-loader -D
Nuxt没有main.js文件,全局css或者插件的样式都要在nuxt.config的css配置进行引入。
export default {
target: 'static',
// 引入全局CSS
css: [
// 'ant-design-vue/dist/antd.css',
'@/assets/less/main.less',
'@/assets/less/theme.less'
// 'ant-design-vue/dist/antd.less'
],
// less、sass配置
build: {
loaders: {
less: {
lessOptions: { // less-loader 5.x以上才有 lessOptions , 5.x 以下直接配置
javascriptEnabled: true
}
}
}
}
}
全局变量配置: 安装@nuxtjs/style-resources模块,然后引入进行配置。
npm i @nuxtjs/style-resources -D
export default {
target: 'static',
// 引入全局CSS
css: [
// 'ant-design-vue/dist/antd.css',
'@/assets/less/main.less',
'@/assets/less/theme.less'
// 'ant-design-vue/dist/antd.less'
],
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios', // nuxt自带的axios请求模块
'@nuxtjs/proxy',
'@nuxtjs/style-resources'
],
// less变量全局配置
styleResources: {
less: ['@/assets/less/_flex.less', '@/assets/less/_common.less', '@/assets/less/theme.less']
},
// less、sass配置
build: {
loaders: {
less: {
lessOptions: { // less-loader 5.x以上才有 lessOptions , 5.x 以下直接配置
javascriptEnabled: true
}
}
}
}
}
# 4、静态文件使用
static里的文件不会被打包,相当于在根路径下,引入直接使用 / 路径,assets文件会被打包,使用相对路径来引入。
// assets中图片引入
<img src="@/assets/image.png" />
// static中图片引入
<img src="/image.png" />
# 5、引入插件
插件安装后,引入需要在plugins文件下新建对应的js文件,在该文件中和在main.js文件中引入一样引入,然后把该js文件在nuxt.config的plugins中引入即可生效。
1、引入Ant Design Vue
npm i ant-design-vue
在plugins下创建一个js文件,引入Vue和插件,注册插件.
// plugins/antd.js
import Vue from 'vue'
import Antd from 'ant-design-vue/lib'
Vue.use(Antd)
然后把整个文件在配置中引入,其他插件也是如此使用,比在Vue中使用多了一个步骤,基本还是一样。
export default {
target: 'static',
// 插件引入
plugins: ['@/plugins/antd'],
}
2、引入swiper
npm i vue-awesome-swiper@3.1.3
在plugins下创建一个js文件,引入Vue和插件,注册插件.
// plugins/swiper.js
import Vue from 'vue'
import 'swiper/dist/css/swiper.css'
// import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
// swiper有Window对象,在服务端无法渲染
if (process.client) {
const VueAwesomeSwiper = require('vue-awesome-swiper/dist/ssr')
Vue.use(VueAwesomeSwiper)
}
在plugins中引入
export default {
target: 'static',
// 插件引入
plugins: ['@/plugins/swiper'],
}
使用
<template>
<div class="index">
<div class="index-banner">
<div ref="mySwiper" v-swiper:mySwiper="swiperOption">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="@/assets/images/banner01.jpg" alt="">
</div>
<div class="swiper-slide">
<img src="@/assets/images/banner02.jpg" alt="">
</div>
<div class="swiper-slide">
<img src="@/assets/images/banner03.jpg" alt="">
</div>
<div class="swiper-slide">
<img src="@/assets/images/banner04.jpg" alt="">
</div>
</div>
<div class="swiper-pagination"></div>
</div>
</div>
</div>
</template>
<script>
/**
* @desc 新闻页
* @author changz
* */
export default {
name: 'News',
// 当前页面使用的基础布局
layout: 'BasicLayout',
data() {
return {
title: '新闻页',
swiperOption: {
loop: true,
autoplay: {
delay: 3000
},
pagination: {
el: '.swiper-pagination'
},
observer: true,
observeParents: true
}
}
},
computed: {
swiperData() {
return this.$refs.mySwiper.swiper
}
},
methods: {
}
}
</script>
<style lang="less" scoped>
.index {
width: 100%;
.index-banner {
width: 100%;
height: 400px;
background-color: #fff;
.swiper-wrapper {
width: 100%;
height: 100%;
.swiper-slide {
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
}
}
}
</style>
# 6、模块
Nuxt专门提供了模块功能来扩展其功能,Nuxt默认提供了很多模块,如axios请求模块,proxy跨域模块等,也可以到其社区去搜索相关模块https://github.com/topics/nuxt-module (opens new window)。
安装模块:
npm install @nuxtjs/axios
在modules中引入
export default {
// 打包模式 static 静态预渲染 server 服务端渲染
target: 'static',
// nuxt内置模块
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios', // nuxt自带的axios请求模块
'@nuxtjs/proxy'
],
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
// Workaround to avoid enforcing hard-coded localhost:3000: https://github.com/nuxt-community/axios-module/issues/308
// baseURL: '/',
proxy: true
},
// 跨域设置
proxy: {
'/api': {
target: 'https://www.ahss.com.cn', // 目标接口域名
// pathRewrite: {
// '^/api': '/', // 把 /api 替换成 /
// }
}
}
}
# 7、布局layout
项目中页面的公共部分,如头部、底部、或者公共模板,可以像嵌套路由那样进行渲染公共布局页面,和后台管理系统的layout布局一样。
<template>
<div class="basic">
<header class="basic-header">
</header>
<main class="basic-main">
<nuxt />
</main>
<footer class="basic-footer">
</footer>
</div>
</template>
<script>
/**
* @description 公共布局页面
* @author changz
* */
export default {
name: 'BasicLayout',
data() {
return {
pageLoad: false
}
},
created() {
console.log(this.$route)
// this.routeList
}
}
</script>
可通过添加 layouts/BasicLayout.vue 文件来扩展应用的基础布局,通过在布局文件中添加
<template>
<!-- Your template -->
</template>
<script>
export default {
layout: 'BasicLayout'
// page component definitions
}
</script>
# 8、接口数据请求
接口请求可以直接使用axios也可以使用Nuxt自带的的模块,两者都可以使用,如果使用axios实例请求,一定要创建好实例后再调用,否则页面多次刷新会报错。为了防止出现这种报错,我们直接使用Nuxt提供的模块@nuxtjs/axios开请求数据。
数据请求分为两种情况,如果只是请求数据,不对数据做SEO优化,那么和正常的接口请求一样,直接使用就可以了,如果要是想对数据做预渲染则需要把请求放在asyncData方法中,asyncData中不能使用this,也不能直接赋值data的数据,需要通过return返回数据,返回的数据和data一样,可以直接使用。
安装axios,然后在nuxt.config的模块中引入。
npm install @nuxtjs/axios
在nuxt.config中的axios中设置,也可以创建一个插件,在plugins引入设置进行全局请求拦截。
export default {
// 打包模式 static 静态预渲染 server 服务端渲染
target: 'static',
// 引入插件
plugins: [
'@/plugins/axios'
],
// nuxt内置模块
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios', // nuxt自带的axios请求模块
'@nuxtjs/proxy'
],
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
// Workaround to avoid enforcing hard-coded localhost:3000: https://github.com/nuxt-community/axios-module/issues/308
baseURL: '/',
proxy: true
},
// 跨域设置
proxy: {
'/api': {
target: 'https://www.ahss.com.cn', // 目标接口域名
// pathRewrite: {
// '^/api': '/', // 把 /api 替换成 /
// }
}
}
}
全局请求拦截
// plugins/axios.js
/**
* @desc 全局请求拦截
* */
import storage from 'store'
import notification from 'ant-design-vue/lib/notification'
export default function ({ $axios, redirect }) {
$axios.onRequest(config => {
const token = storage.get('ACCESS_TOKEN')
// 如果 token 存在
// 让每个请求携带自定义 token 请根据实际情况自行修改
if (token) {
config.headers['Authorization'] = token
}
})
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 401) {
storage.clearAll()
notification.warning({
message: '提示',
description: '授权验证失败,请重新登录'
})
setTimeout(() => {
// window.location.reload()
redirect('/empower')
}, 1000)
return new Promise(() => {})
} else if (code !== 200) {
notification.warning({
message: '提示',
description: error.message
})
return new Promise(() => {})
}
})
}
在页面中使用
export default {
asyncData ({ $axios, params }) {
return $axios.get(`https://my-api/posts/${params.id}`)
.then((res) => {
return { title: res.data.title }
})
},
created() {
this.getVerifyCode()
},
methods: {
// 获取验证码
getVerifyCode() {
this.codeLoad = true
this.$axios.$get('/api/captchas').then(res => {
this.codeLoad = false
if (res.code !== 200) {
this.$notification.warning({
message: '提示',
description: res.msg
})
return
}
const data = res.data
console.log(data)
}).catch(err => {
this.codeLoad = false
this.$notification.warning({
message: '提示',
description: err.message
})
})
},
}
}
# 9、fetch 方法
fetch 方法用于在渲染页面前填充应用的状态树(store)数据, 与 asyncData 方法类似,不同的是它不会设置组件的数据。
fetch 方法的第一个参数是页面组件的上下文对象 context,我们可以用 fetch 方法来获取数据填充应用的状态树。为了让获取过程可以异步,你需要返回一个 Promise,Nuxt.js 会等这个 promise 完成后再渲染组件。fetch无法在内部使用this获取组件实例,fetch是在组件初始化之前被调用。
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
fetch({ store, params }) {
return axios.get('http://my-api/stars').then(res => {
store.commit('setStars', res.data)
})
}
}
</script>
# 10、跨域设置
安装:
npm install @nuxtjs/proxy
在nuxt.config中配置,如果不是使用自带的@nuxtjs/axios也可以直接配置代理。
export default {
// 打包模式 static 静态预渲染 server 服务端渲染
target: 'static',
// nuxt内置模块
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios', // nuxt自带的axios请求模块
'@nuxtjs/proxy'
],
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
// Workaround to avoid enforcing hard-coded localhost:3000: https://github.com/nuxt-community/axios-module/issues/308
baseURL: '/',
proxy: true
},
// 跨域设置
proxy: {
'/api': {
// target: 'https://www.ahss.com.cn', // 目标接口域名
target: 'http://192.168.100.14:3351', // 目标接口域名
// pathRewrite: {
// '^/api': '/', // 把 /api 替换成 /
// }
}
}
}
# 11、中间件
中间件允许您定义一个函数运行在一个页面或一组页面渲染之前,这样就可以像路径导航守卫一样在页面加载之前进行判断,如权限判断,动态验证是否能访问该页面。
中间件放置在 middleware 目录下。文件名的名称将成为中间件名称 (middleware/auth.js将成为 auth 中间件),创建好后可以在 nuxt.config.js 、 layouts 或者 pages 中使用中间件,在哪里引入就会在它之前执行。
添加权限验证中间件:
// middleware/auth.js
/**
* @desc 权限验证
* 判断哪些页面需要登录,跳转到登录接口
* */
import storage from 'store'
export default function ({ app, route, redirect, store }) {
const whiteList = ['/', '/product', '/news', '/about']
const empowerList = ['/empower']
if (process.client) {
const token = storage.get('ACCESS_TOKEN')
if (token) {
if (!store.state.userInfo) {
store.dispatch('getUserInfo')
}
} else {
// eslint-disable-next-line no-lonely-if
if (!whiteList.includes(route.path) && !empowerList.includes(route.path)) {
redirect('/empower')
}
}
}
}
放在nuxt.config中则所有路由都会执行此方法。
export default {
target: 'static',
// 中间件
router: {
middleware: 'auth'
}
}
添加到指定的布局或者页面 pages/index.vue 者 layouts/default.vue
export default {
middleware: 'auth'
}
# 12、Vuex使用
Nuxt中使用vuex不在需要创建vuex实例了,只需要在store目录下创建对应的js文件就可以自动识别,多个js文件会自动识别为多个模块,不需要引入。使用和正常在vue中使用一样,直接调用或者辅助函数都可以。
/**
* @desc vuex状态管理器
* vuex根模块
* */
export const state = () => ({
userInfo: {},
isLogin: false,
counter: 0
})
export const mutations = {
setUserInfo(state, userInfo) {
state.userInfo = userInfo
},
setIsLogin(state, isLogin) {
state.isLogin = isLogin
},
increment(state) {
state.counter++
}
}
export const actions = {
getUserInfo ({ commit }) {
return new Promise((resolve, reject) => {
this.$axios.get('/api/owner/info')
.then((response) => {
console.log(response)
commit('setUserInfo', response.data.data)
commit('setIsLogin', true)
resolve()
})
.catch((err) => {
reject(err)
})
})
}
}
# 13、设置head
全局设置nuxt.config.js
export default {
// 打包模式 static 静态预渲染 server 服务端渲染
target: 'static',
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'Nuxt预渲染',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'keywords', name: 'keywords', content: 'Nuxt' },
{ hid: 'description', name: 'description', content: '这是一个Nuxt应用程序' }
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
}
}
设置当前页面head
export default {
name: 'Mine',
head () {
return {
title: 'Nuxt预渲染-我的',
meta: [
{ hid: 'nuxtkeywords', name: 'keywords', content: '我的' },
{ hid: 'nuxtdescription', name: 'description', content: '我的' }
]
}
}
}