Nuxt.js数据预取

本文采用的技术框架有:

后台:Express + MongoDb

前台:Vue2.js + Nuxt.js@2.9.2

在Nuxt中发送请求有两种方案:

  1. 前后台分离的方案 (数据到页面的过程是在浏览器完成的) 网页源代码中没有数据

    前台发送请求 -> 服务器处理请求 -> 前台接收到数据 -> 前台把数据渲染到页面

    凡是通过js的行为获取到的数据都是前后台分离的

  2. 服务端渲染的方案

    1. asyncData:写在页面组件中,获取页面中需要服务端渲染的数据,把数据return给页面 (直接在页面上使用数据)
    2. nuxtServerInit:在store/index.js里面写,永远在服务器运行,只会在页面首次加载的时候执行一次 (适合写一些全局共享的数据)
    3. fetch:写在页面组件中,获取页面中需要服务端渲染的数据,把数据存到vuex (父子组件的数据共享可以优先使用fetch)

一、asyncData

在组件加载之前先去发起请求拿到数据,提前将数据渲染在页面,然后返回的页面中将包含数据。

  1. 由于在客户端创建实例化之前加载,所以不能使用 this,钩子提供一个参数,可以获取上下文对象({isDev, route, store, env, params, query, req, res, redirect, error}等),从而做一些简单操作。
  2. 只能在路由页面组件中使用(每次加载页面都会调用),在自定义组件中无效。
  3. 返回的数据最终将与 data 数据合并,为了保证不发生页面渲染错误,返回的键应事先在 data 里声明好(如果 template 中没有使用所需属性,则并不必声明)。

1.1 一个例子

我们初次打开某个网站时,页面中会显示立即登录/注册的字样

在这里插入图片描述

在成功登录后,该部分会被替换为

在这里插入图片描述

现在的需求是,通过服务端渲染的方式将用户信息显示在页面上

1.2 初步分析

在用户成功登录的时候,可以将个人信息保存至服务器的session中,也就意味着,我们似乎可以通过session获取到用户信息。

router.post("/singin", async (req, res) => {
    const { username, password } = req.body;
    const findUser = await User.findOne({ username, password });
    //登录成功
    if (findUser) {
        // 个人信息保存至服务器的session中
        req .session.user = findUser;
        res.json({
            code: 0,
            msg: "登录成功"
        })
    }
    //登录失败
    else {
        res.json({
            code: -1,
            msg: "账号或者密码出错"
        })
    }
})

可是,session保存在服务器中,而我们需要的是在浏览器中显示数据。显然,浏览器中拿不到服务器中的数据

上面加粗的文字似乎存在问题,看起来写一个接口,在进入首页时通过接口请求session中的数据好像也可以,尝试一下。

1.2.1 编码

请求session数据的接口:

router.get("/userInfo", (req, res) => {
    if (req.session.user) {
        res.json({
            code: 0,
            user: req.session.user,
            msg: "获取用户信息成功"
        })
    }
    else {
        res.json({
            code: 0,
            user: null,
            msg: "当前用户未登录"
        })
    }
})

前台页面:

import { request } from "@/utils/request";

export default {
    async asyncData() {
        // 封装后的axios请求方法
        let result = await request.get("/users/userInfo");
        console.log(result);
        return {
	    	result
        }
    },
};

1.2.2 测试

在首页登录后刷新页面

此时浏览器中可以成功显示登录后的数据,说明用户已经登录:

在这里插入图片描述

而服务器中则显示用户未登录:

在这里插入图片描述

此时就需要分析一下asyncData这个函数的用法了。

asyncData既可能运行在浏览器,又可能运行在服务器。

刷新页面的时候,asyncData方法运行在在服务器,所以数据消息会在服务器中打印,通过打印我们发现获取不到session数据。

路由跳转<nuxt-link>的时候,asyncData方法运行在浏览器,所以数据消息会在浏览器中打印,通过打印我们发现可以获取session数据。

为什么打印的位置不一样,session数据的打印也不一样呢?

这里涉及到了session的原理:

  • 用户在通过接口登录后,用户信息将会被保存到服务器的session中
  • session内部会创建cookie作为一个凭证,然后接口将这个凭证返回给浏览器保存
  • 浏览器下次发送请求的时候会带上这个cookie凭证

所以,上面的问题的原因就在于:

  • 请求从浏览器发出去的时候,浏览器会自动携带session对应的cookie发送服务器,所以服务器就可以根据cookie获取到数据

  • 而请求从服务器发出去的时候,就没有session对应的cookie,所以获取不到登录的用户信息

在这里我绘制一张图来描述,可能会更加清晰:

在这里插入图片描述

在上面的错误操作中,我们采用的方法是调用asyncData方法,这个方法在服务端中向接口发起请求,而不是浏览器发起的请求。由于cookie存在于浏览器中保存,所以用户登陆后。服务器中显示的是用户未登录。

在第二章中介绍具体的解决方法。

二、nuxtServerInit

这个方法只能在vuex中使用,永远只会在服务器运行,永远只是首屏加载的时候执行一次

可以接收两个参数:

  • 第一个参数保存着vuex信息

在这里插入图片描述

  • 第二个参数是上下文对象(context)

也就是说,在首屏加载过后,无论是通过this.$router.push还是<nuxt-link>跳转,该方法都不会执行,除非是再次刷新页面触发加载

这意味着,采用nuxtServerInit获取到的数据,在首屏加载过后无法通过上述两个方法将数据渲染到页面上。

// 负责登录的函数
submitForm() {
    // 这里采用了Elemnet-ui的表单验证方法
    this.$refs.loginForm.validate(async (valid) => {
        //表单校验通过,可以发送请求
        if (valid) {
            let result = await axios.post("/users/singin", {
                username: this.ruleForm.name,
                password: CryptoJS.MD5(this.ruleForm.pwd).toString(),
            });
            // 通过window.location.href = "/"刷新浏览器,此时可以将数据渲染到页面上
            result.code === 0
            	// ? this.$router.push('/')	无效
                ? (window.location.href = "/")
            	: (this.err = result.msg);
        }
    });
},

编写vuex:

import { request } from '@/utils/request'

export const state = () => ({
    user: null,
})

export const mutations = {
    SET_USER(state, payload) {
        state.user = payload
    }
}

export const actions = {
    // 产品分为前端和后端
    // 采用了SSR的前端可以分为 前端客户端[具体的html页面] + 前端服务端
    // 这里的 nuxtServerInit 运行在 前端服务端中
    
    // 该方法的第二个参数,可以获取到服务端的上下文对象
    // 查阅文档可知,在这里可以获取到nodejs服务器发起请求时的 req 数据
    async nuxtServerInit({ commit }, { req }) {
        const user = req.session.user;
        commit("SET_USER", user)
    }
}

现在回到首页,将vuex中的数据读取出来:

<template>
  <div>
    <template v-if="user">
      欢迎您,
      <span class="username">{{ user.username }}</span>
      [<nuxt-link to="/exit">退出</nuxt-link>]
    </template>
    <template v-else>
      <nuxt-link to="/login">立即登录</nuxt-link> |
      <nuxt-link to="/register">注册</nuxt-link>
    </template>
  </div>
</template>

<script>
import { mapState } from "vuex";
export default {
  computed:{
    ...mapState(['user'])
  }
};
</script>

此时可以成功将数据渲染在页面上。

三、fetch

这里仅讨论v2.12以下的版本

该方法与asyncData差不多,不过它无法将数据通过return的方式返回页面渲染。

如果页面组件设置了 fetch 方法,它会在组件每次加载前被调用(在服务端或切换至目标路由之前)。

fetch 方法的第一个参数是页面组件的上下文对象context,我们可以用 fetch 方法来获取数据填充应用的状态树。为了让获取过程可以异步,你需要返回一个 Promise,Nuxt.js 会等这个 promise 完成后再渲染组件。

注意: 无法在内部使用this获取组件实例fetch是在组件初始化之前被调用

比如:

<template>
	<Son></Son>
</template>

<script>
    export default {
        fetch({ store, params }) {
            return axios.get('/userList').then( res => {
                store.commit('ADD_USER', res.data)
            })
        }
    }
</script>
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值