在页面闲时执行任务
在页面空闲的时候,执行一些计算或某些特殊的后台任务,这样可以避免对用户操作带来卡顿的问题。虽然js支持异步编程,但是即使某些任务是异步执行的,但是因为js是单线程程序,所以,如果一个任务需要花费比较长的时间去进行计算,那么即使它是在异步回调的时候执行,也会带来界面卡顿,而如果用户在这个卡顿期间进行交互操作,就会明显感到卡死状态,体验不好。
有没有一种办法避免这种情况发生?当然有,最好的方式是使用webworker,启用另外一个线程去执行这个需要消耗大量时间的运算。因为webworker和用户界面所在的线程相互不影响,所以,不会给用户带来卡顿感。当计算完毕之后,通过postMessage实现数据传递,对于用户而言,几乎无感。
但是在一些特殊情况下,我们需要在用户界面所在的主线程去执行这种程序。比如,我们要在这个任务中获取DOM的一些信息。这个时候,我们要想办法让这个任务不对用户的操作造成影响。
web标准提供了requestIdleCallback这个接口,它的用法有点像requestAnimationFrame,它主要用于在浏览器闲时执行某个任务。比如:
var a = 0 requestIdleCallback(() => { a ++ })
上面这段代码,给浏览器下了一个命令,当浏览器空闲的时候,执行a ++。
不过requestIdleCallback会有一些兼容性问题,我们只能通过一些手段来使它在低版本浏览器可以用:
export const requestIdleCallback = window.requestIdleCallback || function(cb, delay = 1000) { const start = Date.now() const action = () => cb({ didTimeout: false, timeRemaining: function() { return Math.max(0, 50 - (Date.now() - start)) }, }) const id = setTimeout(() => { timeout.id = setTimeout(action, delay) timeout.reset = () => { clearTimeout(timeout.id) timeout.id = setTimeout(action, delay) } document.addEventListener('keydown', timeout.reset, true) document.addEventListener('mousedown', timeout.reset, true) document.addEventListener('touchstart', timeout.reset, true) document.addEventListener('touchmove', timeout.reset, true) document.addEventListener('mousemove', timeout.reset, true) window.addEventListener('scroll', timeout.reset, true) window.addEventListener('resize', timeout.reset, true) }) var timeout = { id, reset: null } return timeout } export const cancelIdelCallback = window.cancelIdelCallback || function(timeout) { if (!timeout) { return } clearTimeout(timeout.id) document.removeEventListener('keydown', timeout.reset) document.removeEventListener('mousedown', timeout.reset) document.removeEventListener('touchstart', timeout.reset) document.removeEventListener('touchmove', timeout.reset) document.removeEventListener('mousemove', timeout.reset) window.removeEventListener('scroll', timeout.reset) window.removeEventListener('resize', timeout.reset) timeout.reset = null timeout.id = null }
cancelIdelCallback用于取消前面下达的命令,参数是requestIdleCallback的返回值。
不过有一个问题,就是requestIdleCallback是只执行一次的,如果我们想要做一个守护程序,在浏览器空闲的时候,就开始运行这个守护程序,应该怎么做呢?
/** * 创建一个在空闲时执行的任务 * @param {*} fn */ export function autoidle(fn, interval = 1000, immediate = true) { var idle const run = () => { const action = () => { idle = requestIdleCallback(run, interval) } asyncx(fn)().then(action).catch(action) } const start = () => { cancelIdelCallback(idle) idle = requestIdleCallback(run, interval) } const stop = () => { cancelIdelCallback(idle) } if (immediate) { start() } return { start, stop } }
这个函数可以创建一个守护程序,它可以在你的浏览器空闲的时候不断运行,但你的浏览器开始忙碌的时候,又不会运行的效果。另外,它返回两个函数,start 和 stop,用以在必要的时候停止和重启任务。
-
技术人员的文章看不懂。#779 repostone 2019-05-13 17:33
监控用户在当前页面的停留时间
我们希望知道一个用户在当前这个页面停留的时间,这中间要考虑到用户通过切换浏览器tab的情况,把所有用户动作都记录下来。
const tracers = [] // 进入 tracers.push({ type: 'enter', time: Date.now(), }) // 切屏 const visibilitychangeFn = () => { if (document.visibilityState === 'hidden') { tracers.push({ type: 'focusout', time: Date.now(), url: window.location.href, requestId: this.getRequestId(), }) } else { tracers.push({ type: 'focusin', time: Date.now(), }) } } document.addEventListener('visibilitychange', visibilitychangeFn) // 离开 const unloadFn = () => { tracers.push({ type: 'leave', time: Date.now(), url: window.location.href, requestId: this.getRequestId(), }) record({ type: 'usertracer', tracers, stay: tracers[tracers.length - 1].time - tracers[0].time, }) } window.addEventListener('beforeunload', unloadFn)
function record(info) {}
通过一个traces数组,记录了用户从进入这个页面,在这个页面停留、切换tab,到最后离开这个页面的时间。
-
现实是你用的几个api好像有兼容问题~#780 JC 2019-05-24 14:57
-
我博客大部分文章,都是不考虑兼容性的,实现一个东西,不可能面面俱到,最重要的是为读者提供可供参考的思路#781 回复给#780 否子戈 2019-05-28 18:17
-
这个怎么说呢,
大部分开发肯定都是要考虑兼容性的,
就算是参考,
也是片面的,
唉,
不说了#782 回复给#781 JC 2019-05-28 18:18
Unsplash
可免费商用的图片素材供应商。
https://unsplash.com/
思源宋体
思源宋体、思源黑体这两款字体都是开源免费(可商用)的高质量字体,由adobe发布,值得收藏。
https://source.typekit.com/source-han-serif/cn/
手机端真机调试
https://segmentfault.com/a/1190000018613578
文件名绝对不能使用驼峰命名方式,原因很简单,不同操作系统对大小写敏感程度不同,导致修改文件名之后无法被识别,git不对这些文件进行任何处理,但是你在代码里面又是使用新的文件名。等你部署的时候,代码报错说找不到这些文件。正确的文件名命名方式是使用-或_连接单词,但是也又一些命名为了省空间不使用连接符,全部小写,这样会导致一些单词连在一起之后,有歧义,而且说实话,工程项目,有的时候为了提升团队协作的整体效率而牺牲小部分性能,也是可以接受的,成熟的现代编程,会平衡代码开发时和运行时的体验,而非一味追求运行时体验。
js数据模型驱动器
在一次表单重构中,我逐渐意识到,在数据和UI分开的前端开发模式中,UI和数据越来越需要解耦,UI要完完全全和数据本身无关,做到无状态,做到完全靠数据驱动,并且提供响应接口(更新UI的接口)。而数据层面,如果考虑到自身将被用于渲染,那么就要配置一个驱动器,将原始的数据结构转化为便于UI使用的数据结构,这就是独立于UI的数据模型驱动器。
拿表单来说,市面上有一些基于jquery的表单框架,虽然说是框架,实际上,还是一套基于js配置的ui组件,并非真正意义上的框架。而我的思考是,我们同一套表单,它背后的业务逻辑、数据提交的验证、编辑时的数据回溯等等问题,也就是脱离了UI层面的其他逻辑问题,都可以抽象出来。现在的开发全部是多端开发,同一个表单,在UI界面上可能不同,使用的UI技术也可能不同,例如在公众号网页和在小程序中,甚至在ReactNative构建的原生app中,他们抛开UI、交互层面的东西,背后的业务逻辑几乎是一摸一样的,为什么不可以共享?而且这三种场景,背后都支持js原生的计算能力和语言特性,所以,很明显是一定可以共享的,所不同的是,在将模型和UI框架进行拼接时,拼接方式不同。
表单模型只是一个例子。这种将模型抽离出UI的方案,适用于大部分带有业务逻辑的中后台应用中,在都使用了web技术的前提下,想多端共享同一套业务逻辑,就应该用这种思路去写。
现在的问题是,有没有一套完整方案(框架),去像React那样,编写模型。我认为还没有,或者说,还没有一个UI框架是基于这种考虑去实现的,因此,也就没有模型框架可以被应用到实际开发中。如果有一种UI框架(特别是React),可以基于“模型-视图”的方式去开发自己,那么就更有利于模型框架的出现。就目前而言,最接近这种形态的是rxjs作为模型框架,react作为UI框架。
第一步,在鼠标setting里面激活secondary click,并选择click on left side。这样就可以左右两个键互换了。接下来就是修改鼠标指针,参考如下步骤。
1. Download Mousecape.zip containing the 0.0.5 pre-release and install.
2. Install the helper tool from the menu bar by clicking Mousecape > Install Helper Tool.
3. From Mousecape preferences (Command-,) select "I am... left handed"
4. From the menu bar click Capes > Dump Cursors.
5. Click the cursor dump to highlight. Command-Enter to apply. Command-R to restore the default cursors.