vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)...

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第1张图片

作者:阿宝哥

转发链接:https://segmentfault.com/a/1190000023011282

目录

「多图」一文带你彻底搞懂 Web Workers (上)本篇

「多图」一文带你彻底搞懂 Web Workers (中)

「多图」一文带你彻底搞懂 Web Workers (下)

前言

阅读完本文你将学到以下知识:

  • 进程与线程的区别:进程与线程的概念及单线程与多线程;
  • 浏览器内核的相关知识:GUI 渲染线程、JavaScript 引擎线程、事件触发线程等;
  • Web Workers 是什么:Web Workers 的限制与能力及主线程与 Web Workers 之间如何通信;
  • Web Workers 的分类:Dedicated Worker、Shared Worker 和 Service Workers;
  • Web Workers API:Worker 构造函数及如何观察 Dedicated Worker 等。

下面我们开始步入正题,为了让大家能够更好地理解和掌握 Web Workers,在正式介绍 Web Workers 之前,我们先来介绍一些与 Web Workers 相关的基础知识。

一、进程与线程的区别

在介绍进程与线程的概念前,我们先来看个进程与线程之间关系形象的比喻:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第2张图片

如上图所示,进程是一个工厂,它有独立的资源,线程是工厂中的工人,多个工人协作完成任务,工人之间共享工厂内的资源,比如工厂内的食堂或餐厅。此外,工厂(进程)与工厂(进程)之间是相互独立的。为了让大家能够更直观地理解进程与线程的区别,我们继续来看张图:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第3张图片

由上图可知,操作系统会为每个进程分配独立的内存空间,一个进程由一个或多个线程组成,同个进程下的各个线程之间共享程序的内存空间。相信通过前面两张图,小伙伴们对进程和线程之间的区别已经有了一定的了解,那么实际情况是不是这样呢?这里我们打开 macOS 操作系统下的活动监视器,来看一下写作本文时所有进程的状态:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第4张图片

通过上图可知,我们常用的软件,比如微信和搜狗输入法都是一个独立的进程,拥有不同的 PID(进程 ID),而且图中的每个进程都含有多个线程,以微信进程为例,它就含有 36 个线程。那么什么是进程和线程呢?下面我们来介绍进程和线程的概念。

1.1 进程的概念

进程(英语:process),是指计算机中已运行的程序。进程曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的 UNIX,Linux 2.4 及更早的版本)中,进程是程序的基本执行实体;在面向线程设计的系统(如当代多数操作系统、Linux 2.6 及更新的版本)中,进程本身不是基本运行单位,而是线程的容器。

程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。若干进程有可能与同一个程序相关系,且每个进程皆可以同步或异步的方式独立运行。现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中,并借由时间共享(或称时分复用),以在一个处理器上表现出同时运行的感觉。

1.2 线程的概念

线程(英语:thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如 Win32 线程;由用户进程自行调度的用户线程,如 Linux 平台的 POSIX Thread;或者由内核与用户进程,如 Windows 7 的线程,进行混合调度。

同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。 但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。一个进程可以有很多线程,每条线程并行执行不同的任务。

1.3 单线程与多线程

如果一个进程只有一个线程,我们称之为单线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。单线程处理的优点:同步应用程序的开发比较容易,但由于需要在上一个任务完成后才能开始新的任务,所以其效率通常比多线程应用程序低。

如果完成同步任务所用的时间比预计时间长,应用程序可能会不响应。针对这个问题,我们可以考虑使用多线程,即在进程中使用多个线程,这样就可以处理多个任务。

对于 Web 开发者熟悉的 JavaScript 来说,它运行在浏览器中,是单线程的,每个窗口一个 JavaScript 线程,既然是单线程的,在某个特定的时刻,只有特定的代码能够被执行,其它的代码会被阻塞。

JS 中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS 的设计初衷就没有考虑这些,针对 JS 这种不具备并行任务处理的特性,我们称之为 “单线程”。 —— 来自知乎 “如何证明 JavaScript 是单线程的?” @云澹的回答

其实在浏览器内核(渲染进程)中除了 JavaScript 引擎线程之外,还含有 GUI 渲染线程、事件触发线程、定时触发器线程等。因此对于浏览器的渲染进程来说,它是多线程的。接下来我们来简单介绍浏览器内核。

二、浏览器内核

浏览器最核心的部分是 “Rendering Engine”,即 “渲染引擎”,不过我们一般习惯将之称为 “浏览器内核”。 它主要包括以下线程:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第5张图片

下面我们来分别介绍渲染过程中的每个线程。

2.1 GUI 渲染线程

GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。

2.2 JavaScript 引擎线程

JavaScript 引擎线程负责解析 JavaScript 脚本并运行相关代码。 JavaScript 引擎一直等待着任务队列中任务的到来,然后进行处理,一个Tab页(Renderer 进程)中无论什么时候都只有一个 JavaScript 线程在运行 JavaScript 程序。

需要注意的是,GUI 渲染线程与 JavaScript 引擎线程是互斥的,所以如果 JavaScript 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染被阻塞。

2.3 事件触发线程

当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于 JavaScript 引擎是单线程的,所有这些事件都得排队等待 JavaScript 引擎处理。

2.4 定时触发器线程

浏览器定时计数器并不是由 JavaScript 引擎计数的,这是因为 JavaScript 引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确,所以通过单独线程来计时并触发定时是更为合理的方案。我们日常开发中常用的 setInterval 和 setTimeout 就在该线程中。

2.5 Http 异步请求线程

在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。

前面我们已经知道了,由于 JavaScript 引擎与 GUI 渲染线程是互斥的,如果 JavaScript 引擎执行了一些计算密集型或高延迟的任务,那么会导致 GUI 渲染线程被阻塞或拖慢。那么如何解决这个问题呢?嘿嘿,当然是使用本文的主角 —— Web Workers。

三、Web Workers 是什么

Web Worker 是 HTML5 标准的一部分,这一规范定义了一套 API,它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中。Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。

在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,可以在独立线程中处理一些计算密集型或高延迟的任务,从而允许主线程(通常是 UI 线程)不会因此被阻塞或拖慢。

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第6张图片

(图片来源:https://thecodersblog.com/web...)

3.1 Web Workers 的限制与能力

通常情况下,你可以在 Worker 线程中运行任意的代码,但注意存在一些例外情况,比如:直接在 worker 线程中操纵 DOM 元素,或使用 window 对象中的某些方法和属性。 大部分 window 对象的方法和属性是可以使用的,包括 WebSockets,以及诸如 IndexedDB 和 FireFox OS 中独有的 Data Store API 这一类数据存储机制。

下面我们以 Chrome 和 Opera 所使用的 Blink 渲染引擎为例,介绍该渲染引擎下 Web Worker 中所支持的常用 APIs:

  • Cache:Cache 接口为缓存的 Request / Response 对象对提供存储机制,例如,作为ServiceWorker 生命周期的一部分。
  • CustomEvent:用于创建自定义事件。
  • Fetch:Fetch API 提供了一个获取资源的接口(包括跨域请求)。任何使用过 XMLHttpRequest 的人都能轻松上手,而且新的 API 提供了更强大和灵活的功能集。
  • Promise:Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
  • FileReader:FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
  • IndexedDB:IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件/二进制大型对象(blobs)。
  • WebSocket:WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
  • XMLHttpRequest:XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。

更多信息请参见: Functions and classes available to workers 。

3.2 主线程与 Web Workers 之间的通信

主线程和 Worker 线程相互之间使用 postMessage() 方法来发送信息,并且通过 onmessage 这个事件处理器来接收信息。数据的交互方式为传递副本,而不是直接共享数据。主线程与 Worker 线程的交互方式如下图所示:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第7张图片

(图片来源:https://viblo.asia/p/simple-web-workers-workflow-with-webpack-3P0lPkobZox)

除此之外,Worker 还可以通过 XMLHttpRequest 来访问网络,只不过 XMLHttpRequest 对象的 responseXML 和 channel 这两个属性的值将总是 null 。

四、Web Workers 的分类

Web Worker 规范中定义了两类工作线程,分别是专用线程 Dedicated Worker 和共享线程 Shared Worker,其中,Dedicated Worker 只能为一个页面所使用,而 Shared Worker 则可以被多个页面所共享。

4.1 Dedicated Worker

一个专用 Worker 仅仅能被生成它的脚本所使用,其浏览器支持情况如下:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第8张图片

(图片来源:https://caniuse.com/#search=Web%20Workers)

需要注意的是,由于 Web Worker 有同源限制,所以在进行本地调试或运行以下示例的时候,需要先启动本地服务器,直接使用 file:// 协议打开页面的时候,会抛出以下异常:

Uncaught DOMException: Failed to construct 'Worker': Script at 'file:///**/*.js' cannot be accessed from origin 'null'.

4.1.1 专用线程 Dedicated Worker:Ping/Pong

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第9张图片

index.html

      专用线程 Dedicated Worker —— Ping/Pong

阿宝哥:专用线程 Dedicated Worker —— Ping/Pong

dw-ping-pong.js

onmessage = (e) => {  console.log(`Worker: Received message - ${e.data}`);  postMessage("PONG");}

以上代码成功运行后,浏览器控制台会输出以下结果:

Worker: Received message - PINGMain: Received message - PONG

每个 Web Worker 都可以创建自己的子 Worker,这允许我们将任务分散到多个线程。创建子 Worker 也很简单,具体我们来看个例子。

4.1.2 专用线程 Dedicated Sub Worker:Ping/Pong

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第10张图片

index.html

      专用线程 Dedicated Sub Worker —— Ping/Pong

阿宝哥:专用线程 Dedicated Sub Worker —— Ping/Pong

dw-ping-pong.js

onmessage = (e) => {  console.log(`Worker: Received message - ${e.data}`);  setTimeout(() => {    let worker = new Worker("dw-sub-ping-pong.js");    worker.onmessage = (e) => console.log(`Worker: Received from sub worker - ${e.data}`);    worker.postMessage("PING");  }, 1000);  postMessage("PONG");};

dw-sub-ping-pong.js

onmessage = (e) => {  console.log(`Sub Worker: Received message - ${e.data}`);  postMessage("PONG");};

以上代码成功运行后,浏览器控制台会输出以下结果:

Worker: Received message - PINGMain: Received message - PONGSub Worker: Received message - PINGReceived from sub worker - PONG

4.1.3 专用线程 Dedicated Worker:importScripts

其实在 Web Worker 中,我们也可以使用 importScripts 方法将一个或多个脚本同步导入到 Web Worker 的作用域中。同样我们来举个例子。

index.html

      专用线程 Dedicated Worker —— importScripts

阿宝哥:专用线程 Dedicated Worker —— importScripts

worker.js

importScripts("https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js");onmessage = ({ data }) => {  postMessage(_.kebabCase(data));};

以上代码成功运行后,浏览器控制台会输出以下结果:

Main: Received kebab case message - hello-my-name-is-semlinker

4.1.4 专用线程 Dedicated Worker:inline-worker

在前面的例子中,我们都是使用外部的 Worker 脚本来创建 Web Worker 对象。其实你也可以通过 Blob URL 或 Data URL 的形式来创建 Web Worker,这类 Worker 也被称为 Inline Worker。

1. 使用 Blob URL 创建 Inline Worker

Blob URL/Object URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等的 URL 源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,其形式为 blob:/,对应的示例如下:

blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。它允许引用 、 中的 Blob,但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。

const url = URL.createObjectURL(  new Blob([`postMessage("Dedicated Worker created by Blob")`]));let worker = new Worker(url);worker.onmessage = (e) =>  console.log(`Main: Received message - ${e.data}`);

除了在代码中使用字符串动态创建 Worker 脚本,也可以把 Worker 脚本使用类型为 javascript/worker 的 script 标签内嵌在页面中,具体如下所示:

接着就是通过 script 对象的 textContent 属性来获取对应的内容,然后使用 Blob API 和 createObjectURL API 来最终创建 Web Worker:

2. 使用 Data URL 创建 Inline Worker

Data URLs 由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:

data:[][;base64],

mediatype 是个 MIME 类型的字符串,例如 "image/jpeg" 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。如果数据是文本类型,你可以直接将文本嵌入(根据文档类型,使用合适的实体字符或转义字符)。如果是二进制数据,你可以将数据进行 base64 编码之后再进行嵌入。

const url = `data:application/javascript,${encodeURIComponent(  `postMessage("Dedicated Worker created by Data URL")`)}`;let worker = new Worker(url);worker.onmessage = (e) =>  console.log(`Main: Received message - ${e.data}`);

4.2 Shared Worker

一个共享 Worker 是一种特殊类型的 Worker,可以被多个浏览上下文访问,比如多个 windows,iframes 和 workers,但这些浏览上下文必须同源。相比 dedicated workers,它们拥有不同的作用域。其浏览器支持情况如下:

vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)..._第11张图片

(图片来源:https://caniuse.com/#search=Web%20Workers)

与常规的 Worker 不同,首先我们需要使用 onconnect 方法等待连接,然后我们获得一个端口,该端口是我们与窗口之间的连接。

4.2.1 共享线程 Shared Worker:点赞计数器

index.html

      共享线程 Shared Worker

阿宝哥:共享线程 Shared Worker

点赞

阿宝哥一共收获了0

本篇未完结,请见下一篇

推荐JavaScript学习相关文章

《深入细聊前端下载总结「干货」》

《细品西瓜播放器功能分析(上)「实践」》

《细品西瓜播放器功能分析(下)「实践」》

《细聊50道JavaScript基础面试题「附答案」》

《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》

《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》

《细聊前端架构师的视野》

《细聊应用场景再谈防抖和节流「进阶篇」》

《前端埋点统一接入方案实践》

《细聊微内核架构在前端的应用「干货」》

《一种高性能的Tree组件实现方案「干货」》

《进击的JAMStack》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下》

《一文带你搞懂 babel-plugin-import 插件(上)「源码解析」》

《一文带你搞懂 babel-plugin-import 插件(下)「源码解析」》

《JavaScript常用API合集汇总「值得收藏」》

《推荐10个常用的图片处理小帮手(上)「值得收藏」》

《推荐10个常用的图片处理小帮手(下)「值得收藏」》

《JavaScript 中ES6代理的实际用例》

《12 个实用的前端开发技巧总结》

《一文带你搞懂搭建企业级的 npm 私有仓库》

《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》

《细说前端开发UI公共组件的新认识「实践」》

《细说DOM API中append和appendChild的三个不同点》

《细品淘系大佬讲前端新人如何上王者「干货」》

《一文带你彻底解决背景跟随弹窗滚动问题「干货」》

《推荐常用的5款代码比较工具「值得收藏」》

《Node.js实现将文字与图片合成技巧》

《爱奇艺云剪辑Web端的技术实现》

《我再也不敢说我会写前端 Button组件「实践」》

《NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」》

《Node Buffers 完整指南》

《推荐18个webpack精美插件「干货」》

《前端开发需要了解常用7种JavaScript设计模式》

《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》

《了不起的 Webpack HMR 学习指南(上)「含源码讲解」》

《了不起的 Webpack HMR 学习指南(下)「含源码讲解」》

《10个打开了我新世界大门的 WebAPI(上)「实践」》

《10个打开了我新世界大门的 WebAPI(中)「实践」》

《10个打开了我新世界大门的 WebAPI(下)「实践」》

《「图文」ESLint 在中大型团队的应用实践》

《Deno是代码的浏览器,你认同吗?》

《前端存储除了 localStorage 还有啥?》

《Javascript 多线程编程​的前世今生》

《微前端方案 qiankun(实践及总结)》

《「图文」V8 垃圾回收原来这么简单?》

《Webpack 5模块联邦引发微前端的革命?》

《基于 Web 端的人脸识别身份验证「实践」》

《「前端进阶」高性能渲染十万条数据(时间分片)》

《「前端进阶」高性能渲染十万条数据(虚拟列表)》

《图解 Promise 实现原理(一):基础实现》

《图解 Promise 实现原理(二):Promise 链式调用》

《图解 Promise 实现原理(三):Promise 原型方法实现》

《图解 Promise 实现原理(四):Promise 静态方法实现》

《实践教你从零构建前端 Lint 工作流「干货」》

《高性能多级多选级联组件开发「JS篇」》

《深入浅出讲解Node.js CLI 工具最佳实战》

《延迟加载图像以提高Web网站性能的五种方法「实践」》

《比较 JavaScript 对象的四种方式「实践」》

《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》

《前端如何一次性处理10万条数据「进阶篇」》

《推荐三款正则可视化工具「JS篇」》

《如何让用户选择是否离开当前页面?「JS篇」》

《JavaScript开发人员更喜欢Deno的五大原因》

《仅用18行JavaScript实现一个倒数计时器》

《图文细说JavaScript 的运行机制》

《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》

《推荐Web程序员常用的15个源代码编辑器》

《10个实用的JS技巧「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》

《深入JavaScript教你内存泄漏如何防范》

《手把手教你7个有趣的JavaScript 项目-上「附源码」》

《手把手教你7个有趣的JavaScript 项目-下「附源码」》

《JavaScript 使用 mediaDevices API 访问摄像头自拍》

《手把手教你前端代码如何做错误上报「JS篇」》

《一文让你彻底搞懂移动前端和Web 前端区别在哪里》

《63个JavaScript 正则大礼包「值得收藏」》

《提高你的 JavaScript 技能10 个问答题》

《JavaScript图表库的5个首选》

《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》

《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》

《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》

《可视化的 js:动态图演示 Promises & Async/Await 的过程》

《原生JS封装拖动验证滑块你会吗?「实践」》

《如何实现高性能的在线 PDF 预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug 3.0.0正式发布,不再支持 Node.js 6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript 20 年 中文版之创立标准》

《值得收藏的前端常用60余种工具方法「JS篇」》

《箭头函数和常规函数之间的 5 个区别》

《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》

《「前端篇」不再为正则烦恼》

《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》

《深入细品浏览器原理「流程图」》

《JavaScript 已进入第三个时代,未来将何去何从?》

《前端上传前预览文件 image、text、json、video、audio「实践」》

《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧「值得收藏」》

《前端必备基础知识:window.location 详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段「值得收藏」》

《Node + H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation 在 JS 中的应用「前端篇」》

作者:阿宝哥

转发链接:https://segmentfault.com/a/1190000023011282

你可能感兴趣的:(vs的windows应用程序上的鼠标为什么一直是加载状态?_「多图」一文带你彻底搞懂 Web Workers (上)...)