# 数据加载的问题

在使用了mock.js进行数据填充之后,通过axios发送数据请求,而mock进行监听和拦截,并且根据请求发送数据

但是在原本的代码结构中,出现了一些情况。

# 原代码展示

# vue/js

这里是原 App.vue 和 store中的部分代码

//... 代码省略
export default {
  //...
  created () {
    console.log('app.vue.created 执行了')
    axios.get('/user/allUsers')
      .then(res => {
        this.$store.commit('getAlluUsers', res.data.list)
      })
      .catch(err => {
        console.log(err)
      })
    // ...后面类似,在此省略
  }
}

// store.js
//...
state: {
  allUsers: []
},
mutataions: {
  getAllUsers (state, allUsers) {
    state.allUsersReady = true
    console.log('the allUsers is ready')
  }
}

这里是其他几个vue文件中的部分代码

// homePage.vue
// ...
created () {
  console.log('homePage.vue.created执行了')
},
mounted () {
  console.log('homePage.vue.mounted执行了')
},

// homePageMain.vue
// ...
created () {
  console.log('homePageMain.vue.created执行了')
},
mounted () {
  console.log('homePageMain.vue.mounted执行了')
},

// carseAndComTab.vue
// ...
mounted () {
  console.log('carseAndComTab.vue.mounted执行了')
  // 调用 store 中的数据,这里省略
}

# html

在html代码结构上如下:

<!-- App.vue -->
<div>
  <router-view></router-view>   <!-- 这里是载入homePage.vue,后见路由结构 -->
</div>

<!-- homePage.vue -->
<!-- ...省略 -->
<div>
  <router-view></router-view>   <!-- 这里是载入homePageMain.vue -->
</div>

<!-- homePageMain.vue -->
<div>
  <carseAndComTab></carseAndComTab>   <!-- 这里是carseAndComTab组件-->
</div>

# 路由

路由结构如下:

// ...
routers: [
  {
    path: '/',
    redirect: '/home' //默认跳转到/home
  },
  {
    path: 'home',
    component: homePage,
    children: [
      {
        path: '',
        component: homePageMain
      },
      // ...
    ]
  }
]

# 问题描述

到这里,得讲一下这样子出现的一个问题了。

按照期望,我们希望得到的结果就是:

1.打开页面(localhost:8080)

2.获取到数据

3.页面跳转到 localhost:8080/#/home, 该页面的内容在 App.vue中的标签中显示

4.而homePage中的 router-view 标签中也载入了 homePageMain 的内容

5.内容渲染出来

但是实际上每一次打开页面,或在localhost:8080/#/home 刷新页面的时候,会出现这种情况

所有应该渲染出来的数据都没有渲染出来,都为空白

但是打开vue工具,查看vuex,可以看到数据是获取到的,axios也并没有报错。

如果这个时候我面点开另外一个页面,比如 localhost:8080/#/search 这是另外一个页面

然后我们回到home页面,会发现数据渲染出来了

然后刷新页面,数据又没有渲染出来,这是一个比较恶劣的bug

# 判断问题

通过查看控制台

# 打开 localhost:8080 控制台输出

1.app.vue.created执行了

2.homePage.vue.created执行了

3.homePage.vue.mounted执行了

4.homePageMain.vue.created执行了

5.homePageMain.vue.mounted执行了

6.carseAndComTab.vue.mounted执行了

7.the allUsers is ready

在这里就发现了问题:在后续的代码已经执行的情况之下,axios发送的请求还未得到返回

$store.state.allUsers还未完成数据载入,因此是空数组

渲染出来的页面数据也就为空,而之后数据才进行载入,没有触发更新,不触发响应,因此第一次进入页面时没有数据渲染出来

而点进别的页面,而后退回来,因为已经载入了数据,所以数据正常渲染

# 在 localhost:8080/#/home 页面进行刷新

1.app.vue.created执行了

2.homePage.vue.created执行了

3.homePage.vue.mounted执行了

4.homePageMain.vue.created执行了

5.homePageMain.vue.mounted执行了

6.the allUsers is ready

7.carseAndComTab.vue.mounted执行了

# 问题确定

到这里基本上就可以确定问题的发生原因:第一次打开网页,页面渲染了但是数据还没载入(更新)

进一步判断问题出处: axios.get('...').then(res => {...})

这是一个异步请求。

数据还没发送回来,还没载入,后面的代码就已经开跑了。

# 解决问题

# 思路

既然我们要默认跳转到 /home 页面,那么首先为了保证代码能够执行,我们把路由中的 redirect: '/home' 去掉

通过 this.$router.push({path: '/home'}) 来实现

为了避免数据还未获取到就跳转页面,所以我们需要添加一个判断条件,来判断数据是否已经获取到,然后才跳转页面

那么这个判断-跳转的方法应该添加在哪里?created? mounted? 实际上都不对,因为axios.get...then...是异步操作,数据获取到的时间我们不能确定到,除非我们写一个不断判断的方法,但显然这样子并不可取

我们把这个判断-跳转的方法写在 axios.get...then(res => {这里面}),也就是写在回调函数之中,这样子便可精准的在数据获取到了之后进行判断,然后跳转

在 $store.state 中, 添加一个状态: allUsersReady: false

修改 mutations.getAllUsers ,添加语句 state.allUsersReady = true

在axios.get..then 回调函数中,添加语句

axios.get('/user/allUsers')
  .then(res => {
    this.$store.commit('getAllUsers', res.data.list)
    if (this.$store.state.allUsersReady) {
      this.$router.push('/home')
    }
  })

到这里,我们大体上解决了,打开 localhost:8080 时,数据全部为空的错误,但是接下来还有一个问题为解决

那就是在 /home 页面刷新之后,数据依旧丢失的问题

因为上述判断的是当满足条件之后才跳转到 home,但本身在home页面刷新之后数据依旧是比页面渲染慢的

故技重施,我们再加一个判断,在homePage.vue 的div上,加上 v-if

<div v-if="$store.state.allUsersReady">
  <!-- 省略 -->
</div>

这样子,在数据没有载入之前,不会进行渲染

为了界面美观,我们也可以使用element-ui提供的loading组件或其他组件,这里就不加以描述

# 最后代码

// App.vue
//...
created () {
  axios.get('/user/allUsers')
    .then(res => {
      this.$store.commit('getAllusers', res.data.list)
      this.toHome()
    })
    .catch(err => {
      console.log(err)
    })
}
// ...
methods:{
  toHome () {
    if (this.$store.state.allUsersReady) {
      this.$router.push({path: '/home'})
    }
  }
}

// 路由 index.js
//...
{
  path: '/'
}

<!-- homePage.vue -->
<el-main v-if="$store.state.allPostsReady">
  <!-- ... -->
</el-main>

更新时间: 7/4/2021, 10:56:16 PM