当前位置: 首页 > news >正文

武汉p2p网站建设公司免费seo网站优化工具

武汉p2p网站建设公司,免费seo网站优化工具,广告装饰 技术支持 东莞网站建设,邢台建网站公司需求背景 需要基于高德地图展示海量点位(大概几万个),点位样式要自定义(创建DOM),虽然使用了聚合点,但初始化时仍需要将几万个点位的DOM结构都创建出来。 这里补充一句,高德地图在2.…

需求背景

        需要基于高德地图展示海量点位(大概几万个),点位样式要自定义(创建DOM),虽然使用了聚合点,但初始化时仍需要将几万个点位的DOM结构都创建出来。

        这里补充一句,高德地图在2.0版本对这种方式进行了优化,但同时少了某些功能,我的需求要使用1.4版本的这种方式渲染。

问题及定位分析

        功能实现后,发现从开始加载点位,到点位出现的过程中,页面会卡死,无法响应用户交互,可以点击Demo的常规模式查看效果(实际业务下有更多逻辑,阻塞时间会更久)。

        可以看到,当我开始渲染点位后,点击输入框进行输入,是没有立即响应的,点位加载完后才会对之前的交互做响应。

问题分析

        其实从上面高德地图的点位渲染逻辑很容易想到主要是批量创建点位的DOM结构占用了主线程

        可以看到,批量的genMarker任务占用了大量时间,genMarker会在每次创建点位时执行一次,一次创建4w个点位,就会连续执行4w次。

// 生成点位,创建DOM自定义样式
genMarker(device) {const innerHTML = `<div class="camera"></div>`const size = [48, 49]const markerOffset = new AMap.Pixel(-size[0] / 2, -size[1] / 2)const marker = new AMap.Marker({position: device.lnglat,extData: device,size,})const container = document.createElement('div')container.className = 'map-marker'container.innerHTML = innerHTMLmarker.setContent(container)marker.setOffset(markerOffset)marker.selected = falsereturn marker
}

页面显示机制

        动的画面其实是由一帧一帧的静态图快速切换组成的,人眼的反应速度有限,当画面切换的够快,人眼看着就是连续的动画了。

        对于人眼来说,当每秒切换60张图片时,就会认为是连贯的。所以主流的显示器是60hz的,1s刷新60次,那么每16.7ms需要刷新一次,浏览器会自动适配这个频率,这时对应我们前端页面就是每16.7ms需要渲染一次。

        页面每隔16.7ms才会渲染一次,那么在两次渲染的中间时间,就是浏览器的空闲时间,在这段空闲时间执行的任务,是不会阻塞到页面渲染的流畅性的。反之,对于上面的案例,数万个genMarker在一个帧区间内连续的执行,下一帧一直不能渲染,页面看起来就被卡住了。

任务拆分

        对于大量的计算或许首先考虑的是Web Worker使其不占用主线程,但是由于要操作DOM,不适合当前场景。

        对于页面的流畅性来说,这些点位的创建属于「低优先级任务」。既然卡顿的原因是这些genMarker任务一个接一个的「连续」的在执行,一直占用着主线程,那么我们可以将这些批量的任务进行拆分,保证这些任务只在空闲时间执行。每次执行下一个任务的时候,先检查一下当前页面是否该渲染下一帧了,这时需要「把主线程让出来」,让页面进行渲染(了解react的人应该感觉很熟悉,思路来自react的Fiber)

requestIdleCallback

        「让出主线程」,关键的一点在于我们如何知道什么时候是空闲时间,什么时候空闲时间结束,该进行渲染了。requestIdleCallback就是浏览器提供给我们用来判断这个时机的api,它会在浏览器的空闲时间来执行传给它的回调函数。另外如果指定了超时时间,会在超时后的下一帧强制执行

const id = window.requestIdleCallback((deadline) => {// 当前帧剩余时间大于0,或任务已超时if(deadline.timeRemaining() > 0 || deadline.didTimeout) {// do somethingconsole.log(1)}
}, { timeout: 2000 }) // 指定超时时间// window.cancelIdleCallback(id) 与定时器类似,支持取消

        requestIdleCallback在Event Loop的执行时机如下图所示,蓝色区域代表一帧内的渲染任务,当这些任务执行完后,剩余的时间被认为是空闲时间

        以一个简单的任务(singlTask)为例,以常规模式连续执行2w次,全部执行完需要大概2s时间(依赖机器性能变化),这期间主线程被一直被占用,页面会被卡住。

