• 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: "账号或者密码出错"
            })
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可是,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: "当前用户未登录"
            })
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    前台页面:

    import { request } from "@/utils/request";
    
    export default {
        async asyncData() {
            // 封装后的axios请求方法
            let result = await request.get("/users/userInfo");
            console.log(result);
            return {
    	    	result
            }
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
            }
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    编写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)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    现在回到首页,将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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    三、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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    STM32F429基于TouchGFX进行简单控制LED和显示ADC值
    Nginx + RTMP + nginx-http-flv-module 环境搭建(CentOS 7)
    高通平台Android 蓝牙调试和配置手册-- Pairing Failure
    类的构造函数执行顺序
    flutter开发实战-安卓apk安装、卸载、启动实现
    Ansible 多机自动化工具 初学笔记
    HCIP练习(OSPF)
    老黄自己卷自己!GPU要一年更新一代!预告新动作:AI工厂将吞噬一切
    低代码技术研究路径解读|低代码的产生不是偶然,是数字技术发展的必然
    红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远控介绍及界面编写
  • 原文地址:https://blog.csdn.net/flow_camphor/article/details/125490566