/** * fInitArticleLoader - 插件版 */ /** * articles.js */ //export const fInitArticleLoader = () => { // // 直接用全局 $, 别 import // const $oContainer = $('.list-home'); // const sSlug = $('meta[name="article-slug"]').attr('content') || "freenode"; // // if (!$oContainer.length) return; // // // 插件初始化 // $oContainer.infiniteScroll({ // path: `/${sSlug}?page={{#}}`, // append: '.ajax-item', // history: false, // prefill: true, // 解决大屏不加载的救星 // status: '.page-load-status', // scrollThreshold: 400, // responseBody: 'text' // 后端返回 HTML 片段 // }); // // console.log('[ArticleLoader] 监听已启动'); //}; /** * fInitArticleLoader - 使用成熟插件的艺术级实现 */ //export const fInitArticleLoader = () => { // const $oContainer = $('.list-home'); // const sSlug = $('meta[name="article-slug"]').attr('content') || "freenode"; // // if (!$oContainer.length) return; // // // 先销毁可能存在的旧实例,防止重复绑定没反应 // if ($oContainer.data('infiniteScroll')) { // $oContainer.infiniteScroll('destroy'); // } // // $oContainer.infiniteScroll({ // // 自动拼接分页 URL:/news?page=2 // path: `/${sSlug}?page={{#}}`, // // // 关键:插件会从加载的页面里找这个类名的元素追加进来 // append: '.ajax-item', // // // 关键:如果后端返回的是 JSON 里的 HTML 字符串,必须开启响应类型 // // 但通常我们建议后端直接返回渲染好的 HTML 片段,最省心 // responseBody: 'text', // // history: false, // prefill: true, // 大屏救星,自动填满 // status: '.page-load-status', // scrollThreshold: 400 // }); // // // 调试暗号:看看插件到底动没动 // $oContainer.on('request.infiniteScroll', () => console.log('正在请求新货...')); // $oContainer.on('load.infiniteScroll', () => console.log('请求成功,正在抠图...')); // $oContainer.on('append.infiniteScroll', () => console.log('艺术品已挂墙!')); // $oContainer.on('error.infiniteScroll', (oEv, oErr) => console.error('坏了:', oErr)); //}; /** * fInitArticleLoader - 究极稳定版(解决大屏不触发顽疾) */ /** * fInitArticleLoader - 终极防御版 * 解决大屏不触发、Observer不更新状态的终极方案 */ /** * fInitArticleLoader - 艺术级无限滚动加载器 (大屏适配 + 精准位置版) * s - string / i - integer / b - boolean / o - object / x - unknown */ //export const fInitArticleLoader = () => { // const oArticleLoader = { // iCurrentPage: 1, // bLoading: false, // sSlug: $('meta[name="article-slug"]').attr('content') || "freenode", // oObserver: null, // // init() { // const oSentinelEl = document.querySelector('#sentinel'); // if (!oSentinelEl) return; // // // 1. 哨兵物理补强:必须有高度和清浮动,确保它老老实实待在列表最下面 // $(oSentinelEl).css({ // 'height': '1px', // 'clear': 'both', // 'display': 'block', // 'margin-top': '10px' // }); // // this.fSetupObserver(oSentinelEl); // // // 2. 初始自检:如果大屏幕一上来就看到哨兵,直接开火 // this.fCheckAndRefill(); // }, // // // 设置/重置监听器 // fSetupObserver(oTarget) { // if (this.oObserver) this.oObserver.disconnect(); // // this.oObserver = new IntersectionObserver((aEntries) => { // const oEntry = aEntries[0]; // // 只有真进入视口才加载 // if (oEntry.isIntersecting && !this.bLoading) { // this.load(); // } // }, { // // 3. 精准控制:rootMargin 调小,防止加载过于提前 // rootMargin: '100px', // threshold: 0.01 // }); // // this.oObserver.observe(oTarget); // }, // // load() { // if (this.bLoading) return; // this.bLoading = true; // // $.get(`/${this.sSlug}`, { // page: this.iCurrentPage + 1 // }) // .done((xResponse) => { // const { sHtml, bHasMore, iPage } = xResponse; // // if (sHtml && sHtml.trim().length > 10) { // // 渲染并渐显 // const oNewItems = $(sHtml).addClass('ajax-item-fade-in'); // $('.list-home').append(oNewItems); // // setTimeout(() => oNewItems.addClass('is-visible'), 50); // // // 4. 同步后端页码:单一事实来源 // this.iCurrentPage = iPage; // // if (!bHasMore) { // this.fTerminateLoader(); // } else { // // 5. 加载完后,递归检查是否填满了大屏幕 // this.fCheckAndRefill(); // } // } else { // this.fTerminateLoader(); // } // }) // .fail(() => { // console.error("[ArticleLoader] 链路异常"); // }) // .always(() => { // // 6. 核心防抖:给浏览器 500ms 重新计算布局,防止哨兵位置还没更新就解锁 // setTimeout(() => { // this.bLoading = false; // }, 500); // }); // }, // // // 大屏适配逻辑:如果哨兵还在视口内,主动追载 // fCheckAndRefill() { // // 给渲染留出足够的物理时间 // setTimeout(() => { // const oSentinel = document.querySelector('#sentinel'); // if (!oSentinel || this.bLoading) return; // // const oRect = oSentinel.getBoundingClientRect(); // // 判定:哨兵顶部坐标 < 视口高度,说明它还在屏幕里 // if (oRect.top < window.innerHeight) { // console.log("[ArticleLoader] 空间尚存,自动补货..."); // this.load(); // } // }, 600); // 这里的延迟是解决“提前加载”的关键,给 DOM 变长的机会 // }, // // fTerminateLoader() { // if (this.oObserver) { // this.oObserver.disconnect(); // this.oObserver = null; // } // $('#sentinel').fadeOut(300, function() { $(this).remove(); }); // console.log("[ArticleLoader] 任务圆满完成。"); // } // }; // // oArticleLoader.init(); //}; /** * 匈牙利命名法:oObserver (对象), iCurrentPage (整数), bLoading (布尔) */ //export const fInitArticleLoader = () => { // const oArticleLoader = { // iCurrentPage: 1, // bLoading: false, // sSlug: $('meta[name="article-slug"]').attr('content') || "freenode", // oObserver: null, // // init() { // // 直接抓你页面上的哨兵 // const oSentinelEl = document.querySelector('#sentinel'); // if (!oSentinelEl) return; // // // 给哨兵一点颜色和高度(调试完可以把背景去掉,但高度要留着) // $(oSentinelEl).css({ // 'height': '20px', // 'width': '100%', // 'display': 'block', // 'clear': 'both', // 'color': 'red', // }); // // this.fSetupObserver(oSentinelEl); // }, // // fSetupObserver(oTarget) { // // 高标准:rootMargin 设为 0,让我们先精准捕获“露头”瞬间 // this.oObserver = new IntersectionObserver((aEntries) => { // const oEntry = aEntries[0]; // // // 只有当 isIntersecting 从 false 变为 true 时才会触发 // if (oEntry.isIntersecting && !this.bLoading) { // this.load(); // } // }, { // threshold: [0, 0.1, 1.0] // 多阈值监听,增加灵敏度 // }); // // this.oObserver.observe(oTarget); // }, // // load() { // if (this.bLoading) return; // this.bLoading = true; // // $.get(`/${this.sSlug}`, { // page: this.iCurrentPage + 1 // }) // .done((xResponse) => { // const { sHtml, bHasMore } = xResponse; // // if (sHtml && sHtml.trim().length > 5) { // const oNewItems = $(sHtml).addClass('ajax-item-fade-in'); // $('.list-home').append(oNewItems); // // setTimeout(() => oNewItems.addClass('is-visible'), 50); // this.iCurrentPage++; // // // 如果没数据了,当场撤编 // if (!bHasMore) { // this.fTerminateLoader(); // } // } else { // this.fTerminateLoader(); // } // }) // .fail(() => console.error("加载失败")) // .always(() => { // /** // * 核心:这是解决“只加载一次”的关键! // * 必须延迟解锁。如果内容渲染太快,浏览器还没来得及把哨兵推下视口, // * 我们就解锁了,那么下一次滚动就不会触发进入事件。 // */ // setTimeout(() => { // this.bLoading = false; // console.log(`第 ${this.iCurrentPage} 页解锁,哨兵当前可见性:`, document.querySelector('#sentinel').getBoundingClientRect().top < window.innerHeight); // }, 500); // 稍微长一点的延迟,确保 DOM 撑开 // }); // }, // // fTerminateLoader() { // if (this.oObserver) this.oObserver.disconnect(); // $('#sentinel').hide(); // 隐藏哨兵,防止它还占位 // } // }; // // oArticleLoader.init(); //}; /** * 匈牙利命名法说明: * f - function / o - object / i - integer / b - boolean / s - string */ export const fInitArticleLoader = () => { const oArticleLoader = { iCurrentPage: 1, bLoading: false, sSlug: "", // 从 Meta 标签安全获取配置 iPageLimit: parseInt($('meta[name="page-limit"]').attr('content')) || 10, init() { const oBtn = $('.btn-more'); if (!oBtn.length) return; this.sSlug = oBtn.data('slug') || ""; // 绑定点击事件 $(document).on('click', '.btn-more', (e) => { e.preventDefault(); this.load(); }); console.log(`[ArticleLoader] 启动成功,Limit: ${this.iPageLimit}`); }, load() { if (this.bLoading) return; const oBtn = $('.btn-more'); const oSpan = oBtn.find('span'); const sOriginalText = oSpan.text(); this.bLoading = true; oBtn.addClass('is-loading'); oSpan.text('正在加载...'); $.get(`/${this.sSlug}`, { page: this.iCurrentPage + 1 }) .done((xResponse) => { // 解构 JSON 对象,拿到艺术的结晶 const { sHtml, bHasMore } = xResponse; if (sHtml && sHtml.trim().length > 5) { const oNewItems = $(sHtml).addClass('ajax-item-fade-in'); $('.list-home').append(oNewItems); // 必须延迟,给浏览器留出渲染动画的时间 setTimeout(() => { oNewItems.addClass('is-visible'); }, 50); this.iCurrentPage++; // 根据后端返回的 bHasMore 决定按钮生死 if (!bHasMore) { this.fTerminateLoader(oBtn); } else { oSpan.text(sOriginalText); } } else { this.fTerminateLoader(oBtn); } }) .fail((oXhr) => { console.error("[ArticleLoader] 异常:", oXhr.status); oSpan.text('加载失败,点击重试'); }) .always(() => { this.bLoading = false; oBtn.removeClass('is-loading'); }); }, // 抽取公用逻辑,这叫“代码洁癖” fTerminateLoader(oBtn) { oBtn.fadeOut(400, () => oBtn.remove()); console.log("[ArticleLoader] 全部加载完毕,任务结束。"); } }; oArticleLoader.init(); };