function singleTask() {const now = performance.now()while (performance.now() - now < 0.001) { } // 模拟耗时操作,每次任务耗时约0.001ms
}const data = new Array(20000).fill(1)function normarlRun() {for (let i = 0; i < data.length; i++) {// 2w个任务连续执行singleTask(data[i])}result('done')
}

        对其使用requestIdleCallback进行拆分,只在空闲时间执行部分任务,若当前帧的空闲时间结束,则暂停批量任务,让出主线程:

function ridRun() {let i = 0let option = { timeout: 200 } // 任务超时时间function handler(idleDeadline) {while ((idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout) && i < data.length) {// 当前帧有剩余时间,或任务已等待超时强制执行singleTask(data[i++])}// idleDeadline.timeRemaining() === 0 当前帧已没有空闲时间,让出主线程if (i < data.length) {window.requestIdleCallback(handler, option) // 任务未执行完,继续等待下次空闲时间执行} else {result('done')}}window.requestIdleCallback(handler, option)
}

模拟requestIdleCallback

        不幸的是requestIdleCallback兼容性不够好,Safari完全不支持:

        参考react的实现,我们可以使用requestAnimationFrame和MessageChannel来模拟实现一个requestIdleCallback。requestAnimationFrame在每一帧开始渲染前执行(见上面的Event Loopt图),当帧开始渲染前,我们标记开始时间(start),并使用MessageChannel创建一个宏任务,根据上面的Event Loop流程,渲染完毕后,会执行刚才创建出的宏任务,这时在宏任务中对比标记的开始时间,是否超出了一帧的渲染时间(current - start > 16.7),来判断当前是否是空闲时间。

        setTimeout即使指定时间为0 浏览器实际也会延时几毫秒后才执行(chrome大概为4ms),因此使用MessageChannel而不是setTimeout来创建宏任务

        模拟requestIdleCallback的具体实现:

const genId = (function () {let id = 0return function () {return ++id}
})()const idMap: {[key: number]: number
} = {}const _requestIdleCallback: (cb: (idleDeadline: IdleDeadline) => void,options?: { timeout: number }
) => number = function (cb, options) {const channel = new MessageChannel()const port1 = channel.port1const port2 = channel.port2let deadlineTime: number // 超时时间let frameDeadlineTime: number // 当前帧的截止时间let callback: (idleDeadline: IdleDeadline) => voidconst id = genId()port2.onmessage = () => {const frameTimeRemaining = () => frameDeadlineTime - performance.now() // 获取当前帧剩余时间const didTimeout = performance.now() >= deadlineTime // 是否超时if (didTimeout || frameTimeRemaining() > 0) {const idleDeadline = {timeRemaining: frameTimeRemaining,didTimeout}callback && callback(idleDeadline)} else {idMap[id] = requestAnimationFrame((timeStamp) => {frameDeadlineTime = timeStamp + 16.7port1.postMessage(null)})}}idMap[id] = window.requestAnimationFrame((timeStamp) => {frameDeadlineTime = timeStamp + 16.7 // 当前帧截止时间,按照 60fps 计算deadlineTime = options?.timeout ? timeStamp + options.timeout : Infinity // 超时时间callback = cbport1.postMessage(null)})return id
}const _cancelIdleCallback = function (id: number) {if (!idMap[id]) returnwindow.cancelAnimationFrame(idMap[id])delete idMap[id]
}export const requestIdleCallback = window.requestIdleCallback || _requestIdleCallback
export const cancelIdleCallback = window.cancelIdleCallback || _cancelIdleCallback

使用requestIdleCallback拆分点位生成

        将genMarker批量任务进行拆分,只在空闲时间时间进行拆分:

addMarkersByRid() {cancelIdleCallback(this.ridId)const { markerList, points, genMarker, genCluster } = thislet index = 0const ridOption = { timeout: 20 }const handler = (idleDeadline) => {const { timeRemaining } = idleDeadline// 只在空闲时间生成点位while (timeRemaining() > 0 && index < points.length) {const device = points[index]const marker = genMarker(device)markerList.push(marker)index++}if (index < points.length) {this.ridId = requestIdleCallback(handler, ridOption)} else {console.log('done') // 全部点位生成完毕}}this.ridId = requestIdleCallback(handler, ridOption)
}

        可以看到,点位的渲染并没有再影响到页面的响应了

http://www.mmbaike.com/news/51902.html

相关文章:

  • 做网站先做前台还是后台百度一下首页百度一下知道
  • 宣传网站建设背景手机怎么制作网页
  • wordpress插入表格深圳外贸seo
  • 小程序 网站建设 app 开发站长工具查询域名
  • 灵武住房和城乡建设厅网站seo赚钱
  • wordpress建站主题百度排名优化咨询电话
  • 重庆网站怎么做出来的上海网站建设制作
  • 做c语言的网站海外seo
  • 嘉定西安网站建设十大广告联盟
  • 网站建设与推广企业软文营销
  • 深圳的网站建设佐力药业股票
  • 江苏多地发布最新情况seoul是什么意思
  • 商城网站需要注意事项百度数据分析
  • 武汉本土互联网站抖音的商业营销手段
  • 执业医师报考条件2022年最新规定长沙seo优化推荐
  • 企业网站手机端跳转设置关于进一步优化当前疫情防控措施
  • 漳浦县网站建设品牌推广策划方案案例
  • 电子书网站建设互联网营销的方式有哪些
  • 有没有免费建网站软文网站推广
  • 网站建设质量管理定义北京网络营销推广外包
  • 因酷西安网站建设公司怎么样营销型网站建设专家
  • 上海 有哪些做网站的公司网络推广软件免费
  • 蓬莱网站建设哪家专业数字营销是干啥的
  • 大连seo整站优化seo关键词优化方法
  • 做爰网站美女评论优化
  • 怎么做网站赚钱放广告百度网址链接是多少
  • 上海哪些做网站seo关键词优化工具
  • 做视频网站需要什么百度推广图片尺寸要求
  • 婚恋网站女孩子做美容优帮云排名优化
  • 皖icp网站建设文案代写在哪里接单子