1.VUE 路由变化页面数据不刷新问题

问题描述:(知乎项目)新闻板块有推荐、精华、最新等几个Tab,设想通过切换Tab,改变路由参数(get/news/:tab)去获取对应数据,然后渲染到页面(用的是同一套组件),问题来了:当切换Tab时,数据并没有更新

原因:提醒一下,当使用路由参数时,例如从 /user/foo 导航到 user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。意思很明显了:虽然路由参数发生了变化,但组件还是那个组件,Vue生命周期也还没结束,此时并不会刷新数据。

解决:1.复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch(监测变化) $route 对象:

watch: {
  // 方法1
  '$route' (to, from) { //监听路由是否变化
    if(this.$route.params.articleId){// 判断条件1  判断传递值的变化
      //获取文章数据
    }
  } 
   //方法2
  '$route'(to, from) {
    if (to.path == "/page") {  /// 判断条件2  监听路由名 监听你从什么路由跳转过来的
       this.message = this.$route.query.msg     
    }
  }  
}​

2.给<router-view></router-view>添加一个具有唯一值的 key 属性,声明“这两个元素是完全独立的——不要复用它们”。

关于key的作用,可以看看知乎的一篇文章

<router-view :key="key"></router-view>

computed: {
    key() {
        return this.$route.name !== undefined? this.$route.name +new Date(): this.$route +new Date()
    }
 }

使用computed属性和Date()可以保证每一次的key都是不同的,这样就可以如愿刷新数据了。

 

2.vue 滚动行为

问题描述:在页面拉动滚动条,然后刷新浏览器会发现滚动条依然在原来的位置,这是浏览器的默认行为,会记录浏览器滚动条默认位置。但是点击浏览器“前进/后退”按钮,会发现当初那个页面的滚动条从0开始了,没有记录上一次滚动条的位置。现在要求点击浏览器“前进/后退”按钮,页面滚动条要记录上一次的位置,这时需要设置它的的滚动行为。

解决:在路由配置中设置 scrollBehavior(to,from,savePosition)函数,函数有三个参数。scrollBehavior() 函数在点击浏览器的“前进/后退”,或者切换导航的时候触发。

注意:这个功能只在支持 history.pushState 的浏览器中可用。

const router = new  VueRouter({
       mode: 'history',
       scrollBehavior (to, from, savedPosition) {
   
            if (savedPosition) { //如果savedPosition存在,滚动条会自动跳到记录的值的地方
       
                 return savedPosition
           } else{
                 return { x: 0, y: 0}//savedPosition也是一个记录x轴和y轴位置的对象
          }
         },
       routes: [...]
     })

  我们还可以设hash来控制滚动行为,定位到某一位置(模拟『滚动到锚点』的行为):

let router = new VueRouter({
  mode:'history',//默认是hash模式
  linkActiveClass:'menvscode-active',
  scrollBehavior(to,from,savePosition){ // 在点击浏览器的“前进/后退”,或者切换导航的时候触发。
    console.log(to) // to:要进入的目标路由对象,到哪里去
    console.log(from) // from:离开的路由对象,哪里来
    console.log(savePosition) // savePosition:会记录滚动条的坐标,点击前进/后退的时候记录值{x:?,y:?}
    /*if(savePosition) {
      return savePosition;
    }else{
      return {x:0,y:0}
    }*/
    if(to.hash){ //先判断目标路由有没有hash值
      return {selector:to.hash}
    }
  },
  routes:[]
})
 
export default router
 

 

3.路由跳转后setInterval继续运行并没有及时进行销毁

问题描述:(myShop移动端)比如倒计时,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是setInterval还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。

解决:在组件生命周期beforeDestroy停止setInterval

beforeDestory() {
    clearInterval(this.timer);
    MessageBox.close()                
}​

 

4.切换路由的时候取消上一页所有请求

问题描述:在真实项目中,当路由已经跳转,而上一页的请求还在pending状态,如果数据量小还好,数据量大时,跳到新页面,旧的请求依旧没有停止,这将会十分损耗性能,这时我们应该先取消掉之前还没有获得相应的请求,再跳转页面。

