最近接到一个需要,产品经理心愿能新增弹窗广告,广告可依据后盾配置在利用任意页面弹出展现。当后盾扭转以后页面广告次数、链接或者指标页后,以后页面数据批改,不影响其余页面数据
例如后盾设置“首页”呈现广告 1 次,“我的”页面广告呈现 3 次,用户进去后敞开了“首页”广告 1 次,敞开了“我的”页面广告 2 次。此时退出利用,后盾将“首页”广告设置为 2 次,那么该用户“首页”广告重置为 2 次,“我的”页面广告仍为 1 次(3 – 2)
需要剖析
后端返回的数据必然是个数组,每个对象中会有 指标页(展现的页面 ), 跳转链接 , 总呈现的次数 三参数。前端要对数据进行解决:
- 当本地没有数据时(第一次进入),将总呈现次数赋值给一参数 firstTotalTimes(记录原总呈现次数)
-
当本地有数据(非第一次进入)
- 将本地存储中的 firstTotalTimes 革除,返回值赋值为 removeLocalTotalTimeList
-
将 removeLocalTotalTimeList 与 申请返回的数据 advertisementList 进行比照
- 相等,阐明后盾数据没有扭转,查看你本地存储中的总呈现次数是否大于 0,大于则展现广告
- 不相等,阐明后盾批改了数据,这里还要剖析,只重置批改处页的,未修改的中央不做解决
笔者用的框架是 umi3,其中有 wrappers 概念,即一个配置路由的高阶组件封装,在 umi.conf 中加上后,任何页面都要先通过这一道。要害代码如下:
useEffect(() => {dispatch({ type: 'common/fetchGetPopUpAdvertisementList'}).then((resData: any) => {if (resData?.resultCode === "S00000") {if (!localStorage.advertisementList) {const addFirstTotalTimes = resData.advertisementList.map((item: any) => {
item.firstTotalTimes = item.totalTimes
return item;
})
localStorage.advertisementList = JSON.stringify(addFirstTotalTimes);
}
const localAdvertisementList = JSON.parse(localStorage.advertisementList)
const cloneLocalAdvertisementList = JSON.parse(JSON.stringify(localAdvertisementList))
const removeLocalTotalTimeList = cloneLocalAdvertisementList.map((item: any) => {
delete item.firstTotalTimes
return item
})
if (_.isEqual(removeLocalTotalTimeList, resData.advertisementList)) {console.log('相等')
localAdvertisementList.filter((item: any) => {if (item.targetUrl.indexOf(history.location.pathname) > -1) {if (item.firstTotalTimes > 0) {setAdItem(item)
}
}
})
} else {console.log('不相等')
const cloneList = JSON.parse(JSON.stringify(resData.advertisementList));
for (let i = 0; i < cloneList.length; i++) {for (let j = 0; j < cloneLocalAdvertisementList.length; j++) {if (_.isEqual(cloneList[i].pkId, cloneLocalAdvertisementList[j].pkId)) {if (_.isEqual(cloneList[i], cloneLocalAdvertisementList[j])) {cloneList[i].firstTotalTimes = localAdvertisementList[j].firstTotalTimes
} else {cloneList[i].firstTotalTimes = cloneList[i].totalTimes
}
}
}
}
localStorage.advertisementList = JSON.stringify(cloneList);
cloneList.filter((item: any) => {if (item.targetUrl.indexOf(history.location.pathname) > -1) {if (item.firstTotalTimes > 0) {setAdItem(item)
setIsShow(true)
}
}
})
}
}
})
}, [])
难点
JS 的数据可变性
第一个坑点在 JS 的数据是可变的,所以要对其数据进行深拷贝,才不会影响到其余数据,这里我用了最简略的深拷贝:JSON.parse(JSON.stringify)
const cloneLocalAdvertisementList = JSON.parse(JSON.stringify(localAdvertisementList),
)
判断后盾那个数据批改
在之前表述中曾经表明,当本地存储和申请过去的数据不统一时要判断,哪要做重置,哪些页面则维持原状。这就要对两个数组进行比照,最简略的办法就是做双循环(On2).
先 const cloneList = JSON.parse(JSON.stringify(resData.advertisementList));
,深拷贝后盾返回数据,这样对 cloneList 进行解决时就不会影响到原数据。cloneLocalAdvertisementList
则是本地的存储
if (_.isEqual(cloneList[i].pkId, cloneLocalAdvertisementList[j].pkId))
,pkId 是广告惟一标识,先辨认数组中的每一个对象,这是一一对应的,再判断 if (_.isEqual(cloneList[i], cloneLocalAdvertisementList[j]))
,比照对象中的值,如果是 true,即齐全相等,阐明后盾数据没有变动,那就将本地存储中的 firstTotalTimes 赋值给 cloneList 上的 firstTotalTimes。如果是 false,阐明后盾曾经批改,就把 firstTotalTimes 重置为本次拉取数据中的 totalTimes
const localAdvertisementList = JSON.parse(localStorage.advertisementList)
const cloneLocalAdvertisementList = JSON.parse(JSON.stringify(localAdvertisementList))
...
const cloneList = JSON.parse(JSON.stringify(resData.advertisementList));
for (let i = 0; i < cloneList.length; i++) {for (let j = 0; j < cloneLocalAdvertisementList.length; j++) {if (_.isEqual(cloneList[i].pkId, cloneLocalAdvertisementList[j].pkId)) {if (_.isEqual(cloneList[i], cloneLocalAdvertisementList[j])) {cloneList[i].firstTotalTimes = localAdvertisementList[j].firstTotalTimes
} else {cloneList[i].firstTotalTimes = cloneList[i].totalTimes
}
}
}
}
以上,就是对这次我的项目的外围代码,当然,还要思考到 App 端关上和 微信关上的差别,以及当未登录状态下的去登录后数据的更新等等,但这些能够通过监听登录来判断(useEffect 依赖数据)实现
总结
这次被数据可变性坑了,通过 debugger 来排查
双循环在理论我的项目中用的次数不多,所以对此做记录