简陋的nuxt2学习笔记

内容

  • 安装
  • 目录结构
  • 服务端生命周期
    • nuxtServerInit(store, context)
    • 路由中间件 middleware
    • validate
    • asyncData() & fetch()
  • 服务端和客户端共有的生命周期
    • beforeCreate() & created()
  • 客户端生命周期
  • 路由
    • 动态路由
    • 使用router.js
  • 导航守卫
    • router.js
    • nuxt中使用
  • 配置head
  • 配置css
  • 配置plugins
  • 数据交互
  • 配置代理
  • 请求拦截 & vuex持久化存储token & 导航守卫
  • 配置loading
  • Vuex状态树


安装

  1. npx create-nuxt-app Project-name
    (npx在npm版本5.2.0默认安装了)
  2. 自己从头建一个
mkdir 项目名
cd 项目名 

新建package.jsontouch package.json

// package.json
{
  "name": "my-nuxt",
  "scripts": {
    "dev": "nuxt"
  }
}

安装nuxtnpm i nuxt


目录结构

pages:页面,类似于vue-cli项目中的views
nuxt.config.js:全局的配置文件,类似于vue-cli项目中的vue.config.js


服务端生命周期

nuxtServerInit(store, context)

参数1: vuex上下文
参数2:nuxt上下文

nuxt里第一个运行的生命周期
一般在这里设置一些值

// store/index.js
export const actions = {
  nuxtServerInit(store, context) {
    console.log(1)
  }
}

路由中间件 middleware

在渲染页面组件之前调用
类似于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页的

validate

校验动态路由参数
必须return一个truefalse
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() & fetch()

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>

上面都是只在服务端执行的生命周期,按顺序执行完之后,下面执行服务端和客户端共有的生命周期

服务端和客户端共有的生命周期

beforeCreate() & created()


<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'
  }
]

使用router.js

如果不想按nuxt自动生成的路由,而是按我们自己在router/index中的路由配置

  1. 下载@nuxtjs/router
    npm i @nuxtjs/router -S
  2. 配置nuxt.config.jsmodules模块
modules: [
  '@nuxtjs/router'
]
  1. 将router文件放入nuxt项目根目录
    把原vue项目中的router/index.js拖出来,改名叫router.js并扔进nuxt项目根目录
  2. 修改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

如果使用了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中使用

  1. 中间件middleware
    生命周期中介绍过middleware的用法,分全局中的和页面中的
  2. 插件plugins
    全局的(配置在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();
  })
})

配置head

全局:在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


配置css

全局配置在nuxt.config.js

export default {
  css: [
    '~/static/global.css'
  ]
}

如果要使用sass需要安装:
npm i node-sass sass-loader -D
(可能会运行出错,需要更改版本,按照提示修改即可)


配置plugins

渲染页面之前要运行的插件,全局配置在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可以成功拿到数据:
简陋的nuxt2学习笔记_第1张图片
在页面中发请求:

// 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也可以成功拿到数据,不会报跨域
在这里插入图片描述


请求拦截 & vuex持久化存储token & 导航守卫

例:
需求是进入我的页面,若未登录 => 跳转登录,若登录 => 显示我的页面

<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': '',
     }
   }
 },

配置loading

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>

Vuex状态树

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>

将简陋进行到底

你可能感兴趣的:(nuxt,nuxt2)