Home  >  Article  >  Web Front-end  >  Let's talk about how vue prevents repeated requests in this article

Let's talk about how vue prevents repeated requests in this article

青灯夜游
青灯夜游forward
2023-01-28 19:31:132901browse

How does vue prevent repeated requests? The following article will introduce two ways for Vue to prevent repeated requests. I hope it will be helpful to you!

Let's talk about how vue prevents repeated requests in this article

The front-end code in the project will encounter the situation where the same request is sent to the server multiple times. We must avoid wasting server resources. The same request is only allowed to be sent once in a certain period of time. Request

Ideas

(1) If the business is simple, for example, to prevent multiple clicks on the same button, we can use a timer for anti-shake processing
(2 ) If the business is complex, for example, multiple components pass through the code, and the same request is sent multiple times, anti-shake is no longer easy to handle at this time. It is best to cancel the repeated ajax requests in a unified manner

Implement

Method 1 - Anti-shake processing through timer

(a) Overview

Effect: When the user clicks the same button multiple times in succession, after the last click, a request will be initiated after a short period of time
Principle: Each time the method is called Finally, a timer is generated. After the timer expires, the request will be sent. If the method is called repeatedly, the current timer will be canceled, a new timer will be created, and the request will be sent after the timer is over. You can use third-party encapsulated tool functions during the work. For example, the debounce method of lodash is used to simplify the anti-shake code [Related recommendations: vuejs video tutorial, web front-end development]

(b)Code

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
</head>
<body>
    <div id="app">
        <button @click="onClick">请求</button>
    </div>
