聊聊webworker那些事儿~

众所周知,javascript在最开始被设计时,只是为了最简单的页面交互而设计的,因此在秉持这个理念下,js被设计成 单线程 的模型,这样可以避免多线程模型下的临界资源竞争、死锁等问题

单线程模型的确够简单,也完全符合当时重浏览的网页,js也确实不需要强大的性能,但随着技术的发展,现代网页逐渐从 重浏览 变为了 重交互 ,这样就给js提出了更高性能的要求,于是乎当初的单线程模型如今变成了提升性能的绊脚石,人们急需一个"高性能"的js,在这样的需求下,针对提高js性能的各种方案应运而出,而本文的主角 webworker 也是其中一员,那么它具体是如何提高js性能的,就请看下面的正文吧~

浏览器架构

在聊webworker之前,我想先聊聊关于浏览器本身的东西,因为我觉得在了解了浏览器的架构之后,是可以更好地去理解webworker的

chrome 为例,它是采用的 多进程 的架构,具体由以下进程组成:

  • 浏览器进程 ,主要负责:
    1. 包括地址栏,书签栏,前进后退按钮等部分的工作
    2. 负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问
    3. 负责各个页面的管理,创建和销毁其他进程
  • 渲染器进程 ,主要负责一个网页的内容显示,脚本解析执行等,也称为 浏览器内核
  • 插件进程,主要负责一个网页内所使用到的插件的管理
  • GPU进程,主要负责GPU的相关渲染工作

这里说明下,在浏览器里,浏览器进程GPU进程插件进程 是全局唯一的,意思就是被所有网页共享的,而 渲染进程 是被每一个tab所独享的,意思就是新打开一个tab,就会创建独立的 渲染进程

对于前端工程师来说,最关注的应该就是 渲染进程,它由多个线程组成:

  • js引擎线程,不必多言
  • 渲染线程,用于渲染网页内容
  • 事件触发线程,用于管理事件轮询,适时地将事件回调放入任务队列里
  • 定时器触发线程,用于管理settimeout和setinterval,定时器的计时是专门交由它来做的
  • 异步请求触发线程,用于管理异步请求触发与回调

这里的 渲染线程js引擎线程 是互斥的,因此如果js引擎里执行一些耗时操作,渲染线程是会被挂起并等待js引擎执行完成后,才会响应用户的交互进而更新界面,这就会造成页面卡顿、掉帧,影响用户体验

下面给出浏览器架构图

什么是webworker

Web Worker 使得在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作成为可能。这样做的好处是可以在独立线程中执行费时的处理任务,使主线程(通常是 UI 线程)的运行不会被阻塞/放慢。

上面一段话是摘自MDN对它的描述,这里也谈谈我对webworker的看法

因为js是 单线程模型 ,因此在遇到一些耗时的任务时,是一定会被"卡住"的,又从上文得知,js引擎被卡住,那么渲染线程是会被挂起的,这样用户的交互就得不到及时响应,从而使页面卡顿、掉帧,而webworker的解法是直接 提供多线程模型,将一些耗时的任务下放到webworker中去执行,从而可以解放js主线程,最终避免渲染线程被挂起的尴尬局面

webworker具体分为两类

  • 专用worker,被当前浏览器上下文所独占
  • 共享worker ,可以被 同源的 浏览器上下文所共享

要想使用它们也非常简单,示例如下:

javascript 复制代码
    const myWorker = new Worker("worker.js"); //专用worker
    const mySharedWorker = new SharedWorker("worker.js"); //共享worker

需要注意如下几点

  • worker脚本的url必须跟当前浏览器上下文同源
  • worker无法访问dom
  • worker的数据通信采用的是 结构化克隆 的方法,因此主线程与worker拿到的其实是彼此通信数据的拷贝

通信的方式主要是通过 postMessagemessage事件 来进行,而对于共享worker的话,需要注意的是要显示地使用 port 来进行通信,具体使用实例如下

专用worker:

javascript 复制代码
   //主线程发送消息给worker
   myWorker.postMessage('这是一条来自主线程发往worker的消息');
   //主线程接受到来自worker的消息
   myWorker.onmessage = (e) => {
     console.log(e.data,"这是一条来自worker发往主线程的消息");
   };
    
   //worker接收来自主线程的消息
   onmessage = e=>{
     console.log(e.data,"这是一条来自主线程发往worker的消息");
     postMessage("这是一条来自worker发往主线程的消息")
   }

共享worker:

javascript 复制代码
   //主线程发送消息给worker
   mySharedWorker.port.postMessage('这是一条来自主线程发往worker的消息');
   //主线程接受到来自worker的消息
   mySharedWorker.port.onmessage = (e) => {
     console.log(e.data,"这是一条来自worker发往主线程的消息");
   };
    
   //worker接收来自主线程的消息
    onconnect = (e) => {
      const port = e.ports[0];

      port.onmessage = (e) => {
        console.log(e.data,"这是一条来自主线程发往worker的消息");
        port.postMessage(workerResult);
      };
    };

可以看到,共享worker里,主要是借助 port 来与主线程进行通信的,实现方式上比专用worker稍微复杂一些

利用webworker,我们可以使用多线程的能力,最大限度发挥多核cpu的能力来使我们的网页更加丝滑,那这么棒的东西兼容性如何呢?

可以看到,兼容性方面是非常好的,可以放心使用~

结语

webworker可以让我们有多线程的能力去优化网页性能,并且兼容性方面也是非常棒的,但凡事得看两面,webworker带来的代码复杂度以及额外的内存占用都是需要考虑的,我们需要综合考虑自己项目的情况来决定是否使用它,要看它带来的收益是否大于带来的成本,俗话说得好:适合自己的才是最好的~

相关推荐
啧不应该啊8 分钟前
vue配置axios
前端·javascript·vue.js
__fuys__12 分钟前
【HTML样式】加载动画专题 每周更新
前端·javascript·html
Want59515 分钟前
HTML粉色烟花秀
前端·css·html
让开,我要吃人了20 分钟前
HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践
前端·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·鸿蒙开发
一条晒干的咸魚38 分钟前
响应式CSS 媒体查询——WEB开发系列39
前端·css·html·css3·响应式设计·媒体查询
凌晨五点的星1 小时前
网络安全-webshell绕过,hash碰撞,webshell绕过原理
开发语言·前端·javascript
天心天地生1 小时前
【bugfix】-洽谈回填的图片消息无法显示
开发语言·前端·javascript
啧不应该啊1 小时前
element plus 按需导入vue
前端·javascript·vue.js
Gungnirss1 小时前
vue中提示Parsing error: No Babel config file detected
前端·vue.js·ubuntu
梅秃头2 小时前
vue2+elementUI实现handleSelectionChange批量删除-前后端
前端·javascript·elementui