需求: 店铺选择器(h5端)
项目: reactHooks + antdMobile + mobx6
如下图:
大概就是这样一个大分组(品牌)下面有很多个品牌列表,每一个品牌有很多店铺, 需求就是选中店铺存到已选门店那里.这里使用了mbox存储.
细化:
- 每一个小分组下 全选反选功能,反选的时候只能删除该分组下的店铺
- 弹窗内清空按钮 - 全部清空
- 弹窗内 取消选中-删除单个
- 点击到不同的二级分组下,需要检验该分组内的店铺有没有被选中的,都被选中则全选
因为店铺过多在进行js操作的时候会造成卡顿.
解决: 大数据页面渲染问题
这里使用了 # useVirtualList 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。
官网地址: https://ahooks.gitee.io/zh-CN/hooks/use-virtual-list
// 列表店铺数据
const [virtual_list] = useVirtualList( state.shopSearchRes || [], {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: 30,
overscan: 500
})
<Checkbox.Group
value={values}
onChange={v => {
setValues(v as string[])
}}
>
{ virtual_list && virtual_list.map((x: any ,index: any) => {
return (
<li key={x.data?.id } style={{height: '56px'}}>
<div className="text">
<span>{x.data.name}</span>
<p>店铺编码{x.data.sn}|系统编码{x.data.id}</p>
</div>
<Checkbox
// checked={store.storeSelection.storeSelectionData.includes(x)}
onChange={(val) => {
if (val) {
store.storeSelection.AddSelectionData(JSON.parse(JSON.stringify(x)).data)
}else {
// store.storeSelection.ReduceSelectionData([JSON.parse(JSON.stringify(x)).data])
worker.postMessage({ type: 'delcompute', payload: { 'data': [JSON.parse(JSON.stringify(x.data))], 'storeSelectionData': JSON.parse(JSON.stringify(store.storeSelection.storeSelectionData))}}) // 发送信息给worker
}
}}
key={x}
value={x.data.name}
style={{
'--icon-size': '15px',
'--font-size': '14px',
'--gap': '6px',
}}/>
</li>
)
})}
</Checkbox.Group>
进行js操作卡顿问题解决- # web worker
为什么JavaScript是单线程?
众所周知,JavaScript
语言的特点是单线程
,也就是说,同一个时间只能做一件事。那么,为什么JavaScript
不能有多个线程
呢?这样能提高效率
啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript
的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程
,否则会带来很复杂的同步问题。比如,假定JavaScript
同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
什么是Web Worker?
为了利用多核CPU
的计算能力,HTML5
提出Web Worker
标准,允许JavaScript脚本创建多个线程
,但是子线程完全受主线程控制,且不得操作DOM
。所以,这个新标准并没有改变JavaScript单线程的本质。
在worker线程中,虽然无法直接操作dom节点
,也不能使用window对象的默认方法
和属性
,但是仍然可以使用window对象下的东西,比如websocket
,indexedDB
等。
workers
和主线程
间的数据传递通过这样的消息机制进行——双方都使用postMessage()
方法发送各自的消息,使用 onmessage 事件
处理函数来响应消息(消息被包含在Message
事件的 data 属性中)。这个过程中数据并不是被共享而是被复制。
关于web worker的兼容性问题,在can i use
中查找一轮后发现,基本目前所有主流的浏览器都支持了,因此放心食用,无需考虑兼容性的问题。
例子:
该需求中删除选中店铺
在tsx页面中
const worker = new Worker(webWorker)
worker.onmessage = function (e: { data: { type: string; msg: { newArr: any; valArr: any; }; }; }) {
if (e.data.type === "delcompute") {
const { newArr, valArr } = e.data.msg
// newArr = newArr
setValues(valArr)
store.storeSelection.ReduceSelectionData(newArr)
}
}
调用的时候:
// 取消全选选中
<Checkbox
style={{
'--icon-size': '15px',
'--font-size': '14px',
'--gap': '6px',
}}
indeterminate={values.length > 0 && values.length < items.length}
checked={ values?.length && values?.length === (items && items?.length)}
onChange={checked => {
if (checked) {
// values.push(items)
setValues(items)
store.storeSelection.SetShopName(items)
store.storeSelection.AddSelectionData(state.shopSearchRes)
} else {
setValues([])
// store.storeSelection.ReduceSelectionData(state.shopSearchRes)
worker.postMessage({ type: 'delcompute', payload: { 'data': JSON.parse(JSON.stringify(state.shopSearchRes)), 'storeSelectionData': JSON.parse(JSON.stringify(store.storeSelection.storeSelectionData))}}) // 发送信息给worker
}
}}
>
全选
</Checkbox>
// 取消单个选中
<div className='text-29' onClick={() => {
worker.postMessage({ type: 'delcompute', payload: { 'data': [JSON.parse(JSON.stringify(x.data))], 'storeSelectionData': JSON.parse(JSON.stringify(store.storeSelection.storeSelectionData))}}) // 发送信息给worker
const aa = values.map( (item:any) => item !== x.data?.name)
}}>取消选中</div>
</li>
// webworker.js
const workercode = () => {
// 监听事件,主线程可以通过 postMessage 发送信息过来
// eslint-disable-next-line no-restricted-globals
self.onmessage = function (e) {
let passBack = void 0
if (e.data.type === 'delcompute') {
const { data , storeSelectionData } = e.data.payload
let newArr= storeSelectionData.filter((v) =>
data.every((val) => val.id !== v.id)
)
const valArr = newArr.map(item => item.name)
passBack = { newArr: newArr, valArr: valArr }
// eslint-disable-next-line no-restricted-globals
self.postMessage({
type: 'delcompute',
msg: passBack
})
}
}
}
let code = workercode.toString()
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
const blob = new Blob([code], {
type: 'application/javascript'
})
const worker_script = URL.createObjectURL(blob)
module.exports = worker_script
注意事项
虽然web worker可以调用cpu的多线程,从而提高咱们页面的性能。但是它不是随便使用
的,如果滥用web worker可能不仅不会得到性能的提升,还可能造成性能的损耗
。
举一个简单的小栗子
如果咱们将递归获取斐波那契数列
第n位的方法,将传入参数修改为第2位
,这时候咱们再重跑单线程测试和多线程测试。
结果如下图,咱们可以发现单线程模式
下,获取20次斐波那契数列第二位的时间仅需要1.5ms
,而在多线程
的情况下却需要78ms
。这是为什么呢?因为咱们每次创建worker线程
以及possmessage通信
都是需要损耗
一些性能以及时间的。因此web worker是不可以滥用
的哦,日常开发中,建议在需要消耗比较多
的cpu运算能力
的时候酌情使用。
参考: https://juejin.cn/post/7117774868187185188