on
무한대로 스크롤하자!
무한대로 스크롤하자!
사료 운동. 현재 대부분의 앱은 사용자의 흥미를 유지하기 위해 일종의 무한 스크롤 가능 피드를 가지고 있습니다.
여기서 개발할 프로젝트는 Elixir로 작성되었으며 Phoenix Framework와 라이브뷰를 사용하여 웹 앱을 만듭니다.
그 이유
Jascrafts는 제가 아내를 위해 만든 뜨개질 앱 프로젝트 기반입니다. 프로젝트를 완료할 때 사용자(제 아내와 아내 친구)는 완료된 프로젝트에 대한 데이터를 추가하고 선택적으로 이미지를 추가할 수 있습니다. 또한 다른 사용자가 볼 수 있는 피드에서 사진을 공유하도록 선택할 수 있습니다.
피드의 사용이 늘어남에 따라, 그 목록이 길어질 것이기 때문에, 제가 모든 프로젝트를 끌어내서 보여줄 수는 없다는 것이 분명해졌습니다. 그리고 다음 페이지 버튼을 클릭해야 하는 페이지 솔루션도 싫어요.
따라서: 무한 스크롤! 다행히도, 엘릭서는 매우 실용적인 언어이고, 그러한 특징을 추가하는 것은 그리 어렵지 않을 것이다.
뒷쪽 끝
제가 했던 첫번째 질문은 이런 것이었습니다.
defp feed_query() do from p in ProjectDetail, where: p.is_public == true, join: sp in assoc(p, :project), order_by: [desc: sp.finished_at], preload: [:project] end
프런트 엔드에서 렌더링할 때는 단순한 루프에 지나지 않으며 각 요소를 보기에 따라 렌더링합니다.
백엔드 페이지화의 기본 개념은 페이지 가치의 데이터 + 1 요소를 가져오는 것입니다.
def feed(%Jascrafts.Feed.Pagination{ page: page, pr_page: pr_page }) do data = feed_query(page, pr_page) |> Repo.all() has_next_page = Enum.count(data) == pr_page + 1 %{page: Enum.take(data, pr_page), has_next: has_next_page} end defp feed_query(page, pr_page) do from p in ProjectDetail, where: p.is_public == true, join: sp in assoc(p, :project), order_by: [desc: sp.finished_at], offset: ^((page - 1) * pr_page), limit: ^pr_page + 1, preload: [:project] end
먼저 피드 쿼리를 살펴봅시다. 이제 오프셋과 제한이 있습니다. 오프셋 파트 페이지 -1 * pr_page는 데이터베이스의 특정 지점에서만 데이터를 추출할 수 있도록 합니다. 한계는 pr_page + 1로 설정됩니다.
내가 12페이지가 있다고 가정해 보자. >12개의 요소를 가져올 수 있다면, 다음 페이지가 1개의 요소만 있더라도 적어도 한 페이지의 데이터가 더 있다는 것을 안다. <= 12개 요소를 제거하면 마지막 페이지에 있는 것을 알 수 있습니다.
그 논리로 우리는 이 지식을 피드 함수에 구성할 수 있다.
맨 앞 쪽
여기가 끈적거리는 곳입니다. 모를 경우 Phoenix 앱은 서버가 렌더링됩니다.
여기서 우리의 목표는 페이지 끝의 프런트 엔드 이벤트를 듣고, 특정 스크롤 위치에 있을 때 더 많은 요소를 가져오는 것이다. 하지만 저는 이 논리를 전면에 내세우고 싶지는 않습니다.
라이브뷰 훅이 방법입니다. 후크를 설정하려면 프런트엔드 수신기를 만들기 위한 자바스크립트가 필요합니다.
자바스크립트 상호운용성
let Hooks = {}; let scrollAt = () => { let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight; let clientHeight = document.documentElement.clientHeight; return (scrollTop / (scrollHeight - clientHeight)) * 100; }; Hooks.InfiniteScroll = { page() { return this.el.dataset.page; }, mounted() { this.pending = this.page(); window.addEventListener("scroll", (e) => { if (this.pending == this.page() && scrollAt() > 90) { this.pending = this.page() + 1; this.pushEvent("load-more", {}); } }); }, updated() { this.pending = this.page(); }, };
위의 그림은 렌더링된 HTML에 있는 내 피드의 컨테이너입니다. 데이터 페이지 필드를 보십시오. 이것은 위의 자바스크립트와 함께 그것을 접착하는 것이며, 스크롤At 위치가 90%에 도달하면 그것은 더 많은 부하를 트리거하고 라이브뷰 웹 소켓 연결을 통해 그 이벤트를 푸시할 것이다.
백엔드에서 이 이벤트를 수신하려면 handle_event 함수를 구현해야 합니다.
```js
@impl Phoenix.LiveView
def handle_event("load-more", _, %{assigns: assigns} = socket) do
{:noreply, socket |> assign(page: assigns.page + 1) |> fetch()}
end
defp fetch(%{assigns: %{page: page, pr_page: per, has_next: true} = socket) do
%{page: projects, has_next: has_next} = Feed.feed(%Pagination{page: page, pr_page: per})
assign(socket, projects: projects, has_next: has_next)
end
defp fetch(socket) do
socket
end
```
여기 많은 것들이 있고, 그 중 일부는 독자들에게 이해시킬 수 있도록 남겨두겠습니다. 중요한 부분은 핸들 이벤트 함수가 앞서 작성한 JS 스니펫을 통해 전송되는 로드 추가 이벤트에 트리거한다는 것입니다. 이제 우리는 엘릭서 땅에 돌아왔으니, 우리가 원하는 모든 똑똑한 것들을 할 수 있다. 이 경우 추가 데이터 페이지를 가져와 LiveView 소켓 연결을 통해 다시 전송합니다. 여기서 멋진 점은 무한 스크롤 트리거가 데이터와는 아무런 관련이 없다는 것입니다. 이 시점에서, 내가 만들고 싶은 무한한 스크롤 가능 리스트의 경우, 내가 해야 할 일은 handle_event("load-more") 함수를 구현하고 특정한 사용 사례를 수행하는 것이었다.
from http://gong-tech.tistory.com/59 by ccl(A) rewrite - 2021-09-25 04:01:29