programing

동일한 이미지에서 불필요한 http 요청 방지 - vuejs

goodsources 2022. 8. 14. 12:26
반응형

동일한 이미지에서 불필요한 http 요청 방지 - vuejs

상황:

페이지에는 사용자 목록을 수신하는 여러 구성 요소가 있습니다.목록을 받은 후 사용자의 이미지를 가져오기 위해 추가 구성 요소를 호출하는 foreach 사이클이 있습니다.여러 구성 요소가 동일한 사용자를 포함할 수 있습니다. 즉, "반복된 이미지"를 가져오기 위해 동일한 http 요청을 반복해야 합니다.이러한 불필요한 요구를 피하기 위해 vueX 스토어에 특정 base64 이미지가 있는 사용자의 정보를 설정하여 이미지를 이미 받았는지 여부를 검증합니다.

문제:첫 번째 컴포넌트가 이미지 가져오기 및 스토어에 저장 요청을 했을 때 나머지 컴포넌트는 이미 작성되어 있기 때문에 스토어가 비어 있기 때문에 이미지가 있는지 확인할 수 없습니다.

솔루션:컴포넌트를 생성할 때 다음을 사용하여 스토어가 존재하도록 강제합니다.

this.images[this.user.id] = 'reserved'; 

하지만 이것이 이 상황에 대한 올바른 접근법인지 잘 모르겠습니다.제안 접수 :'D'

코드:

부모 성분

<template>
    <div class="info-cards">
        <div class="info-users">
            <div class="info-label">{{ $t('global.users') }}</div>
            <div class="info-images"  v-if="users.length > 0">
                <base-users-image
                    v-for="user in users"
                    :key="user.name"
                    :user="user"
                />
            </div>
            <div v-else class="message">{{ $t('global.noUsersRole') }}</div>
        </div>
    </div>
</template>

<script>
    // import components
    const baseUsersImage = () => System.import(/* webpackChunkName: 'usersImage' */ './../../users/baseUsersImage');

    export default {
        props: {
            users: Array,
            packages: Array
        },
        components: {
            baseUsersImage: baseUsersImage
        },
    }
</script>

이미지 컴포넌트

<template>
    <router-link to="user" class="anchor-image">
        <img v-if="show" :src="image" :alt="user.name" class="image">
        <div v-else class="image-default">t</div>
    </router-link>
</template>

<script>
    // import requests
    import requests from './../../../helpers/requests.js';

    // import store
    import { mapGetters, mapActions } from 'vuex';

    export default {
        props: {
            user: Object
        },
        data() {
            return {
                image: '',
                show: false
            }
        },
        created() {
            if (this.user.avatar) { // check if user has avatar
                if ( this.images[this.user.id] == null) { // check if it already exists in the store
                    this.images[this.user.id] = 'reserved'; // set as reserved in store
                    requests.get(this.user.avatar, { responseType: 'arraybuffer' }) // faz o pedido a API da image
                        .then( (response) => {
                            this.saveImage( { id: this.user.id, url: `data:${response.headers['content-type']};base64,${Buffer.from(response.data, 'binary').toString('base64')}` } );
                        }, error => {
                            console.log(error);
                        });
                }
            }
        },
        methods: {
            ...mapActions({
                saveImage: 'saveImage'
            })
        },
         computed: {
            ...mapGetters({
                images: 'images'
            })
        },
        watch: {
            images:  {
                immediate: true,
                deep: true, // so it detects changes to properties only
                handler(newVal, oldVal) {
                    if ( newVal[this.user.id] !=='reserved'
                        && this.user.avatar
                        && newVal[this.user.id] !== undefined
                    )  {
                        this.image = newVal[this.user.id];
                        this.show = true;
                    }
                }
            }
        }
    }
</script>

가게

const state = {
    images: {}
}

const SAVE_IMAGE = (state, payload) => {
    state.images = {
        ...state.images,
        [payload.id] : payload.url
    }
}

const saveImage = ({commit}, payload) => {
    commit('SAVE_IMAGE', payload);
}

제가 할 일은 다음과 같습니다.

우선 모든 요청 로직을 VueX로 이동하고 컴포넌트를 최대한 단순하게 유지합니다.이것은, 다음의 몇개의 코드로 실현 가능합니다.

export default {
    props: {
        user: Object
    },
    created () {
        if (this.user.avatar) {
            this.$store.dispatch('fetchImage', this.user.avatar)
        }
    }
}

그럼 이 간단한 패턴으로 가게를 정리하겠습니다.먼저, 상태가 어떻게 표시되는지 살펴보겠습니다.

{
    images: {
        '/users/1/avatar': 'data:png:base64,....', // An image that have been loaded
        '/users/2/avatar': null // An image that is supposed to be loading
    }
}

보다시피imagesobject는 이미지 URL을 키로 사용하고 base64 데이터를 값으로 사용합니다.데이터 값이 null이면 이미지가 이미 로드 중임을 의미합니다.

이제 이를 처리하기 위한 작업을 어떻게 작성해야 하는지 알아보겠습니다.

const actions = {
    fetchImage ({state, commit}, url) {
        if (typeof state.images[url] !== 'undefined') {
            return null
        }

        commit('setImage', {
            url,
            payload: null
        })

        return requests.get(url, { responseType: 'arraybuffer'}).then(response => {
            commit('setImage', {
                url,
                payload: `data:${response.headers['content-type']};base64,${Buffer.from(response.data, 'binary').toString('base64')}`
            })
        })
    }
}

첫 번째 조건을 보세요.이미지가 아닌 경우undefined가게에서는 아무것도 하지 않아요.왜냐하면 이미지가 그렇지 않으면undefined, 이것은, 어느쪽인가를 의미합니다.null(로드) 또는 값이 있고 로드됩니다.

이 조건 직후에 이미지를null다른 컴포넌트가 이미지를 로드하지 않도록 합니다.

마지막으로 이미지의 내용을 로드하여 상태에 커밋합니다.

이제 템플릿을 살펴보겠습니다.

<template>
    <router-link to="user" class="anchor-image">
        <img v-if="$store.state.images[user.avatar]" :src="$store.state.images[user.avatar]" :alt="user.name" class="image">
        <div v-else class="image-default">t</div>
    </router-link>
</template>

이미지를 표시해야 하는지 확인하려면v-if="$store.state.images[user.avatar]"이미지가 로드되는 즉시 표시됩니다.

$store.state.images[user.avatar]가 「라고 해도, .loading에는 (이것)이 .nulldiscloss.discloss 。

도움이 됐으면 좋겠네요!

(이것이 전체 스토어입니다.)

const store = {
    state: {
        images: {}
    },
    mutations: {
        setImage (state, image) {
            Vue.set(state.images, image.url, image.payload)
        }
    },
    actions: {
        fetchImage ({state, commit}, url) {
            if (state.images[url] !== undefined) {
                return null
            }

            commit('setImage', {
                url,
                payload: null
            })

            return requests.get(url, { responseType: 'arraybuffer'}).then(response => {
                commit('setImage', {
                    url,
                    payload: `data:${response.headers['content-type']};base64,${Buffer.from(response.data, 'binary').toString('base64')}`
                })
            })
        }
    }
}

언급URL : https://stackoverflow.com/questions/53171358/avoid-unnecessary-http-requests-on-identical-images-vuejs

반응형