• nuxtjs中asyncData异步数据请求、代理配置、fetch网络请求、vuex的使用、中间件处理



    1. asyncData异步数据请求

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

    asyncData 方法会在组件(限于页面组件,页面组件就是写在 pages 中的组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据,Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

    注意:由于asyncData方法是在组件初始化前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。

    <template>
      <div>
        <h3>正在热映h3>
        <hr>
        <ul>
          <li v-for="item in films" :key="item.filmId">{{ item.name }}li>
        ul>
        <hr>
        <nuxt-link to="/">首页nuxt-link>
      div>
    template>
    
    <script>
    export default {
      data() {
        return {
          films: []
        }
      },
      // 这个函数在服务端执行,参数为一个上下文件对象
      async asyncData({ $axios }) {
        // 如果在此处你进行网络请求,在没有设置baseUrl或代理时,请一定要写全路径,否则得不到你想要的请求
        // https://api.iynn.cn/film 域名也可以写在 nuxt.config.js 配置文件的 baseUrl 中
        let ret = await $axios.get('https://api.iynn.cn/film/api/v1/getNowPlayingFilmList?cors=T&cityId=110100&pageNum=1&pageSize=10')
        // console.log(ret.data.data.films)
        // 返回一个对象,它就赋值到data配置中
        return ret.data.data
      }
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述

    注意:在上面的案例中,虽然是服务端渲染,但还是需要后端做跨域处理,因为单单刷新当前请求数据的页面(或者是地址栏回车)时,由于是服务端渲染,所以不存在跨域,但是如果时通过路由跳转到当前页面时,就会出现跨域问题。

    2. 代理配置

    假如服务端没有帮我们做跨域处理,我们就需要在 nuxt.config.js 中写代理配置。注意写代理时不能再 baseURL 中写全路径。

      axios: {
        // baseURL: '/',
        proxy: true
      },
    
      proxy: {
        '/api': {
          target: 'https://api.iynn.cn/film',
          changeOrigin: true
        }
      },
    
      server: {
        // 改端口
        port: 8080
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    3. fetch网络请求

    我们现在让子组件来负责数据的展示,而父组件负责数据的请求,使用 axios 该怎么做呢?

    父组件:

    <template>
      <div>
        <h3>正在热映h3>
        <hr>
        <film-item :films="films" />
        <hr>
        <nuxt-link to="/">首页nuxt-link>
      div>
    template>
    
    <script>
    export default {
      data() {
        return {
          films: []
        }
      },
      // 参数为一个上下文件对象
      async asyncData({ $axios }) {
        // 如果在此处你进行网络请求,在没有设置baseUrl或代理时,请一定要写全路径,否则得不到你想要的请求
        let ret = await $axios.get('/api/v1/getNowPlayingFilmList?cityId=110100&pageNum=1&pageSize=10')
        return ret.data.data
      }
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    子组件:

    <template>
      <div>
        <h3>子组件h3>
        <ul>
          <li v-for="item in films" :key="item.filmId">{{ item.name }}li>
        ul>
      div>
    template>
    
    <script>
    export default {
      props: ['films']
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    我们在第一小节中就知道,asyncData 方法只能在页面组件中使用,而子组件在 components 中,假如我们现在要将网络请求写在子组件中,该怎么做呢?这时候就需要用到 fetch方法。

    父组件:

    <template>
      <div>
        <h3>正在热映h3>
        <hr>
        <film-item />
        <hr>
        <nuxt-link to="/">首页nuxt-link>
      div>
    template>
    
    <script>
    export default {
        
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    子组件:

    <template>
      <div>
        <h3>子组件h3>
        <ul>
          <li v-for="item in films" :key="item.filmId">{{ item.name }}li>
        ul>
      div>
    template>
    
    <script>
    export default {
      data() {
        return {
          films: []
        }
      },
      // fetch它可以运行在任何的组件中
      async fetch() {
        let ret = await this.$axios.get('/api/v1/getNowPlayingFilmList?cors=T&cityId=110100&pageNum=1&pageSize=10')
        this.films = ret.data.data.films
      }
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    4. vuex

    Nuxt.js 会尝试找到 src 目录(默认是应用根目录)下的 store 目录,如果该目录存在,它将做以下的事情:

    1. 引用 vuex 模块
    2. 将 vuex 模块 加到 vendors 构建配置中去
    3. 设置 Vue 根实例的 store 配置项

    4.1 state中的数据展示

    store/count.js:

    // 以独立的函数方法来定义vuex中的state数据
    // 函数的名称一定叫 state
    export const state = () => ({
      num: 100
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    pages/count/index.vue:

    <template>
      <div>
        <h3>路由 /counth3>
        <hr>
        <div>vuex中的state数据显示:{{ num }}div>
      div>
    template>
    
    <script>
    import { mapState } from 'vuex'
    export default {
    
      computed: {
        // 方式1
        // num() {
        //   return this.$store.state.count.num
        // }
        // 方式2
        ...mapState('count', ['num']),
      },
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    4.2 同步方法与异步方法

    store/count.js:

    // 以独立的函数方法来定义vuex中的state数据
    // 函数的名称一定叫 state
    export const state = () => ({
      num: 100
    })
    
    // 同步
    export const mutations = {
      addNum(state, payload) {
        state.num += payload
      }
    }
    
    // 异步
    export const actions = {
      asyncAddNum({ commit }, payload) {
        setTimeout(() => {
          commit('addNum', payload)
        }, 1000);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    pages/count/index.vue:

    <template>
      <div>
        <h3>路由 /counth3>
        <hr>
        <div>vuex中的state数据显示:{{ num }}div>
        <button @click="addNum">+++++button>
      div>
    template>
    
    <script>
    import { mapState, mapGetters } from 'vuex'
    export default {
    
      computed: {
        // 方式1
        // num() {
        //   return this.$store.state.count.num
        // }
        // 方式2
        ...mapState('count', ['num']),
        // ...mapGetters('count', ['showNum'])
      },
      methods: {
        addNum() {
          // 同步
          this.$store.commit('count/addNum', 1)
          // 异步
          // this.$store.dispatch('count/asyncAddNum', 2)
        }
      }
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述

    推荐插件Nuxt,它可以快速生成 vuex 相关的文件:

    在这里插入图片描述

    使用方式:按下 ctrl+shift+p 输入 nuxt 然后选择 create store ,最后输入文件名称就可以创建成功:

    在这里插入图片描述

    在这里插入图片描述

    4.3 数据持久化处理

    安装js-cookie(客户端路由切换时使用)和cookie-parse(服务端渲染时使用):

    yarn add js-cookie cookie-parse
    
    • 1

    mock 假数据(static/login.json):

    {
      "code":0,
      "msg":"ok",
      "data":{
        "uid":1000,
        "token":"fewjlfjewklfewj;fewj;few;"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    登录页面:

    <template>
      <div>
        <h3>我是一个登录页面h3>
        <hr>
        <div>
          <input type="text" v-model="formData.username">
        div>
        <div>
          <input type="text" v-model="formData.password">
        div>
    
        <hr>
        <button @click="doLogin">进入系统button>
      div>
    template>
    
    <script>
    // 这样导入,在初始时,在服务器渲染时,它也会导入,这样影响性能
    // import cookie from 'js-cookie'
    
    // 渲染环境判断的全局变量
    // console.log(process.server);
    // console.log(process.client);
    
    // 性能优化所用
    const cookie = process.client ? require('js-cookie') : null
    
    export default {
      layout: 'empty',
      data() {
        return {
          formData: {
            username: 'admin',
            password: 'admin888'
          }
        }
      },
      methods: {
        async doLogin() {
          // 进行登录请求的验证
          let ret = await this.$axios.get('/login.json')
          // 登录成功,写入到cookie中,进行vuex数据持久化处理
          cookie.set('token', ret.data.data.token)
          cookie.set('uid', ret.data.data.uid)
          // 写入到vuex中
          this.$store.commit('user/setUserInfo', ret.data.data)
    
          // 跳转到后台
          this.$router.push('/admin')
        }
      }
    
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    store/index.js:

    export const actions = {
      // 在服务器加载时,会主动执行1次
      nuxtServerInit({ commit }, { req }) {
        // 在服务器端得到当前的cookie数据,cookie数据是通过请求头发送给服务器
        // string
        // token=fewjlfjewklfewj%3Bfewj%3Bfew%3B; uid=1000
        // 方案1
        let cookieStr = req.headers.cookie
        if (cookieStr) {
          let jsonCookie = cookieStr.split(';').reduce((p, c) => {
            let [key, value] = c.split('=')
            p[key.trim()] = value
            return p
          }, {})
          // 同步到vuex中 -- 解决刷新丢失问题
          commit('user/setUserInfo', jsonCookie)
        }
        // 方案2
        // try {
        //   let cookieStr = req.headers.cookie
        //   let jsonCookie = cookieStr.split(';').reduce((p, c) => {
        //     let [key, value] = c.split('=')
        //     p[key.trim()] = value
        //     return p
        //   }, {})
        //   // 同步到vuex中 -- 解决刷新丢失问题
        //   commit('user/setUserInfo', jsonCookie)
        // } catch (error) {
    
        // }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    store/user/state.js:

    export default () => ({
      uid: 0,
      token: ''
    })
    
    • 1
    • 2
    • 3
    • 4

    store/user/mutations.js:

    export default {
      setUserInfo(state, payload) {
        state.uid = payload.uid
        state.token = payload.token
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    5. 中间件处理

    在根目录下创建 middleware 文件,每一个中间件都应该放在这个目录下。

    middleware/logincheck.js:

    export default ({ store, redirect }) => {
    
      if (!store.state.user.token) {
        redirect('/login')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面这个中间件表示如果不是登录状态,就会重定向到登录页面。

    注册全局中间件:

    全局中间件就是每进入一个页面都会执行的中间件,相当于全局前置守卫。它需要在 nuxt.config.js 的 router 中进行配置。

      router: {
        // 全局中间件注册
        middleware: 'checklogin',
        // 动态添加路由 但是一定要注意在pages中不能有_.vue这样的文件,否则路由匹配不成功[push]
        extendRoutes(routes, resolve) {
          /* routes.push({
            path: '/abc',
            component: resolve(__dirname, 'pages/about.vue')
          }) */
          // 如果你用_.vue,则需要用unshift方法来动态添加路由信息
          routes.unshift({
            path: '/abc',
            component: resolve(__dirname, 'pages/about.vue')
          })
        }
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    匹配布局:

    相当于路由独享守卫,写在 layouts/default.vue 中。

    <template>
      <div>
        
        
        
        <nuxt />
      div>
    template>
    
    <script>
    export default {
      // 路由独享
      // middleware: 'checklogin'
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    匹配页面:

    相当于组件内守卫。

    <template>
      <div>
        <h3>我是一个后台h3>
        
        <nuxt-child />
      div>
    template>
    
    <script>
    export default {
      // 写在组件或父组件中
      middleware: 'checklogin'
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果不是登录状态,就跳转到登录页面,用组件内中间件更合适。

  • 相关阅读:
    阈值回归模型(Threshold Regression Model)及R实现
    Vue3+typescript项目使用script-setup写法时,模版中的变量和方法编辑器检测都为Unresolved variable或者Element is not exported,如何解决
    React(2)
    客户端负载均衡_负载均衡策略
    如何选择正确的SSL证书?
    前端培训丁鹿学堂:每天五分钟,轻松入门vue3(三)
    【Unity ShaderGraph】| Shader Graph入门介绍 | 简介 | 配置环境 | 窗口介绍 | 简单案例
    关于博主的介绍
    sentinel环境搭建以及微服务接入
    2022年下半年软件设计师下午真题及答案解析
  • 原文地址:https://blog.csdn.net/weixin_45605541/article/details/128107883