この記事では、Vue がリクエストの繰り返しをどのように防ぐかについて説明します。

青灯夜游
リリース: 2023-01-29 09:22:18
転載
2898 人が閲覧しました

vue はどのようにしてリクエストの繰り返しを防ぐのでしょうか?次の記事では、Vue でリクエストの繰り返しを防ぐ 2 つの方法を紹介します。

この記事では、Vue がリクエストの繰り返しをどのように防ぐかについて説明します。

プロジェクトのフロントエンド コードでは、同じリクエストがサーバーに複数回送信される状況が発生します。サーバー リソースの無駄を避ける必要があります。同じリクエストは次のとおりです。リクエスト

アイデア

(1) 複数のクリックを防ぐなど、単純なビジネスの場合同じボタンを押すと、手ぶれ補正処理にタイマーを使用できます
(2) ビジネスが複雑な場合、たとえば、複数のコンポーネントがコードを通過し、同じリクエストが複数回送信される場合、手ぶれ補正は機能しません。現時点では、繰り返される Ajax リクエストを統一した方法でキャンセルするのが最善です。

実装

方法 1 - アンチ- タイマーによるシェイク処理

#(a) 概要

効果: ユーザーが同じボタンを複数回クリックした場合連続して、最後のクリックの後、短時間後にリクエストが開始されます
原則: メソッドが呼び出されるたびに、最後にタイマーが生成されます。タイマーの期限が切れると、リクエストはメソッドが繰り返し呼び出された場合、現在のタイマーはキャンセルされ、新しいタイマーが作成され、タイマーが終了した後にリクエストが送信されます。作業中にサードパーティのカプセル化されたツールの機能を使用できます。たとえば、lodashdebounce メソッドは、手ぶれ補正コードを簡素化するために使用されます [関連する推奨事項: vuejs ビデオ チュートリアル web フロントエンド開発]

(b)コード
<!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)プレビュー

接続# #(d)既存の問題

次の 2 つの状況など、複数のボタン コンポーネントに対するリクエストを繰り返し送信する問題を解決できません

#クリック イベント時の手ぶれ補正の状況

ボタンイベントは互いに独立しています はい、異なるメソッドが呼び出され、ボタン間の手ぶれ補正効果は達成できません

<!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

ケース 2- アンチインターフェイスメソッドの -shake

ボタン間で呼び出されるメソッドは同じです メソッドに対して手ぶれ補正処理を行うことは可能ですが、処理自体がメソッドを一度カプセル化するため、前のメソッドの戻り値。変更するには、前のメソッドでさらに処理を行う必要があります。より複雑です。

非推奨

<!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

##方法 2 - ajax リクエストをキャンセルする

(a) 概要 リクエスト メソッドを直接処理し、API を通じて繰り返しリクエストをキャンセルします。 ajax ライブラリのメソッド

(b) 原則

ネイティブ ajax キャンセル リクエスト

abort

メソッドを呼び出してリクエストをキャンセルします。 XMLHttpRequest

オブジェクト インスタンス<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset=&quot;UTF-8&quot;&gt; &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt; &lt;title&gt;Document&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;/body&gt; &lt;script&gt; //原生ajax的语法 let xhr = new XMLHttpRequest(); xhr.open(&quot;GET&quot;, &quot;http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test?username=zs&amp;age=20&quot;, true); xhr.onload = function(){ console.log(xhr.responseText) } xhr.send(); //在谷歌浏览器的低速3g下面测试 //通过XMLHttpRequest实例的abort方法取消请求 setTimeout(() =&gt; xhr.abort(), 100); &lt;/script&gt; &lt;/html&gt;</pre><div class="contentsignin">ログイン後にコピー</div></div>Preview

axios はリクエストをキャンセルします

CancelToken

オブジェクト インスタンスを通じてリクエストをキャンセルします

axios

## の cancel メソッド #

<!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) コード

ステップ 1 - キャンセルaxios リクエスト インターセプタを介してリクエストを複製します。

via

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://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

ステップ 2 - axios レスポンス インターセプターを通じてリクエストを正常に処理します

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://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入门教程编程基础视频

以上がこの記事では、Vue がリクエストの繰り返しをどのように防ぐかについて説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート