路由组件获取异步数据的两种方式总结

(我来水经验了×,因为马上要去美团实习了,技术栈是vue2 + ts,在看vue-router文档的时候看到了关于路由组件获取异步数据的两种方式的总结,以前做业务都是无脑写,还真没总结过,所以简单写个vue2 + class-component + ts的demo,包括一些技术栈使用相关的细节也会记录一下,如果不感兴趣跳过就行

路由组件获取数据方式总结

某个路由组件需要通过网络请求获取服务端数据,其实无非就两种方式:第一种即在组件内获取,比如在created钩子中发请求;第二种就是在beforeRouteEnter路由守卫中。

两种方式从逻辑来讲都是没有问题的,主要的差别就在于用户体验的不同:生命周期中获取异步数据,用户会第一时间转跳到路由视图,往往需要搭配一些条件渲染的逻辑来优化数据获取之前的路由视图(比如数据渲染前展示Loading);路由守卫中获取异步数据的话,在next调用之前会一直停留在当前页面而不会进入路由视图,自然也需要一些类似于Loading或者progress的反馈组件来优化体验。

下面简单实现一下上面的两种情形,适合前端小白入门以及熟悉技术栈来食用,当作一个学习demo练手的话可以跟着操作一下

项目搭建

vue-cli创建项目:

arduino 复制代码
vue create vue2-class-component

选择Manually select features,选择Typescript以及vue2.x + class-style component,其它关于lint的选项就无所谓了

安装vue-router

如果是vue2项目,安装vue-router一定不要安装vue-router@4,4版本是对应vue3的

所以安装依赖:

css 复制代码
pnpm i vue-router@3.5.2

@/router/index.ts

ts 复制代码
+ import Home from '@/pages/Home.vue';
+ import VueRouter from 'vue-router';
+ import Vue from 'vue';
+ Vue.use(VueRouter);
​
+ const routes = [
+     {
+        path: '/',
+         redirect: '/home'
+     },
+     {
+         path: '/home',
+         component: Home
+     }
+ ]
​
+ const router = new VueRouter({
+     routes,
+ })
​
+ export default router;

main.ts

ts 复制代码
import 'vue-class-component/hooks'
import Vue from 'vue'
import App from './App.vue'
+ import router from './router'
​
Vue.config.productionTip = false
​
new Vue({
+ router,
  render: h => h(App),
}).$mount('#app')

上面简单配置了两个路由组件HomeCenterHome用来实现生命周期钩子里获取数据,Center实现路由守卫里获取数据。

路由组件

生命周期中获取数据

Home

html 复制代码
<template>
  <div>
    <Loading v-if="isLoading" />
    <div v-else>
        homeData: {{ homeData }}
    </div>
  </div>
</template>
​
<script lang="ts">
import Loading from '@/components/Loading.vue';
import { Component, Vue } from 'vue-property-decorator';
import { getHomeData } from '../api/index';
​
@Component({
    components: {
        Loading
    }
})
export default class Home extends Vue {
    public isLoading = true;
    public homeData = '';
    async created() {
        try {
            const homeData = await getHomeData();
            this.homeData = homeData;
            this.isLoading = false;
        } catch(e) {
            console.log(e);
        }
    }
    mounted() {
        console.log(this);
    }
}
</script>
​
<style scoped>
</style>

getHomeData即用定时器简单模拟了一下网络请求,@/api/index.ts

ts 复制代码
export const getHomeData: () => Promise<string> = async () => {
    // 模拟ajax请求
    const p = new Promise<string>((resolve) => {
        setTimeout(() => {
            resolve('home data');
        }, 3000);
    })
    return p;
}
​
export const getCenterData: () => Promise<string> = async () => {
    const p = new Promise<string>((resolve) => {
        setTimeout(() => {
            resolve('center data');
        }, 3000);
    })
    return p;
}

自定义的Loading组件也非常简单,没有样式,重在理清逻辑,点到为止~,@/components/Loading.vue

html 复制代码
<template>
  <div>
    loading...
  </div>
</template>
​
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
​
@Component
export default class Loading extends Vue {
}
</script>

路由守卫中获取异步数据

因为使用class-component的缘故,我们需要事先注册一下vue-router的钩子class组件才能识别router的路由守卫钩子

注册router-hook

@/class-component-hooks.ts

ts 复制代码
// class-component-hooks.js
import Component from 'vue-class-component'
​
// Register the router hooks with their names
Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate'
])

在项目入口文件中引用一下,但一定要保证引用语句在导入组件的语句之前,如import App from './App.vue'之前,@/main.ts

ts 复制代码
+ import './class-component-hooks' // 先于组件引用
import App from './App.vue'
​
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

因为自己的Loading太丑了,所以还是用Element-ui的组件吧

引入element-ui

安装依赖:pnpm i element-ui

全局引入组件库的样式,main.ts

arduino 复制代码
import 'element-ui/lib/theme-chalk/index.css';

这样在需要Loading组件的地方导入并使用就可以了

Center.vue路由组件

下面是Center组件的逻辑,@/pages/Center.vue

html 复制代码
<template>
  <div>
    <div>
        centerData: {{ centerData }}
    </div>
  </div>
</template>
​
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { getCenterData } from '../api/index';
import { Route } from 'vue-router';
import { Loading } from 'element-ui';
​
@Component
export default class Center extends Vue {
    public centerData = '';
    async beforeRouteEnter(to: Route, from: Route, next: (callback: (vm: Center) => void) => void) {
        try {
            const loading = Loading.service({
                lock: true,
                text: 'Loading',
                spinner: 'el-icon-loading',
                background: 'rgba(0, 0, 0, 0.7)'
            });
            const centerData = await getCenterData();
            next(vm => vm.setCenterData(centerData));
            // next后的函数体仍会执行
            loading.close();
        } catch(e) {
            console.log(e);
        }
    }
    setCenterData(centerData: string) {
        this.centerData = centerData;
    }
}
</script>
​
<style scoped>
</style>

说白了就是beforeRouteEnter这个钩子里面先给一个loading的反馈,数据获取之后next放行以及关闭loading,但注意给next方法标注类型的时候,next函数回调的入参vm标注当前组件类型Center比较合适,方便后续调用setCenterData方法来修改组件内数据,之所以要通过这种方式修改组件数据,因为beforeRouteEnter钩子执行时机是早于beforeCreate(组件创建)的,所以vue-router考虑到了用户操作组件状态的需求,所以暴露给我们一个异步回调,回调中可以访问组件实例用来后续组件操作。

测试与结果

简单在HelloWorld.vue中消费一下这俩路由组件,@/components/HelloWorld.vue

html 复制代码
<template>
  <div class="hello">
    <router-view></router-view>
    <router-link to="/home">go Home</router-link>
    <hr />
    <router-link to="/center">go Center</router-link>
  </div>
</template>
​
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
​
@Component
export default class HelloWorld extends Vue {
}
</script>

效果:

demo源码

相关推荐
吕彬-前端24 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱27 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai36 分钟前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb