Vue Router的数据获取方式优化

1,310 阅读2分钟

用过Vue,Vue-Router的同学在获取数据的时候,肯定遇到过路由切换后,数据没有重新获取的情况。

官方文档中给出来两种解决方案:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。

  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

今天主要讲下第一种的情况:导航完成后获取数据。


首先给出官网的例子:

<template>
  <div class="post">
    <div class="loading" v-if="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>
export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // 组件创建完后获取数据,
    // 此时 data 已经被 observed 了
    this.fetchData()
  },
  watch: {
    // 如果路由有变化,会再次执行该方法
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}

vue-router的路由加载是相同的的组件会重复利用,节约性能开销,当路径的变化时,会利用其他的手段来监听路参数的变化,如watch属性。上述代码中,在component的created钩子函数中获取数据,然后在watch属性中监听路由$route的参数的变化,当路由切换后,根据id再次获取数据。这个方法可以有两种不同的另类实现方式。

第一种方式:

export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  watch: {
    // 如果路由有变化,会再次执行该方法
    '$route': {
        handler: 'fetchData',
        immediate: true
    }
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        //
      })
    }
  }
}

watch监听的$route变成了一个对象,handler是调用的方法,immediate则表示在组件加载完成后立即执行handler的方法,并且可以删除created生命周期函数。

第一种方式已经稍微简化了代码。但是其实还有另外一种的方法。

export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created() {
    this.fetchData()
  }
  methods: {
    fetchData () {
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        //
      })
    }
  }
}

<router-view :key="$route.fullPath"></router-view>

上述代码的trick是在<router-view />组件添加一个keyprop,值为$route.fullPath。当key变化时间,组件会删除再重新创建,然后执行created钩子函数。这个方法会稍微影响应用的性能,但是数据加载不会出bug。