npx create-nuxt-app Project-name
mkdir 项目名
cd 项目名
新建package.json
:touch package.json
// package.json
{
"name": "my-nuxt",
"scripts": {
"dev": "nuxt"
}
}
安装nuxt
:npm i nuxt
pages
:页面,类似于vue-cli项目中的views
nuxt.config.js
:全局的配置文件,类似于vue-cli项目中的vue.config.js
参数1: vuex上下文
参数2:nuxt上下文
nuxt里第一个运行的生命周期
一般在这里设置一些值
// store/index.js
export const actions = {
nuxtServerInit(store, context) {
console.log(1)
}
}
在渲染页面组件之前调用
类似于vue中的导航守卫
a.全局:
要在全局配置文件nuxt.config.js
中配置
// nuxt.config.js
export default {
router: {
middleware: 'auth'
}
}
新建middleware
目录,新建文件.js
// middleware/auth.js
export default function() {
console.log('auth')
}
b.页面:
要在该页面配置
<script>
export default {
name: "Index",
middleware: "auth",
};
script>
新建middleware
目录,新建文件.js
// middleware/auth.js
export default function({ store, route, redirect, params, query, req, res }) {
console.log('auth')
}
代码只在index.vue
页面生效
或者直接在页面中写成函数形式,无需新建middleware
目录
<script>
export default {
name: "Index",
middleware() {
console.log('auth');
},
};
script>
nuxtServerInit()
和middleware
配合使用:
用户进入index页面,校验有无token,如果没有就跳转到demo页面
// store/index.js
export const state = () => ({
token: ''
})
export const mutations = {
setToken(state, token) {
state.token = token
}
}
export const actions = {
nuxtServerInit(store, context) {
// 实际上要获取到真实的token,这里模拟成空字符串
const token = ''
store.commit('setToken', token)
}
}
使用全局的路由中间件:
// nuxt.config.js
export default {
router: {
middleware: 'auth'
}
}
// middleware/auth.js
export default function({ store, redirect }) {
let { token } = store.state
if(!token) {
// 如果没有token,重定向到demo页
redirect('/demo')
}
}
这个例子里拿到的token就是空字符串,那么它是一定会被重定向到demo页的
校验动态路由参数
必须return
一个true
或false
true
时正常打开,false
时会跳转404
假如要通过路由传递id参数,并且规定id是整数,现在访问http://localhost:3000?id=fsdfs
这个参数就是错误的
我们可以通过validate()
检测到有错时直接跳转404
<script>
export default {
name: "Index",
toDemo() {
this.$router.push({
path: "/demo",
query: {
id: "abcd",
},
});
},
};
script>
<script>
export default {
name: "Demo",
validate({ params, query }) {
console.log(query)
// 校验id参数是否为整数
return /^\d+$/.test(query.id)
},
};
script>
传的id不合法,由index页 => demo页的时候,就会直接跳转404页面
asyncData()
只能在页面中用(pages)
会在页面每次加载之前被调用
用来请求数据
<script>
export default {
name: "Demo",
asyncData({ store, params }) {
console.log("asyncdata");
},
};
script>
fetch()
可以在页面中使用也可以在组件中使用
<script>
export default {
name: "Demo",
fetch({ app, store, params }) {
console.log("fetch");
},
};
script>
上面都是只在服务端执行的生命周期,按顺序执行完之后,下面执行服务端和客户端共有的生命周期
<script>
export default {
name: "Demo",
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
};
script>
beforeMount()
mounted()
beforeUpdate()
updated()
beforeDestroy()
destroyed()
无需配置路由(router.js)
nuxt会根据pages
目录下的vue文件自动生成vue-router
配置
在页面之间使用路由,官方推荐使用
,类似于vue中的
假如pages的目录结构:
pages/
--| user/
-----| index.vue
-----| a.vue
--| index.vue
自动生成的路由:
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-a',
path: '/user/a',
component: 'pages/user/a.vue'
}
]
如果要定义带参数的动态路由,就创建以下划线为前缀
的文件/目录
假如pages的目录结构:
pages/
--| _user/
-----| index.vue
-----| a.vue
--| admin/
_____| _b.vue
--| index.vue
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'admin-b',
path: '/admin/:b',
component: 'pages/admin/_b.vue'
},
{
name: 'user',
path: '/:user',
component: 'pages/_user/index.vue'
},
{
name: 'user-a',
path: '/:user/a',
component: 'pages/_user/a.vue'
}
]
如果不想按nuxt自动生成的路由,而是按我们自己在router/index
中的路由配置
@nuxtjs/router
npm i @nuxtjs/router -S
nuxt.config.js
的modules
模块modules: [
'@nuxtjs/router'
]
router/index.js
拖出来,改名叫router.js
并扔进nuxt项目根目录router.js
内容createRouter()
函数原:
const Demo = () => import("../views/Demo.vue");
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/demo',
component: Demo,
}
]
})
export default router;
现在:
import Demo from '@/pages/demo.vue';
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/demo',
component: Demo,
}
]
})
export function createRouter() {
return router
}
如果使用了router.js
,那么导航守卫的用法就跟以前在vue-cli中一样
import Demo from '@/pages/demo.vue';
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/demo',
component: Demo,
}
]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
console.log(to, from, next);
next();
})
export function createRouter() {
return router
}
nuxt.config.js
中的都是全局的)nuxt.config.js
中进行配置plugins: [
'~/plugins/router.js'
]
再在根目录下新建plugins/router.js
export default ({app} => {
app.router.beforeEach((to, from, next) => {
console.log(to, from, next);
next();
})
})
全局:在nuxt.config.js
中配置
所有页面都共用这个head
export default {
head: {
title: 'xxx',
meta: [
{
hid: 'description',
name: 'description',
content: 'xxx网站描述'
},
{
hid: 'keywords',
name: 'keywords',
content: 'xxx网站关键词'
}
]
},
}
局部:在单个页面中配置
这里的head
是函数
<script>
export default {
name: "demo",
head() {
return {
title: "xxxx",
meta: [
{
hid: "description",
name: "description",
content: "xxx网站描述",
},
{
hid: "keywords",
name: "keywords",
content: "xxx网站关键词",
},
],
};
},
};
script>
hid
键为meta
标签配一个唯一的标识编号,为了避免子组件中的meta
标签不能覆盖父组件中相同的标签而产生重复的现象
也可以设置成动态的,比如说文章的详情页title都不一样,就可以通过传文章列表页传id给详情页,然后详情页通过id请求到具体的title再设置给head
全局配置在nuxt.config.js
中
export default {
css: [
'~/static/global.css'
]
}
如果要使用sass需要安装:
npm i node-sass sass-loader -D
(可能会运行出错,需要更改版本,按照提示修改即可)
渲染页面之前要运行的插件,全局配置在nuxt.config.js
中
我们自己封装的一些插件(.js文件)或者第三方的插件就可以在这引入
举个例子,我们现在要引入element ui
官网:https://element.eleme.cn/#/zh-CN/component/quickstart
安装:npm i element-ui -S
引入css:
(在vue-cli的写法是在main.js
中import)
// nuxt.config.js
css: [
'element-ui/lib/theme-chalk/index.css'
]
注册全局组件的代码就要新建一个文件来放
在plugins
目录下创建一个element.js
// plugins/element.js
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.use(ElementUI);
在nuxt.config.js
中全局配置
// nuxt.config.js
plugins: [
'~/plugins/element.js'
]
安装axios
npm i @nuxtjs/axios -S
nuxt.config.js
进行配置
modules: [
'@nuxtjs/axios'
]
使用asyncData()
方法:
只能在pages
中用,不能在components
中用
这里的$axios
只有安装并配置好了axios
才能用
asyncData()
是在组件初始化前被调用的,无法使用this
要在模板中用的数据就放在return
出去
<template>
<div>
<ul>
<li v-for="(item, index) in list">{{ item }}li>
ul>
div>
template>
<script>
export default {
name: "Index",
async asyncData({ $axios }) {
let { data } = await $axios.get(url);
return {
list: data
}
}
};
script>
使用fetch()
:
fetch()
在asyncData()
之后执行
在components
中用
<template>
<div>
<ul>
<li v-for="(item, index) in list">{{ item }}li>
ul>
div>
template>
<script>
export default {
data() {
return {
list: [],
};
},
async fetch() {
let { data } = await this.$axios.get(url);
this.list = data;
},
};
script>
⚠️:如果报错说组件未引入,要在nuxt.config.js
中配置components: true
axios.nuxtjs官网
安装:
npm i @nuxtjs/axios @nuxtjs/proxy -S
在nuxt.config.js
中配置:
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
axios: {
proxy: true, // 开启代理
baseURL:
},
proxy: {
'/api': { // 匹配所有以/api开头的请求路径
target: url, // 要代理的地址
pathRewrite: { // 将代理请求中中的/api删去
'^/api': '',
}
}
}
测试:
使用express写一个简易接口
(确保安装了express和node)
// index.js
const express = require('express')
const app = express()
app.get('/list', (req,res) => {
res.send({
code: 200,
msg: 'popopo'
})
})
app.listen(8888, () => {
console.log('index.js is running');
})
在本地访问localhost:8888/list
可以成功拿到数据:
在页面中发请求:
// index.vue
export default {
async asyncData({ $axios }) {
// 根据上面的配置,这里记得➕上/api
let res = await $axios.get("http://localhost:3000/api/list");
console.log(res.data);
},
}
在本地访问localhost:3000
打开index.vue
也可以成功拿到数据,不会报跨域
例:
需求是进入我的页面,若未登录 => 跳转登录,若登录 => 显示我的页面
<template>
<div>
<nuxt-link to="/login">登录nuxt-link>
<nuxt-link to="/mine">我的nuxt-link>
div>
template>
在nuxt.config.js
中配置:
plugins: [
'~/plugins/axios.js'
]
在plugins
目录下新建axios.js
文件
注意拦截器写法和在vue中不同
export default ({ $axios, store }) => {
// 请求拦截
$axios.onRequest(config => {
// 读store中的token,加在请求头中
const { token } = store.state;
config.headers['Authorization'] = token;
})
// 响应拦截
$axios.onResponse(response => {
return response.data;
})
// 错误处理
$axios.onError(err => {
//...
})
}
为了实现vuex中数据持久化,我们应该要把后端响应的token存在本地,但是在nuxt的store
中无法使用localStorage
我们需要使用插件cookie-universal-nuxt
安装:
npm i cookie-universal-nuxt -S
nuxt.config.js
中配置:
modules: [
'cookie-universal-nuxt'
],
注意store/index.js和vue中的写法不同
// store/index.js
export const state = () => ({
token: ''
})
export const mutations = {
setToken(state, token) {
state.token = token;
// cookie-universal-nuxt的用法
this.$cookies.set('token', token)
},
getToken(state) {
state.token = this.$cookies.get('token') || ''
}
}
在mine.vue
页设置导航守卫:
export default {
middleware: "auth"
}
新建middleware/auth.js
:
export default function({ store, redirect }) {
// 进入mine.vue先取token,若没有则跳转登录
store.commit('getToken')
if(!store.state.token) {
redirect('/login')
}
}
在login.vue
页发请求登录:
<script>
import { mapMutations } from "vuex";
export default {
name: "Login",
data() {
return {
username: "",
password: "",
};
},
methods: {
...mapMutations(["setToken"]),
async login() {
const data = await this.$axios.post(
"api/login",{
username: this.username,
password: this.password,
}
);
// 登录后设置token
this.setToken(data.token);
},
},
};
script>
若后端未对跨域进行配置,我们还需要配置代理来解决跨域问题:
// nuxt.config.js
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy',
'cookie-universal-nuxt'
],
axios: {
// 是否可以跨域
proxy: true
},
proxy: {
'/api': {
target: 'xxxxxxx',
pathRewrite: {
'^/api': '',
}
}
},
nuxt有默认的加载进度条,可以禁用/自定义
// nuxt.config.js
export default {
// 禁用
loading: false,
}
export default {
// 自定义样式
loading: {
color: 'blue',
height: '5px'
}
}
或者还可以完全自定义自己的加载进度条:
在components
目录下新建LoadingBar.vue
在nuxt.config.js
中给出进度条的路径:
// nuxt.config.js
export default {
loading: '~/components/LoadingBar.vue',
}
官网给出的例子:
start()
和finish()
方法是规定必须写的
start()
:路由更新时调用
finish()
:路由更新完毕或asyncData()
调用完成时调用
<template>
<div v-if="loading" class="loading-page">
<p>Loading...p>
div>
template>
<script>
export default {
data: () => ({
loading: false,
}),
methods: {
start() {
this.loading = true;
},
finish() {
this.loading = false;
},
},
};
script>
<style scoped>
.loading-page {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
text-align: center;
padding-top: 200px;
font-size: 30px;
}
style>
在store
目录下使用vuex
即可,无需安装vuex
推荐使用store
的方式:模块方式
store
目录下的每个.js
文件都被转换为状态树指定命名的子模块(index.js
)是根模块
例如创建一个store/user.js
export const state = () => ({
userInfo: {}
})
export const mutations = {
setInfo(state, info) {
state.userInfo = info
},
}
在页面中使用user
模块:
<template>
<div>
{{ userInfo }}
<button @click="set({ name: 'poem' })">button>
div>
template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
computed: mapState({
userInfo: state => state.user.userInfo
}),
methods: {
set(value) {
// 可以commit 模块/方法名
this.$store.commit('user/setInfo', value)
},
// 也可以这样用
...mapMutations({
set: 'user/setInfo'
})
}
}
script>
如果是使用原本vue-cli中的store/index.js
,需要进行一些修改:
// 原来
const store = new Vuex.store({
// ...
})
// 现在
const store = () => new Vuex.store({
// ...
})
export default store;
需要分模块的话,可以在store
目录下建modules
目录,例如store/modules/user.js
// store/modules/user.js
export default {
state: () => ({
userInfo: {
name: 'poem'
}
})
}
然后导入
import user from './modules/user.js';
const store = () => new Vuex.store({
modules: {
user
}
})
使用方法和是一样的
<template>
<div>
{{ userInfo }}
div>
template>
<script>
import { mapState } from 'vuex'
export default {
computed: mapState({
userInfo: state => state.user.userInfo
}),
}
script>
将简陋进行到底