</body>
<script>
// 定义请求接口
function sendPost(data){
    return axios({
        url: &#39;https://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}
new Vue({
    el: &#39;#app&#39;,
    methods: {
        // 调用lodash的防抖方法debounce,实现连续点击按钮多次,0.3秒后调用1次接口
        onClick: _.debounce(async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求的结果&#39;, res.data)
        }, 300),
    },
})
</script>
</html>

(c)Preview

Connection

(d)Existing problems

Cannot solve the problem of sending repeated requests for multiple button components, such as the following two situations

Situation-anti-shake on click events

Button events are independent of each other Yes, different methods are called, and the anti-shake effect between buttons cannot be achieved

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
</head>
<body>
    <div id="app">
        <button @click="onClick1" ref="btn1">请求1</button>
        <button @click="onClick2" ref="btn2">请求2</button>
    </div>
</body>
<script>
  
let sendPost = function(data){
    return axios({
        url: &#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}
new Vue({
    el: &#39;#app&#39;,
    mounted() {
        this.$refs.btn1.click()
        this.$refs.btn2.click()
    },
    methods: {
        // 使用lodash对请求方法做防抖
        //这里有问题,只是对每个按钮的点击事件单独做了防抖,但是两个按钮之间做不到防抖的效果
        onClick1: _.debounce(async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求1的结果&#39;, res.data)
        }, 300),
        onClick2: _.debounce(async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求2的结果&#39;, res.data)
        }, 300),
    },
})
</script>
</html>

Preview

Case 2- Anti-shake in the interface method

The methods called between buttons are the same. It is possible to perform anti-shake processing on the method, but the processing itself encapsulates the method once, which will affect the reception of the return value of the previous method. More processing needs to be done on the previous method to change It is more complicated, Not recommended

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
</head>
<body>
    <div id="app">
        <button @click="onClick1" ref="btn1">请求1</button>
        <button @click="onClick2" ref="btn2">请求2</button>
    </div>
</body>
<script>
// 使用lodash对请求方法做防抖,    
let sendPost = _.debounce(function(data){
    //这里有问题,这里的返回值不能作为sendPost方法执行的返回值,因为debounce内部包裹了一层
    return axios({
        url: &#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}, 300)
new Vue({
    el: &#39;#app&#39;,
    mounted() {
        this.$refs.btn1.click()
        this.$refs.btn2.click()
    },
    methods: {
        onClick1: async function(){
            //这里有问题,sendPost返回值不是promise,而是undefined
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求1的结果&#39;, res)
        },
        onClick2: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求2的结果&#39;, res)
        },
    },
})
</script>
</html>

Preview

##Method 2 - By canceling the ajax request

(a) Overview

Directly process the request method, and cancel repeated requests through the api method of the ajax library

(b) Principle

Native ajax cancel request

Cancel the request by calling the

abort method of the XMLHttpRequest object instance

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
</body>
<script>
//原生ajax的语法    
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test?username=zs&age=20", true);
xhr.onload = function(){
    console.log(xhr.responseText)
}
xhr.send();
//在谷歌浏览器的低速3g下面测试
//通过XMLHttpRequest实例的abort方法取消请求
setTimeout(() => xhr.abort(), 100);
</script>
</html>

Preview

axios cancels the request

Cancel the request through the

CancelToken object instancecancel method of axios

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
</head>
<body>
</body>
<script>
/*axios的取消的语法*/
// 方式1-通过axios.CancelToken.source产生cancelToken和cancel方法
/*
const source =  axios.CancelToken.source();
axios.get(&#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;, {
    params: {username: &#39;zs&#39;, age: 20}, 
    cancelToken: source.token
}).then(res=>{
    console.log(&#39;res&#39;, res.data)
}).catch(err=>{
    console.log(&#39;err&#39;, err)
})
//在谷歌浏览器的低速3g下面测试
//通过调用source的cancel方法取消
setTimeout(() => source.cancel(), 100);
*/

/**/
// 方式2-通过new axios.CancelToken产生cancelToken和cancel方法
let cancelFn 
const cancelToken =  new axios.CancelToken(cancel=>{
    cancelFn = cancel
});
axios.get(&#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;, {
    params: {username: &#39;zs&#39;, age: 20}, 
    cancelToken: cancelToken
}).then(res=>{
    console.log(&#39;res&#39;, res.data)
}).catch(err=>{
    console.log(&#39;err&#39;, err)
})
//在谷歌浏览器的低速3g下面测试
//通过调用cancelFn方法取消
setTimeout(() => cancelFn(), 100);

</script>
</html>

Preview

(c) Code

Step 1 - Cancel duplicate requests via axios request interceptor

via

axios Request interceptor, puts the request information and the request cancellation method into a map object before each request, and determines whether there is already a request for the request information in the map object. If there is a request to cancel the upload,

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script>

</head>
<body>
    <div id="app">
        <button @click="onClick1" ref="btn1">请求1</button>
        <button @click="onClick2" ref="btn2">请求2</button>
    </div>
</body>
<script>
//存储请求信息和取消方法的的map对象    
const pendingRequest = new Map();  
//根据请求的信息(请求方式,url,请求get/post数据),产生map的key
function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}   
//请求拦截器
axios.interceptors.request.use(
    function (config) {
    //根据请求的信息(请求方式,url,请求get/post数据),产生map的key
    let requestKey = getRequestKey(config)
    //判断请求是否重复
    if(pendingRequest.has(requestKey)){
        //取消上次请求
        let cancel = pendingRequest.get(requestKey)
        cancel()
        //删除请求信息
        pendingRequest.delete(requestKey) 
    }
    //把请求信息,添加请求到map当中
    // 生成取消方法
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
        // 把取消方法添加到map
        if (!pendingRequest.has(requestKey)) {
            pendingRequest.set(requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return Promise.reject(error);
  }
);
let sendPost = function(data){
    return axios({
        url: &#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}
new Vue({
    el: &#39;#app&#39;,
    mounted() {
        this.$refs.btn1.click()
        this.$refs.btn2.click()
    },
    methods: {
        // 使用lodash对请求方法做防抖
        //这里有问题,只是对每个按钮的点击事件单独做了防抖,但是两个按钮之间做不到防抖的效果
        onClick1: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求1的结果&#39;, res.data)
        },
        onClick2: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求2的结果&#39;, res.data)
        },
    },
})
</script>
</html>

Preview

Step 2 - Process the request successfully through the axios response interceptor

After the request is successful through the response interceptor of

axios In the map object, delete the data of the request information

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script>

</head>
<body>
    <div id="app">
        <button @click="onClick1" ref="btn1">请求1</button>
        <button @click="onClick2" ref="btn2">请求2</button>
    </div>
</body>
<script>
//存储请求信息和取消方法的的map对象    
const pendingRequest = new Map();  
//根据请求的信息(请求方式,url,请求get/post数据),产生map的key
function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}   
//请求拦截器
axios.interceptors.request.use(
    function (config) {
    //根据请求的信息(请求方式,url,请求get/post数据),产生map的key
    let requestKey = getRequestKey(config)
    //判断请求是否重复
    if(pendingRequest.has(requestKey)){
        //取消上次请求
        let cancel = pendingRequest.get(requestKey)
        cancel()
        //删除请求信息
        pendingRequest.delete(requestKey) 
    }
    //把请求信息,添加请求到map当中
    // 生成取消方法
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
        // 把取消方法添加到map
        if (!pendingRequest.has(requestKey)) {
            pendingRequest.set(requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return Promise.reject(error);
  }
);

//响应拦截器
axios.interceptors.response.use(
  (response) => {
        //请求成功
        //删除请求的信息
        let requestKey = getRequestKey(response.config)
        if(pendingRequest.has(requestKey)){
            pendingRequest.delete(requestKey)   
        }
        return response;
   },
   (error) => {
        return Promise.reject(error);
   }
);
let sendPost = function(data){
    return axios({
        url: &#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}
new Vue({
    el: &#39;#app&#39;,
    mounted() {
        this.$refs.btn1.click()
        this.$refs.btn2.click()
    },
    methods: {
        // 使用lodash对请求方法做防抖
        //这里有问题,只是对每个按钮的点击事件单独做了防抖,但是两个按钮之间做不到防抖的效果
        onClick1: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求1的结果&#39;, res.data)
        },
        onClick2: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求2的结果&#39;, res.data)
        },
    },
})
</script>
</html>

Preview

步骤3-通过axios响应拦截器处理请求失败

通过axios的响应拦截器,在请求失败后在map对象当中,删除该请求信息的数据

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script>

</head>
<body>
    <div id="app">
        <button @click="onClick1" ref="btn1">请求1</button>
        <button @click="onClick2" ref="btn2">请求2</button>
    </div>
</body>
<script>
//存储请求信息和取消方法的的map对象    
const pendingRequest = new Map();  
//根据请求的信息(请求方式,url,请求get/post数据),产生map的key
function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}   
//请求拦截器
axios.interceptors.request.use(
    function (config) {
    //根据请求的信息(请求方式,url,请求get/post数据),产生map的key
    let requestKey = getRequestKey(config)
    //判断请求是否重复
    if(pendingRequest.has(requestKey)){
        //取消上次请求
        let cancel = pendingRequest.get(requestKey)
        cancel()
        //删除请求信息
        pendingRequest.delete(requestKey) 
    }
    //把请求信息,添加请求到map当中
    // 生成取消方法
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
        // 把取消方法添加到map
        if (!pendingRequest.has(requestKey)) {
            pendingRequest.set(requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return Promise.reject(error);
  }
);
//删除请求信息
function delPendingRequest(config){
    let requestKey = getRequestKey(config)
    if(pendingRequest.has(requestKey)){
        pendingRequest.delete(requestKey)   
    } 
}
//响应拦截器
axios.interceptors.response.use(
  (response) => {
        //请求成功
        //删除请求的信息
        delPendingRequest(response.config)
        return response;
   },
   (error) => {
        //请求失败
        //不是取消请求的错误
        if (!axios.isCancel(error)){
            //服务器报400,500报错,删除请求信息
            delPendingRequest(error.config || {})
        } 
        return Promise.reject(error);
   }
);
let sendPost = function(data){
    return axios({
        url: &#39;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test&#39;,
        method: &#39;post&#39;,
        data
    })
}
new Vue({
    el: &#39;#app&#39;,
    mounted() {
        this.$refs.btn1.click()
        this.$refs.btn2.click()
    },
    methods: {
        // 使用lodash对请求方法做防抖
        //这里有问题,只是对每个按钮的点击事件单独做了防抖,但是两个按钮之间做不到防抖的效果
        onClick1: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求1的结果&#39;, res.data)
        },
        onClick2: async function(){
            let res = await sendPost({username:&#39;zs&#39;, age: 20})
            console.log(&#39;请求2的结果&#39;, res.data)
        },
    },
})
</script>
</html>

预览

(学习视频分享:vuejs入门教程编程基础视频

The above is the detailed content of Let's talk about how vue prevents repeated requests in this article. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete