Vue의 REST API에서 데이터를 가져올 수 있는 조합 가능한 함수를 작성합니다.js
40506 단어 vuetypescript
우리가 어떤 요구를 해야 할지 정의하는 것부터 시작합시다.필터, 페이지 나누기, 정렬...어쨌든 이것은 우리의 문제가 아니니, 우리 나중에 다시 처리하자.
단순 URL 탐지기
우선, 우리는 데이터가 업데이트될 때 데이터를 다시 불러올 간단한 URL 탐지기를 만들 것이다
const useUrlQuery = <T>(url: string | Ref<string>) => {
const result = ref<T | null>(null);
const reload = async () => {
const res = await fetch(unref(url));
const data = await res.json();
result.value = data;
};
if (isRef(url)) { // if url is reactive
watch(url, reload);
}
reload();
return {
result,
reload,
};
};
우리는 완성했다.다음과 같이 간단히 사용할 수 있습니다.setup () {
const page = ref(0);
const url = computed(() => `/items?page=${page.value}`);
const { result: list } = useUrlQuery<ItemType[]>(url);
return {
list,
page
};
}
우리가 제공한 url
는string이거나string을 포함하는reactive입니다.computed
는 수동적이며 Ref<T>
와 호환됩니다. set
가 실현되지 않으면 그 값은 읽기만 하기 때문에 ref
와 computed
를 각각 입력할 필요가 없습니다.만약 우리가 T | Ref<T>
을 입력한다면, 이것은 그것이 수동적일 수도 있지만, 반드시 수동적일 수는 없다. 우리가 그 값을 사용하려면 unref(val)
속성에 들어가려고 하는 것이 아니라 .value
속성을 사용해야 한다. (컴퓨터나watch에서 사용하면 수동적이라면 의존항으로 간주될 것이다.)잠시 후, 우리는 url
가 반응적인지 확인하고, 만약 그렇다면 관찰자를 설치합니다.그렇기 때문에 우리는 마지막에 원어를 보았다. 이것은 원어가 바뀌지 않으면 작동하지 않는다는 것을 의미한다.여기서
result
이름을 list
로 변경합니다.JavaScript를 사용하여 필드의 구조를 해제하고 이름을 바꿉니다.이렇게 하면 우리는 어떤 명칭 충돌도 피할 수 있다. 이것은 믹스에서 발생할 수 있다.언제든지
page
가 바뀌면 다시 계산url
하고 터치watch(url, reload)
한다.초기 상태에서result
는 null
가 될 것이기 때문에 기본값T | null
을 추가해야 유형isLoading
을 피할 수 있습니다.그리고 진행 중인 요청이 있는지 알고 싶습니다.상태 및 error
:const useUrlQuery = <T, K = T | null>(
url: string | Ref<string>,
initial: K
) => {
const result = ref<K>(initial);
const state = reactive({
isLoading: false,
error: null as any,
});
const reload = async () => {
state.isLoading = true;
try {
const res = await fetch(unref(url));
const data = await res.json();
result.value = data;
} catch (e) {
state.error = e;
} finally {
state.isLoading = false;
}
};
if (isRef(url)) {
watch(url, reload);
}
return {
...toRefs(state),
result,
reload,
};
};
K
가 T
인 경우 TypeScript는 제공된 유형 이외의 다른 유형을 고려하지 않기 때문에 이 유형을 수동으로 작성할 필요가 없습니다. (null
이 기본 형식이라고 가정할 수도 있습니다. 목록의 끝점이라면 빈 그룹을 쉽게 전달하고 그룹 형식 (([] as ItemType[]
으로 변환하거나 자리 차지 문자 대상을 놓을 수 있습니다.현재 우리는
isLoading
를 사용하여 사용자에게 그들이 본 목록이 아직 업데이트되지 않았음을 알릴 수 있다.당신이 이 사례를 위해 무엇을 설계했든지 간에, 목록을 숨길 수 있는 마이크로 모뎀을 추가할 수 있습니다.setup () {
const { result: list, isLoading, error } = useUrlQuery('/items', [] as ItemType[]);
return {
list,
isLoading,
error,
}
}
팁: URL의 페이지 번호
const route = useRoute();
const url = computed(() => `/items/?page=${route.params.page}`);
지키다
그러나 때로는 요청이 더 길고, 때로는 오래된 검색이 끝나기 전에 새로운 검색의 응답을 받을 수도 있습니다.예를 들어 필터를 삭제할 때나 페이지와 백엔드 사이를 빠르게 전환할 때 필터를 얻는 데 더 많은 시간이 걸린다.예를 들어 우리는 첫 페이지에 있다.우리는 "2페이지"를 클릭했다.그것은 불러오기 시작했지만, 완성되기 전에, 우리는 3위로 뛰어올랐다.세 번째 페이지는 요소가 하나밖에 없기 때문에 백엔드는 두 번째 페이지에 대한 요청을 완성하기 전에 응답을 한다.Composable는 3페이지에 데이터를 설정하고 엉망입니다.두 번째 페이지의 응답이 왔습니다. 이전에 터치한
reload
을 터치하고 결과를 무효한 페이지의 결과로 바꿉니다.우리가 있는 이곳에는 거의 방법이 없다.만약 우리가 native
fetch
를 사용한다면 우리는 AbortController
를 사용할 수 있다.이것은 간단한 변화이다. let controller: AbortController | null = null;
const reload = async () => {
controller?.abort();
state.isLoading = true;
try {
controller = new AbortController();
const res = await fetch(unref(url), { signal: controller.signal });
result.value = await res.json();
state.isLoading = false;
} catch (e: any) {
if(e.name === "AbortError") {
// request was aborted
return;
}
// will set error and disable `isLoading` only if it wasn't aborted
state.isLoading = false;
state.error = e;
}
};
이렇게!그러나 우리가 본기Fetch를 사용할 때 상황은 이렇다.또한 finally
섹션을 삭제해야 합니다. (중지) 상황이 있기 때문에 isLoading
를 false
로 설정하지 않았습니다. Axios를 사용하려면 그 방법이 필요합니다.개발자가 상태를 되돌릴 수 있도록 비동기 함수를 제공할 수도 있습니다. 이 경우 요청을 중단하거나 잘못된 결과를 저장하지 않도록 함수를 강제로 제공해야 합니다.try {
const forUrl = url.value;
// fetch
const data = await res.json();
****if (url.value == forUrl) {
result.value = data; //set only if url is still the same
}
**} //...**
이 해결 방안이 있으면 우리는 실제 URL을 감청할 수 있으며, 그것이 바뀌지 않은 상황에서만 우리는 결과를 받아들일 수 있다.1페이지에서 2페이지로 전환한 다음 3페이지로 전환한 다음 2페이지로 되돌아가면 이전의 응답도 받아들일 것입니다.2페이지의 첫 번째 요청이 2페이지에서 실제로 선택되었을 때 완료되면 이 응답을 받아들인다. (마지막 응답도 받아들일 수 있지만 데이터베이스에 변경 사항이 없으면 변경되지 않는다.)이것은 캐시가 우리가 쉽게 실현할 수 있는 마지막 몇 가지 결과를 초래할 수도 있다.
URL 생성기
url 변경을 감청하고 로컬 상태를 업데이트할 수 있는 구성 요소가 생겼으니 작업을 시작해야 합니다.수동으로 URL을 구축하면 혼란스러울 수 있습니다. 이것이 바로
URLSearchParams
의 용도입니다.객체에 따라 질의 매개변수를 생성하는 생성기를 구현합니다.type ParamType = string | number | boolean;
type ParamsType = Record<string, ParamType>;
export const useParamBuilder = (params: ParamsType | Ref<ParamsType>) => {
return computed(() => {
const search = new URLSearchParams();
const obj = unwrap(params); // unwrap in case if it's Ref
const entries = Object.entries(obj)
.filter(e => e[1] != null)
.filter(e => typeof e[1] == "string" ? e[1].length > 0 : true)
.sort(([a], [b]) => a > b ? 1 : a < b ? -1 : 0);
entries.forEach(([key, value]) => search.append(key, value.toString()));
return search
});
}
export const useUrlParams = (base: string, params: Readonly<Ref<URLSearchParams>>) => {
return computed(() => `${base}?${params.value.toString()}`);
}
여기서, 실현은 그리 중요하지 않다. 우리는 단지 문자열, 숫자, 브리 값을 검색에 넣을 뿐이다.우선, 우리는 빈 문자열과 빈 문자열을 필터한 다음, 알파벳 순서에 따라 항목을 정렬하여, 같은 순서로 배열하도록 한다. (중요한 것은 watch
차이를 정확하게 알아차리는 것이다.const filter = ref<ItemFilters>({
active: false,
search: "", //will be filtered out if empty,
category_id: null, //filtered out if null
});
const { result: list } = useUrlQuery(
useUrlParams('/items', useParamBuilder(fitler)),
[]
);
return {
list
}
물론 단독으로 사용하지 않으면 이 두 구성 요소를 하나로 합칠 수 있다.export const useUrlParams = (base: string, params: Ref<Record<string, string|number|boolean>) => {
return computed(() => {
const search = new URLSearchParams();
const obj = params.value;
const entries = Object.entries(obj)
.filter(e => e[1] != null)
.filter(e => typeof e[1] == "string" ? e[1].length > 0 : true)
.sort(([a], [b]) => a > b ? 1 : a < b ? -1 : 0);
entries.forEach(([key, value]) => search.append(key, value.toString()));
return `${base}?${params.value.toString()}`
});
}
// in setup:
const { result: list } = useUrlQuery(
useUrlParams('/items', fitler),
[]
);
이를 통해 REST API에서 데이터를 가져오는 거의 모든 기능을 하나의 조합 가능한 함수에 패키지화할 수 있습니다.예제 업무 목록 구성 요소
<template>
<div>
<h1>TODOS:</h1>
<input :value="filters.search" @input="setSearch" placeholder="search" />
<ul class="todo-list" :class="{ 'is-loading': isLoading }">
<li class="todo-item" v-for="item in list" :key="item.id">
<input type="checkbox" :value="item.done" @change="setDone(item)" />
{{ item.text }}
</li>
</ul>
<button @click="reload">Reload</button>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, computed } from "vue";
import { useUrlParams, useUrlQuery } from "@/composables/useApiRequest";
import debounce from "lodash/debounce";
type TodoItem = {
id: number;
text: string;
done: boolean;
};
const defaultPlaceholder = [
{ id: 1, text: "Perform fetch", done: false },
] as TodoItem[]
export default defineComponent({
name: "TodoListComponent",
setup() {
const state = reactive({
loading: false,
});
const filters = reactive({
done: false,
search: "",
});
const url = useUrlParams("/todos", filters);
const { result: list, reload, isLoading: listLoading } = useUrlQuery(url, defaultPlaceholder);
const setDone = async (item: TodoItem) => {
state.loading = true;
try {
const response = await fetch(`/todos/${item.id}`, {
method: "PATCH",
body: JSON.stringify({ done: !item.done }),
});
await response.json(); // ignore response
await reload();
} catch (e) {
console.error(e);
} finally {
state.loading = false;
}
};
const setSearch = debounce((event: InputEvent) => {
filters.search = (event.target as HTMLInputElement).value;
}, 200); // prevent reloading after each character
return {
isLoading: computed(() => listLoading.value || state.loading),
filters,
list,
reload,
setDone,
setSearch,
};
},
});
</script>
<style scoped>
.is-loading {
opacity: 0.5;
pointer-events: none;
}
.todo-list {
margin: 0;
padding: 0;
}
.todo-item {
cursor: pointer;
}
</style>
코드는 레이아웃에 주목하지 않고 서면으로 조합할 수 있는 예시적인 용법에만 주목한다.이 문서는 URL을 수신하는 GET 요청에만 초점을 맞춥니다.결론
새 Vue의 조합 API가 있으면 구성 요소 외부에서 논리를 위임하고 다시 사용할 수 있으며, 명칭 충돌에 신경 쓸 필요가 없다. 마치 믹스에서 발생한 충돌처럼 매개 변수화 조합성도 믹스보다 훨씬 쉽다.그것은 간단하고 안전하며 개발 속도를 가속화시켰다.이것은 단지 하나의 예일 뿐입니다. 다음에 이 새로운 API를 사용하는 예를 더 많이 제공할 것입니다.
Reference
이 문제에 관하여(Vue의 REST API에서 데이터를 가져올 수 있는 조합 가능한 함수를 작성합니다.js), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/razi91/writing-a-composable-function-to-fetch-data-from-rest-api-in-vue-js-4957텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)