Nuxt.js 学习总结

## Nuxt.js 学习笔记

- **Nuxt.js 是什么?**
  - 官方定义: 是它是一个基于 Vue.js 的服务端渲染的应用框架

- **安装 使用**
  - Nuxt.js 十分简单易用。一个简单的项目只需将 nuxt 添加为依赖组件即可
  - 运行 **npx create-nuxt-app 项目名称**
    - Programming language: 程序设计语言
    - Package manager: 包管理器
    - UI framework: UI框架
    - Nuxt.js modules: Nuxt的js模块
    - Linting tools: 代码校验工具
    - Testing framework: 测试框架
    - Rendering mode: 渲染模式
    - Deployment target: 部署目标
    - Development tools: 开发工具


#### 目录结构

- pages: 存放页面
- components: 存放组件
- static: 存放静态资源
- store: Vuex状态树
- nuxt.config.js: 配置文件


#### 服务端生成周期

- **服务器初始化: nuxtServerInit(store, context)**
- 如果在状态树中指定了 nuxtServerInit 方法,Nuxt.js 调用它的时候会将页面的上下文对象作为第 2 个参数传给它
  (服务端调用时才会酱紫哟)当我们想将服务端的一些数据传到客户端时,这个方法是灰常好用的
  在这里,改钩子定义在stroe目录下,使用一个index.js文件进行保存

```js
// 可以在这里初始一些内容到store中
export const state = {
  token: ''
}

export const mutations = {
  setToken(state, token) {
    state.token = token
  }
}

export const actions = {
  nuxtServerInit(store, context) {
    store.commit('setToken', 'abc123')
    console.log(store ,'nuxtServerInit')
  }
}
  • 中间件运行: middleware
    • 中间件允许您定义一个自定义函数运行在一个页面或一组页面渲染之前。每一个中间件应放置在 middleware/ 目录
      文件名的名称将成为中间件名称 (middleware/auth.js将成为auth 中间件),这里定义一下这个auth 中间件
    • 全局需要在 nuxt.config.js 中配置 router:
export default ({ store, route, redirect, params, query, req, res }) => {
  // context 服务端上下文
  // 全局守卫业务
  console.log('middleware nuxt.config.js outside')

  // store状态树信息
  // route 一条目标路由信息
  //redirect 路由的强制跳转
  // params query 校验参数的合理性
  // console.log('全局守卫前置守卫')
  // redirect('/login')
}
  • 页面层级的中间件使用/定义
<script>
  export default {
    // middleware: 'auth' // 页面层级中间件定义
    middleware({ store, route, redirect, params, query }) {
      console.log('middleware layouts inside')
       // store状态树信息
       // route 一条目标路由信息
       //redirect 路由的强制跳转
       // params query 校验参数的合理性
       // console.log('layouts守卫前置守卫')
       // redirect('/reg')
    }
  }
</script>

中间件执行顺序: nuxt.config.js(配置文件级别) > layouts(布局文件级别) > pages(页面级别)

  • 校验参数: validate
    • 在动态路由对应的页面组件中配置一个校验方法用于校验动态路由参数的有效性
  • pages/index.vue
<script>
  export default {
    // 同步模式
    validate({ params, query }) {
      return true // 如果参数有效
      return false // 参数无效,Nuxt.js 停止渲染当前页面并显示错误页面
    }
    // 异步模式
    async validate({ params, query, store }) {
      // await operations
      return true // 如果参数有效
      // return false // 将停止Nuxt.js呈现页面并显示错误页面

      // return为true时会继续访问该页面,return为false则会跳转到错误页面。
    }
  }
</script>
  • 异步数据处理: asyncData() & fetch()
    • asyncData() 在服务器端获取并渲染数据。可以在渲染组件之前异步获取数据
    • fetch() 用于在渲染页面前填充应用的状态树(store)数据, 与 asyncData 方法类似,不同的是它不会设置组件的数据
<script>
  export default {
   asyncData({store, params}) {

   }

   fetch(store, params) {

   }
  }
</script>

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

  • beforeCreate与 created异步数据
    • 这两个方法在客户端或服务端都会被执行
<script>
  export default {
    beforeCreate() {
			console.log("beforeCreate")
		},
		created() {
			console.log("created")
		}
  }
</script>

Nuxt路由

  • 约定式路由

    • Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置
    • 要在页面之间使用路由, Nuxt.js 建议我们使用 <nuxt-link> 标签
    • 在约定式路由中,会根据 page 目录下的层级关系自动生成构建出路由,并且使用 index.vue 文件来作为某个目录下的主页面
  • 案例:

    • 这里我们在 page 目录文件下新建 list.vue 文件,来进行路由的测试
<template>
  <div>
    <!-- 
      跳转: 声明式 路由跳转 【此时的路由我们称为是一级路由跳转】
    -->
    <!-- 
      此时携带的参数:
        name: 指向的是路由(文件夹或文件名),而路由死活区分大小写的,所以to后面区分大小写,建议文件夹都写成小写的
        注意:
          使用 query 传递参数会拼接在路由后面显示在地址栏中
          使用 params 纯涤的参数不会拼接在路由后面,地址栏上看不到参数
     -->
    <nuxt-link :to="{name: 'list', query: {id: 1}, params: {id: 1}}">跳转列表页面</nuxt-link>

    <!-- 编程式路由导航 -->
    <button @click="toGoods">跳转到商品页</button>

    <!-- 展示区: 类似于 router-view -->
    <Nuxt />
  </div>
</template>

<script>
export default {
  methods: {
    toGoods() {
      /* 编程式路由导航, 携带 query 参数 */
      this.$router.push({
        name: 'goods',
        query: {id: 1}
      })
    }
  }
}
</script>

<style scoped>

</style>
  • 在 list.vue 页面中写以下的代码

路由校验
Nuxt.js可以让你在动态路由对应的页面组件中配置一个 validate 方法
用于校验动态路由参数的有效性,该函数有一个布尔类型的返回值:
如果返回true则表示校验通过,如果返回false则表示校验未通过

<template>
  <div class="list">
    <h3>列表页面</h3>
  </div>
</template>

<script>
export default {
  created() {
    console.log(this.$route)
  },
  /* 
    路由校验:
      nuxt中使用 validate 方法进行路由参数校验,这个方法必须返回一个布尔值 
      为true表示校验通过,为false表示校验失败
    注意 validate不能写到 methods属性中
  */
   validate({params}) {
    // console.log(obj);
    // return true
    return /^\d+$/.test(params.id)
  }
}
</script>

<style>

</style>
  • 动态路由

    • 在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录
  • 嵌套路由

    • 添加一个Vue文件,作为父组件
    • 添加一个与父组件同名的文件夹来存放子视图组件
    • 在父文件中,添加组件,用于展示匹配到的子视图
  • 路由守卫

    • 前置路由守卫: 【依赖于中间件: middleware 或者插件】
    • 后置路由守卫: 【使用 Vue 的 beforeRouteleave 钩子函数】
  /* 
    middleware
      需要在 nuxt.config.js 中配置 router: { middleware: 'auth' }
      然后根目录新建 middleware 文件夹---> auth.js
    
    这种的我们称之为是中间件的路由守卫, 我们可以获取到 { store, route, redirect, params, query, req, res }
      这些个对象属性,当我们需要进行数据拦截之后可以通过 redirect 对页面进行强制跳转
      例如: 一个登录系统, 在没有登录之前是无法访问的,那么这时 就可以进行将其拦截, 给其重定向到登录页
  */
 export default ({ store, route, redirect, params, query, req, res }) => {
  // context 服务端上下文
  // 全局守卫业务
  
  // store状态树信息
  // route 一条目标路由信息
  //redirect 路由的强制跳转
  // params query 校验参数的合理性
  // console.log('全局守卫前置守卫')
  // redirect('/login')
}
  /* 
    插件 
      插件的路由守卫,这里我们需要先定义一个插件,在plugins目录下新建一个route.js文件作为一个插件
      之后将该插件在nuxt.config.js文件当中进行配置
    
    在插件当中我们可以获取到app对象,再通过app对象的beforeEach和afterEach进行设置前置和后置守卫
    分别对应的是进入页面于离开页面要执行的方法和操作
  */
 /* 插件全局守卫 */
export default ({ app, redirect, params, query, store }) => {
  console.log('插件')
  // app == vue实例
  // redirect 跳转函数
  app.router.beforeEach((to, from, next) => {
    // 全局前置守卫 -- 插件
    // console.log('插件配置,', to)
    // if (to.name === 'login' || to.name === 'reg') {
    //   next()
    // } else {
    //   redirect({ name: 'login' })
    // }
    next()
  })

  app.router.afterEach((to, from) => {
    console.log('插件全局后置守卫')
  })
}
  /* nuxt.config.js */
  plugins: [
    '~/plugins/router',
  ],
  <!-- 
  而对于页面进行路由守卫的配置,我们同样的还是采用钩子进行定义
    使用 beforeRouteLeave钩子当离开当前路由触发该方法(路由守卫--组件后置)
  -->
  <script>
  export default {
    beforeRouteLeave(to,from,next){
      next(window.confirm("是否离开"))
    }
  }
</script>
  • router.js 导航守卫
    • 下载 @nuxtjs/router 这个插件: npm install @nuxtjs/router -S
    • 下载ok以后在 next.config.js 的 modules 配置项中进行配置
      modules: [
      '@nuxtjs/router'
      ]
    • 把 router 文件放入 nuxt项目根目录 【router.js】-- 注意: 使用此插件不支持路由懒加载
    • 修改配置返回的内容
      export function createRouter() {
      return new VueRouter({
      mode: 'history',
      routes
      })
      }

Nux 配置

  • nuxt.config.js 配置 head
  <!-- 组件内部 -->
<script>
  export default {
    head() {
      return {
        title: '标题',
        meta: [
          { hid: 'description', name: 'description', content: '此处是网站描述' },
          { name: 'keywords', name: 'keywords' content: '此处是网站关键词' }
        ]
      }
    }
  }
</script>
  • nuxt.config.js 配置 css

在开发多页项目时,都会定义一个全局的 CSS来初始化我们的页面渲染
网上也有非常出名的开源css文件normailze.css
需要在 nuxt.config.js里进行操作

  css:['~assets/css/normailze.css'],
  • 设置好后,在终端输入npm run dev 然后你会发现样式以及起效了

  • nuxt.config.js 配置 plugins

    • 此配置就是用于配置全局的插件:例如 ElementUI

数据交互

  • Nuxt.js 扩展了 Vue.js,增加了一个叫 asyncData 的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据

  • asyncData方法会在组件(限于页面组件)每次加载之前被调用

    • 它可以在服务端或路由更新之前被调用。
    • 在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象
    • 可以利用 asyncData方法来获取数据,Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件
    • 注意: asyncData方法是在组件初始化前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象
  • 既然要进行数据交互,那么必然少不了axios来进行发送请求,同时我们将nuxt的代理的依赖加入进来

    • npm i @nuxtjs/axios @nuxtjs/proxy --save
  • 可以将axios直接丢到 nuxt.config.js 中的 modules 配置中,后续我们就可以直接再项目当中通过$axios进行获取axios对象
    modules: [
    '@nuxtjs/axios'
    ],

  • 在前面我们有说过nuxt的目录,其中static是用来存放相对应的静态文件的,这里我们在static下加上一个data/data.json文件用来模拟axios返回的数据

  {
    "title": "next数据"
  }
  • 然后我们可以在首页中进行数据的获取
<template>
  <div>
    <h2>首页</h2>
    <span>{{title}}</span>
  </div>
</template>

<script>
export default {
  /*  
    这里直接使用异步的方式来进行对asyncData方法进行测试,获取前面加入的json文件的数据
      并且可以直接进行return返回给到客户端直接渲染
    
    这里是获取同域的资源数据,但是当我们发送一个请求访问的是跨域的资源的时候,可以看到会报一个跨域的错误
  */
  async asyncData({ $axios }) {
    let res = await $axios({ url: '/data/list.json' })
    console.log('读取到了静态资源',res.data)
    return {
      title: res.data.title
    }
  }
}
</script>
  • 解决跨域问题
    • 这个时候我们需要在配置文件当中加上允许跨域和代理的配置
  axios: {
    proxy: true,
  },

  proxy: {
    '/api': {
      target: "http://localhost:8888",
      chageOrigin: true,
      pathRewrite: {}
    }
  },
  • 拦截配置与携带token
    • 我们可以对axios异步请求进行请求、响应、error拦截。这里使用一个插件来对axios进行统一管理
    • 添加一个axios.js作为一个插件,将插件加入到配置文件当中
  	plugins: [
		{
			src: '~/plugins/axios',
			ssr: true // 开启服务端渲染
		}
	],
  • 这里在axios.js文件当中获取$axios对象,对其请求(onRequest)、响应(onResponse)、异常(onError)方法进行拦截处理
 export default function({ $axios, redirect, route, store, app:{$cookies} }) {
  // 基本配置
  $axios.defaults.timeout = 6000

  // 请求拦截
  $axios.onRequest(config => {
		console.log("请求拦截")
		config.headers.token = state.user.token
		return config
	})
  // 响应拦截
  $axios.onResponse(res => {
		console.log("响应拦截")
		if (res.data.err === 2 && route.fullPath !== '/login') {
      redirect('/login?path=' + route.fullPath)
    }
    return res
	})

  // 错误拦截
	$axios.onError(error => {
		console.log("请求异常")
		return error
	})
 } 

自定义loadding

  • nuxt支持自定义的loading加载动效。在配置文件当中我们可以使用nuxt自带的简单的loadding加载
    • loading: {color: "#339",height: "3px"},
      
  • 同样我们也可以进行自定义的loadding加载,在 nuxt.config.js 配置文件中添加
    • loading:'~/components/loadding.vue'
      
    • 这里对应的loadding自定义加载就简单写了,后续的样式可以导入elementui来进行美化
  <template>
	<div>
		<div v-if="loadding">loading...</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				loadding: true
			}
		},
		methods: {
			start() {
				this.loadding = true
			},
			finish() {
				this.loadding = false
			}
		}
	}
</script>

Vuex

  • 这里的和 你在学习 Vue中的 Vuex 几乎一致
  • 首先我们在store目录下使用 index.js 来作为默认的主store状态树
// 主模块

//state/index.js
export const state = () => ({
  bNav: false,
  bLoading: false
})

//mutations
export const mutations = {
  M_UPDATE_NAV(state, payload) {
    state.bNav = payload
  },
  M_UPDATE_LOADING(state, payload) {
    state.bLoading = payload
  }
}

// actions
export const actions = {
  nuxtServerInit(store, { app: { $cookies } }) {
    // 初始化东西到store里 token信息
    // console.log('nuxtServerInit', store, context)
    console.log('nuxtServerInit')
    let user = $cookies.get('user') ? $cookies.get('user') : {err:2, msg: '未登录', token: ''}
    store.commit('users/M_UPDATE_USER', user)
  }
}

// getters
export const getters = {
  getNav(state) {
    return state.bNav ? '显示' : '隐藏'
  }
}
  // store/home.js
export const state = () => ({
  err: 1,
  data: {}
})

export const mutations = {
  M_UPDATE_HOME(state, payload) {
    state.err = payload.err
    state.data = payload.data
  }
}

export const actions = {
  A_UPDATE_HOME({ commit, state }, payload) {
    // 异步处理
    commit('M_UPDATE_HOME', { err: 0, data: {title: 'home模块 actions所传递的数据'} })
  }
}
  // store/users.js
  export const state = () => ({
  err: 1,
  msg: '未登录',
  token: '',
  data: {}
})

export const mutations = {
  M_UPDATE_USER(user, payload) {
    state.err = payload.err
    state.msg = payload.msg
    state.data = payload.data
    state.token = payload.token
  }
}

export const actions = {
  A_UPDATE_USER({commit, user}, payload) {
    // 异步业务
    commit('M_UPDATE_USER', payload)
  }
}
  • 而在页面当中进行使用直接使用以下代码进行获取数据、而对于其他的相关vuex操作可查看前面关于vuex的文档
    • console.log("state", this.$store.state)

状态持久化与token校验

  • 这里要使用token我们需要先安装该依赖,用来读写cookie到浏览器当中

    • npm i cookie-universal-nuxt --save
  • 安装之后将该依赖加入到配置文件的modules当中
    modules: [
    '@nuxtjs/axios',
    'cookie-universal-nuxt'
    ],

  • 这里我们已登录为例,发送axios请求进行获取数据,将数据再写道cookies当中

  <template>
  <div>
    <button @click="login">login</button>
  </div>
</template>

<script>
  export default {
    methods: {
      login() {
        this.$axios({
          url: '',
          method: 'post',
          data: {
            username: '',
            password: ''
          }
        }).then(res => {
          this.$cookies.set('user', res.data)
          this.$store.commit('', res.data)
        
          if (!this.$route.query.path || /login|reg/.test(this.$route.query.path)) {
            this.$router.replace('/user')
          }else {
            this.$router.replace(this.$router.query.path)
          } 
        })
      }
    }
  }
</script>

整合ElementUI

  • npm i element-ui --save 进行安装

  • 然后将插件通过配置文件进行注册
    plugins: [
    {
    src: '~/plugins/element-ui',
    ssr: true
    }
    ],

  • 并且将css样式也在配置文件导入
    css: [
    'element-ui/lib/theme-chalk/index.css'
    ],

import Vue from 'vue'
/整体引入
import ElementuI from 'element-ui'
vue.use(ElementUI)

//按需引入全局使用
import {Button} from 'element-ui'
vue.use(Button)
  • 然后就可以在所有的pages页面使用elementui 的组件了

mixin混入定义全局方法

  • nuxt.config.js
plugins:[  //配置插件
    '~/plugins/mixins'
]
  • plugins/mixins.js
import Vue from 'vue'
let show = ()=>console.log('全局方法')
Vue.propotype.$show = show  //服务端钩子内部不可以使用,this不会执行vue实例

// 全局锅过滤器
import * as filters from '../assets/js/filter.js'
Object.keys(filters).forEach(key=>Vue.filter(key, filters[key]))
assets/js/filter.js

export function filerzero(n) {
  return n < 10 ? '0'+n : ''+n
}

export const date = time => {
  let d = new Date()
  d.setTime(time)
  let year = d.getFullYear()
  let month = d.getMonth()+1
  let date = d.getDate()
  let hour = d.getHours()
  let min = d.getMinutes()
  let sec = d.getSeconds()
  return `${year}年${filerzero(month)}月${filerzero(date)}日 ${filerzero(hour)}:${min}:${sec}`
}
posted @ 2022-08-08 00:20  小学生学Web前端  阅读(295)  评论(0)    收藏  举报