前言
背景
- 操作步骤
- 1 商品列表页和详情是使用ssr渲染, 商品列表页面一直滚动使用分页加载,
- 2 从列表去详情,详情返回无法定位离开位置,需要解决的问题就是定位到列表上次离开的地方,(safari back 可以定位到对应位置,非safari back 无法定位到位置)
- 方法尝试
- 1 使用sessionStorage配合scrollTo 、setInterval
- 1.1页面离开保存 scrollTop值到sessionStorage
- 1.2下次进入调用 scrollTo 到对应位置,
- 1.3如果scrollTo一次无法滚动到对应的位置,使用setInterval滚动到对应位置后,触发view more 直到滚动到对应位置或者10秒超时停止
- 2 使用localStorage结合sessionStorage
- 2.1页面离开保存 scrollTop值到sessionStorage,保存数据到 localStorage
- 2.2下次进入,取localStorage的数据、取sessionStorage中 scrollTop值
- 2.3在domready的时候,重新写列表数据,并执行scrollTo到对应位置
- 3 使用indexDB结合sessionStorage
- 3.1页面离开保存 scrollTop值到sessionStorage,保存数据到 indexDB
- 3.2下次进入,取indexDB的数据、取sessionStorage中 scrollTop值
- 3.3在domready的时候,重新写列表数据,并执行scrollTo到对应位置
- 1 使用sessionStorage配合scrollTo 、setInterval
坎坷之路1
1使用sessionStorage配合scrollTo 、setInterval
// 页面离开监听保存
useEffect(() => {
function saveDataList(){
const onBeforeUnload = (event) => {
const scrollPos = { x: window.scrollX, y: window.scrollY }
sessionStorage.setItem(url, JSON.stringify(scrollPos))
}
window.addEventListener('beforeunload', onBeforeUnload)
function scrollTo(scrollPosition: any) {
let index = 0
if (scrollPosition) {
const footerEle = document.getElementById('layout_footer')
const scrollLoadingEle = document.getElementById('scrollLoading')
const layoutEle = document.getElementById('layout')
const footerClientHeight: any =
(footerEle?.clientHeight as any) * 1 || 0
if (footerEle) {
footerEle.style.height = '0px'
footerEle.style.overflow = 'hidden'
}
if (layoutEle) {
layoutEle.style.opacity = '0'
}
if (
scrollLoadingEle &&
scrollPosition >
(document.body || document.documentElement).clientHeight
) {
scrollLoadingEle.style.display = 'block'
}
window.requestAnimationFrame(() => {
window.scrollTo({
top: scrollPosition,
})
const times: any = setInterval(() => {
if (
window.scrollY >= scrollPosition - footerClientHeight ||
index > 50
) {
index++
if (footerEle) {
footerEle.style.height = `auto`
footerEle.style.minHeight = `${footerClientHeight}px`
}
window.scrollTo({
top: scrollPosition,
})
if (layoutEle) {
layoutEle.style.opacity = '1'
}
if (scrollLoadingEle) {
scrollLoadingEle.style.display = 'none'
}
clearInterval(times)
} else {
index++
window.scrollTo({
top: scrollPosition,
})
}
}, 100)
})
}
}
const scrollPos: any = JSON.parse(sessionStorage.getItem(url) as any)
scrollTo(scrollPos?.y)
return () => {
window.removeEventListener('beforeunload', saveDataList)
}
})
看下效果图
在滚动量比较小或者加载页数比较小的情况下是可以接受的(也可以配合虚拟加载)
当列表页面过大的或者也是交多少lodaing时间会很长,不见建议使用
坎坷之路2
2 使用localStorage结合sessionStorage 在使用localStorage的时候咱们先了解一下 localStorage最大存储空间
- 1、localStorage 拓展了 cookie 的 4K 限制。
- 2、localStorage 会可以将第一次请求的数据直接存储到本地,这个相当于一个 5M 大小的针对于前端页面的数据库,相比于 cookie 可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的。 localStorage 的局限
- 3、浏览器的大小不统一,并且在 IE8 以上的 IE 版本才支持 localStorage 这个属性。
- 4、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换。
- 5、localStorage在浏览器的隐私模式下面是不可读取的。
- 6、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡。
- 7、localStorage不能被爬虫抓取到。localStorage 与 sessionStorage 的唯一一点区别就是 localStorage 属于永久性存储,而 sessionStorage 属于当会话结束的时候,sessionStorage 中的键值对会被清空。
看伪代码
- 1 就是在离开页面的 操作
- 1.1 sessionStorage.setItem(url, JSON.stringify(scrollPos))
- 1.2 slocalStorage.setItem(url, JSON.stringify(dataList))
- 2 再次进入该页面
- 2.1 const scrollPos= sessionStorage.getItem(url, JSON.stringify(scrollPos))
- 2.2 const dataList= slocalStorage.getItem(url, JSON.stringify(dataList))
- 2.3 把页面的数据替换成dataList,并滚动到对应位置 看gif动图,秒开,但是当数据量比较打的时候就会出现异常导致存储出现问题,所以不是可靠的方案
注意 当dataList过大时就会出现异常
坎坷之路3
3 使用indexDB结合sessionStorage,在使用indexDB咱们先了解一下indexDB
-
1、键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
-
2、异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
-
3、支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
-
4 、同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
-
5、浏览器的最大存储空间是动态的——它取决于您的硬盘大小。 全局限制为可用磁盘空间的50%。 在Firefox中,一个名为Quota Manager的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。
//打开数据库
var db;
function useIndexedDB() {
try {
function saveDataList() {
var onBeforeUnload = (event) => {
var elementListData = $('.className').html()
setMoreData(elementListData)
saveScrollPos(location.pathname)
}
window.addEventListener('beforeunload', onBeforeUnload)
}
// 保存scrollPos
function saveScrollPos(url) {
var scrollPos = { x: window.scrollX, y: window.scrollY }
if (scrollPos) {
sessionStorage.setItem(url, JSON.stringify(scrollPos))
}
}
// 保存自动滚动 配合indexdb
var scrollPos = JSON.parse(
sessionStorage.getItem(window.location.pathname)
)
// scrollPos不存在或者scrollPos.y不大于0
if (!scrollPos || !(scrollPos && scrollPos.y)) {
scrollLoadingEle.style.display = 'none'
}
window.indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB
if (!window.indexedDB) {
scrollLoadingEle.style.display = 'none'
return
}
var openReq = window.indexedDB.open('moreDatabase')
openReq.onerror = function (event) {
scrollLoadingEle.style.display = 'none'
}
openReq.onsuccess = function (event) {
db = openReq.result
if (!db.objectStoreNames.contains('moreData')) {
var objectStore = db.createObjectStore('moreData', {
keyPath: 'id',
})
objectStore.createIndex('listData', 'listData', {
unique: false,
})
} else {
//读取并滚动
readDB()
}
saveDataList()
useDatabase(db)
console.log('数据库打开成功')
}
openReq.onupgradeneeded = function (event) {
db = event.target.result
if (!db.objectStoreNames.contains('moreData')) {
var objectStore = db.createObjectStore('moreData', {
keyPath: 'id',
})
objectStore.createIndex('listData', 'listData', {
unique: false,
})
} else {
//读取并滚动
readDB()
}
saveDataList()
}
function useDatabase(db) {
db.onversionchange = function (event) {
if (db.close && typeof db.close === 'function') {
db.close()
}
}
}
function setMoreData(elementListData) {
try {
var compressedString = pako.gzip(elementListData, {
to: 'string',
})
var requestUpdate = db
.transaction('moreData', 'readwrite')
.objectStore('moreData')
[keyWord]({
id: location.pathname,
listData: compressedString,
})
requestUpdate.onerror = function (event) {}
requestUpdate.onsuccess = function (event) {
if (db.close && typeof db.close === 'function') {
console.log('数据库关闭成功')
db.close()
}
}
} catch (error) {
console.log(error)
}
}
function readDB() {
try {
var scrollPos = JSON.parse(
sessionStorage.getItem(window.location.pathname)
)
var prevPagePathname =
sessionStorage.getItem('prevPagePathname')
if (!/\/products\//g.test(prevPagePathname)) {
scrollLoadingEle.style.display = 'none'
window.scrollTo({
top: 0,
})
var request = db
.transaction('moreData', 'readwrite')
.objectStore('moreData')
.delete(location.pathname)
request.onsuccess = function (event) {}
request.onerror = function (event) {}
return
}
var request = db
.transaction('moreData', 'readonly')
.objectStore('moreData')
.get(location.pathname)
request.onerror = function (event) {
window.scrollTo({
top: 0,
})
scrollLoadingEle.style.display = 'none'
}
request.onsuccess = function (event) {
if (
request &&
request.result &&
request.result.listData &&
pako
) {
keyWord = 'put'
var listData = pako.ungzip(request.result.listData, {
to: 'string',
})
$('.classaName').html(listData)
window.scrollTo({
top: scrollPos.y,
})
} else {
window.scrollTo({
top: 0,
})
}
scrollLoadingEle.style.display = 'none'
}
} catch (error) {
scrollLoadingEle.style.display = 'none'
}
}
} catch (error) {
scrollLoadingEle.style.display = 'none'
}
}
try {
useIndexedDB()
} catch (error) {
scrollLoadingEle.style.display = 'none'
}
优点 相比第一个想法、加载数据会很稳定,数据存储容量大 看图
indexDB的使用的坑
* 1 虽然indexDB存储空间很大,但是会对每天数据大小有个限制大致130M
* 2一定要及时清理indexDB,当超过最大的存储浏览器闪退
如有不足,可以留言指出😁