解决:axios给我们提供了一个方法:cancelToken

让我们来看看cancelToken的使用方法:

//官网方法一:
var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
    cancelToken: source.token
}).catch(function (thrown) {
    if (axios.isCancel(thrown)) {
        console.log('Request canceled', thrown.message);
    } else { // 处理错误
    }
}); // 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

如果我要跳转页面的话,我调用source.cance()方法就可以干掉之前这个没有请求完的请求了。
但是这个方法有个弊端,就是比较麻烦,每次都要手动去调用source.cance()方法。怎么做到全局统一管理呢?

官网给了以下方法:

还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

axios.get('/user/12345', {
    cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数
        cancel = c;
    })
}); // 取消请求cancel();

根据这个方法:我们一步一步来实现它:
在main.js里写一个全局httpRequestList的空数组,用来装我们的cancel函数:

// main.js
Vue.$httpRequestList = []

再在我们封装好的post,get请求里面,将每一个请求里面都做一个将cancel函数推入的httpRequestList数组的动作:

POST(url, data, errMsg) {
    const CancelToken = axios.CancelToken
    return axios.post(url, data, {
        timeout: 30000,
        cancelToken: new CancelToken(function executor(c) {
            Vue.$httpRequestList.push(c)
        })
    }).then(checkStatus).then(res => checkCode(res, errMsg))
},
GET(url, params, errMsg) {
    const CancelToken = axios.CancelToken
    return axios.get(url, {
        params: {
            _t: +(new Date()),
            ...params
        },
        timeout: 30000,
        cancelToken: new CancelToken(function executor(c) {
            Vue.$httpRequestList.push(c)
        })
    }).then(checkStatus).then(res => checkCode(res, errMsg))
}

这样我们的每一个请求里面,都包含了一个cancelToken对象。

在这之后我们要写一个执行cancel方法的 方法:

import Vue from 'vue'
export const clearHttpRequestingList = () => {
    if (Vue.$httpRequestList.length > 0) {
        Vue.$httpRequestList.forEach((item) => {
            item()
        })
        Vue.$httpRequestList = []
    }
}

tem就是之前每一个请求装进httpRequestList数组的cancel方法,item()执行后,如果该请求是pending状态,那么可以直接取消掉。执行完后记得清空httpRequestList数组。

最后我们回到main.js
在每次跳转之前执行clearHttpRequestingList()函数。

router.beforeEach((to, from, next) => {
  clearHttpRequestingList()
  ..........这下面是你的路由验证代码..........
})

这样就实现了每次路由跳转之前,就清空之前出于pending状态的请求,优化了性能。

 

5.解决 4 问题的方法的另外一种

问题描述:如果上面的方法有用就不用看这里了,但如果像我一样,把向后台请求数据的request方法封装起来,并且把业务逻辑封装成类继承了有request方法的base类,那么我们可以

1)建一个vue-bus.js,

const VueBus = {
  $httpRequestList: [],
  clearHttpRequestingList: () => {
    if (VueBus.$httpRequestList.length > 0) {
      VueBus.$httpRequestList.forEach((item) => {
        item();
      })
      VueBus.$httpRequestList = []
    }
  }
}

export default VueBus;

2)然后在base.js中request方法中将每个请求都存储到VueBus的$httpRequestList数组中

class Base {
  constructor() {
    this.baseRestUrl = Config.restUrl;
  }
  request(params, noRefetch) {
    const CancelToken = axios.CancelToken
    var that = this;
    var url = this.baseRestUrl + params.url;
    if (!params.type) {
      params.type = 'get';
    }
    return axios({
      url: url,
      data: params.data,
      header: {
        'content-type': 'application/json',
      },
      method: params.type,
      // 在这里加上每个请求都存储到VueBus的$httpRequestList数组中
      timeout: 30000,
      cancelToken: new CancelToken(function executor(c) {
        VueBus.$httpRequestList.push(c);
      })
    }).then(res => {
      var code = res.status.toString();
      var startChar = code.charAt(0);

      if (startChar == '2') {
        params.sCallback && params.sCallback(res.data);
      } else {
        that._processError(res);
        params.eCallback && params.eCallback(res.data);
      }
    }).catch(err => {
      that._processError(err);
    })

  }

3)最后在main.js中添加一个全局前置导航钩子,在进入另外一个页面前执行VueBus.clearHttpRequestingList();方法,先将上一页的请求都撤销掉

