背景:项目提测后,自己无聊,查看bug(测试没测出来,我自个儿测出来了~),在webview里边,有个倒计时,然后我接触滑动,一直上下滑动,倒计时回中止,松开后,倒计时又持续了,如下图:
演示:
分析一下
js是单线程言语,虽然Web Worker
答应 JavaScript 脚本创立多个线程m d 3 Q f L Q i X,但是子线程彻底受主线程控制,且不能e G l b b ; ; R G操作 DOMA R n Z ` I X 9 r,所以,这个新标准并没有改变 JavaScript 单线程的本质
浏览器是多线程的,事件触发线程、定时触发器线程、异步 HTTP 请求线程
出现引擎:又称烘托引擎,也被称为浏F T G 4 4 K览器内核,在线程方面又称为 UI线程
(Trident、Gecko、Blink、Webkit、Presto)
JavaScript 解说器:又称为 JavaSq % A / q * cript 解析引擎,又称为 JavaScript 引擎,也能够称为 JavaScript 内核,在线程方面又称为 JavaScript^ T e 8 引擎线程
(V8、} g 6 dChakr( Y P I [ ] $ ia、TJ X % ( ) x v 1raceMoo V E [ } nkey)
UI线程
与 JS引擎线程
互斥
移动端开发,一些用到UI线程
的方法B R B .(比方:经过动画、setInterval、seQ N ? n + ktTimeout等频繁操作dom),在引擎线程
被占用时,会发作卡顿的现象。
处理方案 — Web Workers
Web Workers
(MDN,知乎)W 6 S K U S能够在独立于主线程的后台线程中,运转一个脚本操作。也就是能够` A Z r在独立线程中履行耗时的使命,从而答应主线程(通常是UI线程)不会因此被堵塞/怠慢。
Workers API
,这儿我4 b = O就不再赘述,请看 MDN、知乎、知乎
写 Worker 脚本
依据API文档可知,Worker(aURL)
结构函数,它只履行URL指定的脚本,不指定URL时,而由运用Br o r t o ]lob
创立,也就是说这个 aURL
除了能够是url(同源),还能够是Blob
,可惜的是不支持es模块化,当下的项目大都是| ? A A * @ k w h用webpack进行打包,考虑到这点,有三种方式来加载这个脚本:
- 单独保护这t t A m L个脚本,放到cdn上
- 运用相对路径,把脚本放到静} [ u | r 2 M态资源
assets
中,打包时copy到输出目录dist
,此刻要考虑布置的问题,布置在非根目录F U e 9 m |时,为保证在相对目录仍能找到这个脚本,要在这个相/ a h 0对路径中要包含对应环境的public
- 凭借
Blob
用内联脚本经过blD , i A Bob URL目标创立,进行模块化,使worker初始化更快,因为消除了网络来回的延迟
我比较喜爱第三种,下面来看看详细完成
/**
创立 blob URV ( l 5 2 p @ p nL,供worker运用
./worker/countdown.ts
*/
consg s q m J :t workerScr+ k =ipt = `
self.onmessage = function(event) {
var + , u L f ) ( P num = event.data;
var T = setInj R Z V G R ]terval(function() {
self.postMessage(--num);
if (num <= 0) {
clearInterval(T);
self.close();
cN X l p t Jonsole.log('clearInterval & worker closede f U 8');
}
}, 1000);
};
`;
const workerScriptBlob = new Blob([wo` N _ v CrkerScript]);
const workerScriptBlobUrl = URL.createObjectURL(workerScriptBlob);
export default workerScriptBlobUrl;
/**
详细运用
index.ts D f ; 2 Y 0sx
*/
import * as React from "react";
import CountdownB% z v H E 4 wolb from "./worker/countdown";
const transfDate = (second: numbe4 : / r T 7 E } Tr): (numbe{ 3 x * V 9 & a qr | string)[] => {
if (second < 0) {
return [D 2 G y"--", "--", "--` ] 5 E Q v 4 C n", "--"];w u / 4
}
const DD = second / (24 * 60 * 60);
const HH = (Y K # ! _ O a o fsecondh g % % (24 * 60 * 60)) / (60 * 60);
const mm[ u J 2 W C x N + = ((second % (24 * 60 * 60)) % (60 * 60)) / 60;
const ss = ((second % (24 * 60 * 60)) % (60 * 60)) % 60;
return [DD, HH, mm, ss].map(item => {
item = Math.floor(i$ k =tem);
if (item < 1m ! l0) {
return `0${item}`;
}
return item;
});
};
interface IState {
re# 1 ? #main_second: number;
}
export default class Ind: M . O g _ex extends ReactM 9 = u a.PureComponent- N n E ; V 1 t 0<{}, IState> {
state = {
remain_second: 30
};
_worke+ / q Ur = new Worker(CountQ E G M @downBolb);
runTime = (): v+ [ h E 0oid => {
/@ u + 0 n w !/ 运用web worker 处理直接运用setInterval的接触滑动时烘托卡顿问题
const { remain_second } = this.state;
this._worker.postMessage(remain_secA 4 k [ tond);
this._worker.onmesss h A e +age = event => {
constE @ & Z 6 S v 1 A s = event.data;
this.setState({ remak , v { A ] +in_second: s });
};
};
componentDidMount() {
try {
thi| L ]s.runTime();
} catch (e) {
cons= - mole.log(e);
}
}
componentWillUnmount() {
// 传入0,清除定时器并关闭worker
this._worker.postMessage(0);
}
renderCountd& i / d w X k Eown = () => {
const { remain_second } = this.state;
coJ i T , Fnst [DD, HH, mm| . Q k W s, ss] = transfDate(remain_second);
return (
<divz f 5 6>E H & ~ f 0;
<span style={{ width: "10px" }} /&} U ^ w ) ! .gt;
{D m H sDD}Day {HH}h {mm}m {ss}s
</div>
);
};
render() {
return <: g p Wdiv className=` y & l"App">{this.renderCountdown()}</div>8 _ X ; P Z;;
}
}
在线预览 codesandbox
【参考】:
- deE Y [ Y | K rveloper.mozilla.org/zh-CN/docs/…
- devN 5 0 Z h p 6 ~eloper.mozilla.o* | a I a k ! brg/zh-CN/docs/…
- developer.mozilla.org/zh-CN# % ] 1 o S ; ^/docs/…
- zhuanlan.zhihu.com/p/25184390
- zhuanlan.zh[ , + ] }ihu.com/p/93470509