Nhiều sinh viên viết danh sách ảo làm điểm nhấn trong sơ yếu lý lịch của mình nhưng lại không biết cách viết tay nên đây không phải là cộng mà là điểm trừ. cố định cao, nhưng trong các dự án thực tế, nhiều danh sách ảo hơn có chiều cao thay đổi Trong bài viết này, Ouyang sẽ hướng dẫn. hướng dẫn bạn phát triển danh sách ảo có chiều cao thay đổi. Ảo có chiều cao cố định trước khi đọc bài viết này để có kết quả tốt hơn.
Âu Dương cũng đang tìm việc, mời tham khảo Thành Đô .
Ý nghĩa của chiều cao thay đổi rất đơn giản, đó là không biết chiều cao cụ thể của từng hạng mục, như hình dưới đây:
Bây giờ chúng tôi đang gặp sự cố. được hiển thị trong các vùng trực tiếp dựa trên ScrollTop của thanh cuộn hiện tại, là giá trị bắt đầu.
Nếu không bắt đầu, không thể chỉ hiển thị các mục trong vùng hiển thị khi cuộn.
Vì không biết chiều cao của từng hạng mục nên chúng tôi sử dụng phương pháp ước tính chiều cao để đạt được chiều cao đó. Ví dụ:
const { listData, itemSize } = definProps({ // Liệt kê dữ liệu listData: { type: Array, default: () => [], }, // Chiều cao mục ước tính, không phải chiều cao mục thực tế itemSize: { type: Number, mặc định: 300, }, });
sau This code như sau:
const renderCount = được tính toán (() => Math.ceil(containerHeight.value / itemSize));
Lưu ý: Vì chúng tôi đang ước tính chiều cao nên số lượng renderCount không chính xác.
Nếu chiều cao ước tính cao hơn nhiều so với chiều cao thực tế thì số lượng màn hình thực tế sẽ không đủ, dẫn đến màn hình trắng hiện tại ở trang cuối cùng.
Nếu chiều cao tính toán quá nhỏ thì số lượng mục ở đây sẽ được hiển thị quá nhiều và hiệu suất sẽ không còn tốt như trước.
Vì vậy, ước tính chiều cao của mục cần được đưa ra một cơ sở hợp lý về giá trị dựa trên lý do, kinh doanh thực tế lý thuyết, nên ước tính giá trị nhỏ hơn là ước tính quá cao (nếu cao quá sẽ hiển thị màn hình trắng).
Giá trị ban đầu của phần bắt đầu là 0 và renderCount được tính toán. như sau:
const end = được tính toán (() => start.value + renderCount.value);
Như trong bài viết trước, khi tính toán phần cuối, một mục lại được hiển thị bên dưới. Lần đầu tiên lăn ra khỏi vùng hiển thị, màn hình trắng có thể xảy ra nếu bạn không thực hiện thêm phần hiển thị.
Mã như sau:
const renderList = được tính toán (() => listData.slice(start.value, end.value + 1));
Bằng cách này, chúng tôi biết các mục nào sẽ được hiển thị trong vùng trực tiếp trong quá trình khởi động, nhưng vì trước đó Ở đây chúng tôi đã ước tính chiều cao cho từng mục nên chúng tôi nên sửa đổi chiều cao giá trị này.
Để ghi lại chiều cao của từng mục trong danh sách có chiều cao thay đổi, chúng tôi cần một mảng để lưu trữ chiều cao của từng mục.
Vì chiều cao của mỗi mục được lưu trữ nên bạn cũng có thể sử dụng các trường trên cùng và dưới cùng để ghi lại vị trí bắt đầu và kết thúc của từng mục trong danh sách.
Ngoài ra, còn có trường ghi lại chỉ mục giá trị của từng mục được xác định rõ ràng như sau:
vị trí const = ref< { chỉ mục: số; chiều cao: số; trên cùng: số dưới cùng: số;
Việc khởi tạo giá trị của các vị trí là một mảng trống, vậy khi nào bạn chỉ định giá trị cho mảng này?
Vì vậy, chúng tôi xem lắng nghe listData và thêm ngay lập tức: true.
watch(() => listData, initPosition, { ngay lập tức: true, }); function initPosition() {position.value = []; , chiều cao: itemSize, top: chỉ mục * itemSize, đáy: (index + 1) * itemSize, } });
Bằng cách duyệt qua listData và kết hợp itemSize ước tính, chúng ta có thể nhận được các giá trị của trường chiều cao, trên cùng và dưới cùng trong mỗi mục.
Còn một vấn đề nữa, chúng ta cần một phần tử để mở rộng thanh cuộn. Trong danh sách ảo có chiều cao cố định, chúng ta lấy nó thông qua itemSize * listData.length. Rõ ràng điều đó không thể thực hiện được ở đây, vì mảng vị trí lưu trữ vị trí của tất cả các mục, khi đó giá trị dưới cùng của mục cuối cùng là chiều cao thực của danh sách. Trước đây nó không chính xác nhưng nó sẽ ngày càng chính xác hơn khi chúng ta sửa các giá trị ở các vị trí.
Vậy chiều cao thực sự của danh sách là:
const listHeight = được tính toán( () =>positions.value[positions.value.length - 1].bottom );
Lúc này vị trí cụ thể của từng mục đã được ghi vào mảng vị trí, mặc dù vị trí này có sai sót. Tiếp theo chúng ta cần sửa những giá trị sai này. Làm thế nào để sửa chúng?
Câu trả lời rất đơn giản, hãy sử dụng hàm hook onUpdated của Vue, hàm này sẽ được gọi sau khi cập nhật cây DOM của nó để phản hồi các thay đổi trạng thái. Nghĩa là, nó sẽ được kích hoạt sau khi renderList được đưa vào DOM! .
Tại thời điểm này, các mục này đã được hiển thị thành các nút DOM, sau đó chúng ta có thể duyệt qua các nút DOM của các mục này để có được chiều cao thực sự của từng mục. Bây giờ chúng ta đã biết chiều cao thực sự của từng mục, chúng ta có thể cập nhật phần trên cùng và phần dưới cùng của tất cả các mục bên trong. Mã này như sau:
{{ item.index }}
{{ item.value }}
Sử dụng: data-index="item.index" để liên kết chỉ mục với mục. Khi cập nhật, bạn có thể lấy chỉ mục của mục tương ứng thông qua +el.getAttribution("data-index").
itemRefs lưu trữ các phần tử DOM của tất cả các mục bằng cách duyệt qua nó, bạn có thể lấy từng mục, sau đó lấy chỉ mục và chiều cao thực của từng mục trong danh sách dài.
Giá trị của diffVal là chiều cao ước tính lớn hơn bao nhiêu so với chiều cao thực tế. Nếu giá trị của diffVal không bằng 0, điều đó có nghĩa là chiều cao ước tính không chính xác. Lúc này, bạn cần cập nhật chiều cao của mục hiện tại. Vì chiều cao chỉ ảnh hưởng đến giá trị đáy nên bạn chỉ cần cập nhật chiều cao và đáy của mục hiện tại.
Vì chiều cao của mục hiện tại đã thay đổi nên nếu giá trị của diffVal là dương, điều đó có nghĩa là chiều cao ước tính của chúng tôi quá lớn. Tại thời điểm này, chúng ta cần bắt đầu duyệt từ phần tử tiếp theo của mục hiện tại cho đến khi duyệt qua toàn bộ danh sách dài. Nếu chúng ta ước tính quá nhiều, chúng ta chỉ cần di chuyển toàn bộ các mục tiếp theo lên trên và khoảng cách di chuyển là chênh lệch ước tính diffVal.
Vì vậy, ở đây bạn cần bắt đầu duyệt qua chỉ số + 1 và trừ diffVal khỏi giá trị trên cùng và dưới cùng của tất cả các phần tử được duyệt qua.
Tất cả các mục được hiển thị trong vùng hiển thị đều được duyệt qua, chiều cao và vị trí của từng mục được sửa, đồng thời sửa phần trên và dưới của các mục không được hiển thị sau đó để chiều cao được cập nhật. Về mặt lý thuyết, nếu bạn cuộn từ đầu đến cuối, tất cả các vị trí và độ cao trong toàn bộ danh sách dài sẽ được sửa.
Thông qua việc điều chỉnh giá trị chiều cao ước tính trước đó, chiều cao và vị trí của mục được hiển thị sẽ được điều chỉnh. Tại thời điểm này, chúng ta cần cách tính vị trí bắt đầu mới và giá trị offset sau khi cuộn.
Nó vẫn tương tự như việc thiết lập chiều cao. Khi thanh cuộn cuộn ở giữa mục, thanh cuộn của trình duyệt sẽ được sử dụng lại. Khi cuộn từ mục này sang mục khác, giá trị bắt đầu và giá trị offset cần được cập nhật. Nếu bạn không hiểu câu này, tôi khuyên bạn trước tiên nên đọc bài viết trước của tôi về cách triển khai danh sách ảo có chiều cao cố định.
Chúng ta nên tính giá trị bắt đầu mới nhất tại thời điểm này như thế nào?
Rất đơn giản! Có hai trường được lưu trữ ở các vị trí, trên cùng và dưới cùng, tương ứng với vị trí bắt đầu và vị trí kết thúc của mục hiện tại. Nếu ScrollTop của thanh cuộn hiện tại nằm chính xác giữa trên và dưới, tức là ScrollTop >= top && ScrollTop < dưới cùng, thì điều đó có nghĩa là cuộn hiện tại vừa đến vị trí của mục này?
Và vì giá trị của đáy trong mảng vị trí ngày càng tăng nên vấn đề trở thành việc tìm cuộnTop < dưới cùng của mục đầu tiên. Vì vậy, chúng tôi nhận được:
hàm getStart(scrollTop) { return vị trí.value.findIndex((item) => rollTop < item.bottom }
Tìm kiếm này sẽ được kích hoạt mỗi khi cuộn cuộn, vậy chúng ta có thể tối ưu hóa thuật toán trên không?
Trường dưới cùng trong mảng vị trí ngày càng tăng, phù hợp với quy tắc tìm kiếm nhị phân. Những học sinh không hiểu tìm kiếm nhị phân có thể xem câu hỏi này trên leetcode: https://leetcode.cn/problems/search-insert-position/description/.
Vì vậy, đoạn mã trên có thể được tối ưu hóa như thế này:
hàm getStart(scrollTop) { let left = 0; let right =positions.value.length - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); value[mid].bottom === ScrollTop) { return mid + 1; } else if (positions.value[mid].bottom < ScrollTop) { left = mid + 1; } else { right = mid - 1; } } quay lại bên trái }
Cũng giống như danh sách ảo có chiều cao cố định, khi cuộn trong mục bắt đầu, thao tác cuộn của trình duyệt sẽ được sử dụng lại trực tiếp mà không cần làm gì cả. Do đó, phần bù bù tại thời điểm này phải bằng giá trị cao nhất của mục bắt đầu hiện tại, là chiều cao tổng hợp của tất cả các mục ở phía trước mục bắt đầu. Vậy giá trị của offset là:
offset.value = vị trí.value[start.value].top;
Một số bạn có thể nhầm lẫn, tại sao giá trị cuộn ở mục bắt đầu không được tính vào offset offset?
Bởi vì cuộn trình duyệt được sử dụng trực tiếp khi cuộn trong phạm vi mục bắt đầu, nên đã có cuộnTop, do đó không cần thêm nó vào offset offset.
Vì vậy, chúng tôi nhận được đoạn mã sau khi sự kiện cuộn được kích hoạt:
hàm handScroll(e) { const ScrollTop = e.target.scrollTop; start.value = getStart(scrollTop);
Giá trị offset tương tự được áp dụng cho div trong vùng hiển thị bằng cách sử dụng dịch3d. Mã như sau:
...bỏ qua