router.beforeEach((to, from, next) => {
  VueBus.clearHttpRequestingList();
  .....
  next();
})

 

6.跨域

问题描述:vue项目中:localhost:8010,但我们需要的数据是我们自己搭建的服务器node项目中:localhost:8011 提供给我们的。因为浏览器的“同源政策”,我们不能直接进行跨域访问。

解决:解决方案也有很多。CORS,jsonp,...。而在我们的项目中,webpack-dev-server 给我们提供的proxy模块,可以很快解决这个问题。配置如下:

** config/index.js **

  dev: {
    env: require('./dev.env'),
    port: 8525,
    autoOpenBrowser: true,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {      
      '/api': {
        target: 'http://localhost:8011
        changeOrigin: true
      }
    },

 

7.页面跳转样式错位但是刷新又好了的情况

问题描述:同样的页面,由别的页面跳转过来,样式错位,但是刷新样式又好了

原因:要是同样的样式,前面已经加载过了,浏览器在跳到下个页面相同的样式就不会再加载了。在起名的时候可以自行区分,也可以根据官网的提示

解决办法:在page的style中加上scoped,这个可选 scoped 属性会自动添加一个唯一的属性 (比如 data-v-21e5b78) 为组件内 CSS 指定作用域(只在该组件内有效),也就是给你定义的名字前加一串哈希值,编译的时候 .list-container:hover 会被编译成类似 .list-container[data-v-21e5b78]:hover。

如下:

<style scoped>
.note{
    position: absolute;
    width:400px;
    height:400px;
    margin-left:20%;
}
</style>

 

8.VUE如何实现切换页面时的过渡动画?

问题描述:在切换页面时根据页面的层级判断做前进还是后退动画

解决:这个问题的难点就是如何来确定路由是前进还是后退的问题,下面是解决的方案

1)我们需要给各个页面定义层级,在切换路由时判断用户是进入哪一层页面,如果用户进入更高层级那么做前进动画,如果用户退到低层级那么做后退动画.

router/index.js

import VueRouter from 'vue-router'
import Home from '../components/home/home'
import User from '../components/user/user'
var router = new VueRouter({
    routes: [{
            name: 'test',
            path: '/',
            meta: {
                index: 0
            }, // meta对象的index用来定义当前路由的层级,由小到大,由低到高
            component: {
                template: '<div>test</div>'
            }
        },
        {
            name: 'home',
            path: '/home',
            meta: {
                index: 1
            },
            component: Home
        },
        {
            name: 'user',
            path: '/user/:id',
            meta: {
                index: 2
            },
            component: User
        }
    ]
})

2)监控路由跳转,判断切换页面之间的层级关系,并以此来判断路由前进或者后退.

3)编写slide-left 和 slide-right 类的动画

<template>
   <div id="app">
    <transition :name="transitionName">
       <router-view></router-view>
     </transition>
   </div>
 </template>
 <script>
export default {
  name: "App",
  data() {
    return {
      transitionName: ""
    };
  },
  watch: {
    // 使用watch 监听$router的变化
    $route(to, from) {
      // 如果to索引大于from索引,判断为前进状态,反之则为后退状态
      if (to.meta.index > from.meta.index) {
        // 设置动画名称
        this.transitionName = "slide-left";
      } else {
        this.transitionName = "slide-right";
      }
    }
  }
};
</script>
 <style>
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
  will-change: transform;
  transition: all 500ms;
  position: absolute;
}
.slide-right-enter {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
.slide-right-leave-active {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}
.slide-left-enter {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}
.slide-left-leave-active {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}
</style>
 

 

持续更新中~

 

Logo

前往低代码交流专区

更多推荐