2023-07-15
浏览器
00
请注意,本文编写于 505 天前,最后修改于 367 天前,其中某些信息可能已经过时。

目录

Worker
Worker的用法
Worker的限制
localStorage 与 sessionStorage
Servlet
IndexedDB
Canvas
Svg
Page Visibility API
IntersectionObserver
WebComponents
Server-Sent Events
Fetch
MessageChannel
浏览器多个tab页如何通信
MutationObserver
文件压缩上传、下载
废弃的API

本文讲述浏览器一些实用的API。包含如下内容 WorkerlocalStorageServletCanvasSvg。由于浏览器API知识确实很多,本文将以系统概括和deme展示的方式带你快速学习理解这些API。深入掌握请阅读demo源码+多动手练习。

image.png

更详细内容点击这里

Worker

  • 由于JS一开始设计的很简单 -- 单线程, JS执行与DOM互斥
  • 再加上JS开发的web环境中 -- 使用解释型语言, 运行较慢
  • 为了web体验,提出了 Worker方案,
  • 将复杂运算 不涉及dom操作的任务放在其它线程中并行处理,进而解决渲染卡顿的问题。

Worker的用法

js
// main.js const worker = new Worker('./worker.js') worker.onmessage = function(e) { console.log(`收到 web worker 消息 "${e.data}"`, e); // 1 setTimeout(() => { worker.postMessage('你好,我是主线程,我收到你的消息') }); } // worker.js self.onmessage = function(e) { console.log(`收到主线程消息 "${e.data}"`, e); // 2 } self.postMessage('1. 你好,我是web worker');
  • self表示子线程自身,以下三种写法作用
js
// 写法1 self.addEventListener('message', function (e) { self.postMessage('You said: ' + e.data); }, false); // 写法2 self.onmessage('message', function (e) { self.postMessage('You said: ' + e.data); }, false); // 写法3 this.addEventListener('message', function (e) { this.postMessage('You said: ' + e.data); }, false); // 写法4 addEventListener('message', function (e) { postMessage('You said: ' + e.data); }, false);
  • 关闭Worker

    • 主线程 worker.terminate()
    • worker线程 self.close()
  • 错误处理 主线程可以监听Worker是否发生错误,

js
// 主线程 worker.onerror(function (event) { console.log([ 'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message ].join('')); }); // 或者 worker.addEventListener('error', function (event) { // ... }); // worker线程也可以监听错误 self.addEventListener('error', function(e){ console.log('worker线程发生错误', e); })
  • 加载脚本
js
importScripts('script1.js'); // 也可以加载多个脚本 importScripts('script1.js', 'script2.js');

Worker的限制

  • 同源限制
  • DOM限制
    • 无法使用window document parent这些对象
    • 可以使用navigatorlocation
  • 通信限制
    • Web Worker是在渲染进程中新开辟的线程,与主线程不能直接通信,需要使用postMessage
  • 脚本限制
    • 不能执行alert()方法和confirm()方法,但可以使用XMLHttpRequest对象发出ajax请求
  • 文件限制
    • 不能加载本地文件(file://),它所加载的脚本必须来自网络

更多关于WebWorker: 《Web Worker使用教程》

localStorage 与 sessionStorage

sessionStoragelocalStorage的区别是:localStorage可以长期保留, sessionStorage当前会话结束(页面被关闭时)会被清除。 除此之外,它们两的用法及特性几乎一致

  • 遵循同源策略(协议、域名、端口一致)
  • 设置 localStorage.setItem('myCat', 'tom');localStorage.myCat='tom';
  • 读取 localStorage.getItem('myCat');localStorage.myCat

Servlet

js
// index.html CSS.paintWorklet.addModule('paint-grid.js'); // paint-grid.js registerPaint('transparent-grid', class { // ... }}

更多关于 Servlet 请看我的另一篇文章

IndexedDB

  • 现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;
  • LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。
  • 所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景

特点:

  • 键值对储存 json格式天然对JS友好
  • 异步
    • localStorage是同步,且存取时 序列化与反序列化比较耗费性能
    • 可以用于异步通信 如ServiceWork与WebWorker的中介
  • 支持事务
  • 同源限制
  • 储存空间大 500MB
  • 支持String Number 对象、数组、Date、File、blob、arraybuffer、二进制等

indexedDB相关概念

  • 数据库: IDBDatabase
  • 对象仓库: IDBObjectStore
  • 索引: IDBIndex对象
  • 事务:IDBTransaction
  • IDBRequest
  • IDBCursor
  • IDBKeyRange

对比mysql数据库, 快速上手indexdb

image.png

用途:重客户端应用,客户端游戏、chrome扩展开发

入门示例

这个AI助手项目,就是使用indexedDB存储,关于index部分代码很独立,你可以看一些这文件,帮你快速入门indexedDB

本段参考资料:

Canvas

Canvas 是html5 新增的API

坐标 Canvas画布提供了一个作图的平面空间,该空间每个点都有自己的坐标。

  • 原点(0,0)位于图像的左上角.
  • x轴的正向是原点向右
  • y轴的正向是原点向右

画布

  • Canvas标签默认只有两个属性 widthheight 默认画布宽度300px 高度150px
  • 如果想用CSS定义画布宽高, 必须与 Canvas widthheight 属性一致,否则会出现拉伸

路径

  • Canvas绘图如同在拿笔纸上绘图一样,需要移动画笔,这就是路径
  • 路径同样式一样也是一种状态,绘图时注意恢复状态(save()restore()
  • .store().fill()方法会自动闭合路径
  • 路径可以复制、组合等。

事件

  • 无法针对某个图形进行事件处理,只能监听整个canvas画板,然后对根据光标坐标、画板位置、图形位置进行逻辑分析和编程。

动画

  • 动画的过程就是清空画板,重新配置。

关于Canvas学习重点在于练习, 先推荐一些学习资料

主要参考上述文章,我写了一些极简demo,demo有详细的注释

image.png

感兴趣的同学 点这里, 打开开发者工具看一下源码。

为了使用编辑器API提示效果,demo是使用TS写的,熟悉TS的同学 可以修改文件后缀名就可以查看ts代码, 如 canvas.js canvas.ts

Canvas的用途

  • 一般不做可视化、游戏相关项目基本用不到Canvas
  • 普通项目中也有一些场景需要用到Canvas如下
    • 估算文本宽度 如何获取本本宽度
    • 0.5px 边框
    • 水印
    • html内容转图片
    • 地图天气绘制 (雨、雪、冰雹、雾)

Svg

SVG是指可伸缩矢量图形

相对于传统图片它有体积小和放大不失真的优势

对比 canvas与svg

-CanvasSvg
描述图形方式使用JavaScript描述2D图形
绘制完成浏览器不在关注
位置变更需要重新渲染
使用XML描述2D图形。
每个被绘制的图形被称为对象,
对象属性发生变化会重新渲染
分辨率基于像素绘制,依赖分辨率基于图形绘制,放大不失真
事件不支持事件处理器,需要手动编程
用户交互到像素
支持事件编程
用户交互到路径
元素单个Canvas元素多种图形元素
操作只能脚本渲染支持脚本和css
-能够以 .png 或 .jpg 格式保存结果图像复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
使用场景适合小面积,大数量应用场景(图像密集型游戏)适合大面积小数量应用场景(如google地图)

主要参考 《终于会用SVG画图了》我写了一些demo

image.png

感兴趣的同学 点这里, 打开开发者工具看一下源码。

Page Visibility API

移动端h5应用,有一些场景 如埋点需求、性能优化(切到后台避免不必要的请求和渲染)需要开发者知道页面切换到后台或者被销毁。

pagehide beforeunload unload 在移动端可能不会被触发

后来HTML5新增了document.visibilityState API

  • 它有三个值 hidden visiable至少一部分可见 prerender页面即将或正在渲染,处于不可见状态
  • 它覆盖了页面被卸载的所有情况

由于历史原因还保留document.hidden 仅当document.visibilityState属性返回visible时,它的值时false,其他都是true,包括prerender状态, 所以只要有可能我们都是应该使用document.visibilityState

visibilitychange事件

  • 只要document.visibilityState 发生改变都会触发 visibilitychange事件

总结

  • 现在浏览器只需要使用visibilitychange事件即可
  • beforeunload事件只有一种适用场景,就是用户修改了表单,没有提交就离开当前页面。

IntersectionObserver

开发中有一些场景需要用到IntersectionObserver

  • 内容曝光、卡片曝光
  • 图片懒加载
  • 视频播放
  • 页面按模块内容懒加载(提高FP首屏渲染体验)

注意:

  • IntersectionObserver是采用 requestIdleCallback()实现,也就是说该API异步执行且优先级很低,如果你滑动过快可能不被触发

写不如练, 我这边写了四个关于IntersectionObserver的demo,代码极简且有详尽注释,希望能带你快速掌握IntersectionObserver。

image.png

感兴趣的同学 点这里, 打开开发者工具看一下源码。

本段内容主要参考资料 阮一峰 WebAPI 教程

WebComponents

WebComponents 不是单一的规范,而是由一些列技术组成

  • Custom Elements
  • Template
  • Shadow DOM
  • HTML Import 已被废弃

使用时,并不一定上面四种 API 都要用到。其中,Custom ElementShadow DOM 比较重要,TemplateHTML Import 只起到辅助作用。

Custom Elements

  • web components的核心, 唯有它是必须的。
  • customElement.define('my-ele', MyELement) 定义组件
  • customElement.get('my-ele') 获取类, 可以通过new再次创建
  • customElement.whenDefine('my-ele', callback) 定义组件后触发
  • attribute
    • 设置属性 1) HTML元素上写属性 2) setAttribute动态设置
    • WC类通过 static get observedAttributesattributedChangedCallback 监听属性变化
  • prop
    • DOM元素也是JS对象, 是对象都可以设置属性,通过此方式来传递JSON数据
    • 通过 get set 拦截器动态更新
  • slot
    • WC的插槽比较简单,仅支持默认插槽和具名插槽,不支持类似Vue的作用域插槽
  • 事件
    • 派发事件 this.dispatchEvent(new CustomEvent('myevent', {detail: ''}))
  • 生命周期
    • constructor 创建时被调用
    • attributeChangedCallback 当自定义元素的一个属性被添加、移除或修改时,这个方法会被调用。
    • connectedCallback 第一次被连接到文档 DOM 时被调用'
    • adoptedCallback 当自定义元素被移入新文档后调用(需借助iframe演示)
    • disconnectedCallback 当自定义元素从文档中移除时,这个方法会被调用。这时可以进行一些资源清理操作。

Template

  • 就是一个代码片段,浏览器解析时会跳过

Shadow DOM

  • Shadow DOM 内部样式是隔离的,即不受外部影响,也不影响外部
  • 如果不想样式隔离 可以不使用ShadowDOM
  • mode有两个值open closed 设置为 open 则JS可以通过element.shadowRoot 属性来访问ShadowDOM内部的结构

这里写了一些webcomponent使用demo

Server-Sent Events

服务器向客户端推送数据,有很多解决方案。除了“轮询” 和 WebSocket,HTML 5 还提供了 Server-Sent Events(以下简称 SSE)。

服务端向客户端声明,接下来要发送的是流信息,这时客户端就不会关闭连接,会一直等待服务端发送过来的新的数据流

与websocket比较。总体来说WebSocket更强大灵活。因为他是全双工通信

-WebsocketSSE
通信方式全双工通信单向通信
steaming本质上是下载
协议基于HTTP升级后的一种新协议使用HTTP协议
重连机制需要自己实现断线重连默认支持断线重连
数据类型支持文本、二进制消息一般只用来传输文本,二进制数据需要编码后传送

综上 websocket属于重量级武器 SSE属于轻量级武器,两者各有特点,适合不同的场景

关于SSE的学习 推荐这篇文章《Server-Sent Event》

这是参考这篇文章,我写了个nodejs的domo, 可以下载下面两个文件本地运行,体验一下SSE

应用案例

ChatGPT的打字效果就是使用SSE实现的, 略有不同的一点是 在SSE中客户端使用 EventSource构建请求, 而chatGPT 使用的是 fetch请求。

推测原因是 EventSource只能发出GET请求(url长度最多2000字符),且不支持自定义请求头, 而fetch请求要模拟 onopen onmessage onerror事件和close方法,另外要了解stream机制。更多可以参考这篇文章 《chatGPT打自机消息回复机制》

总结: SSE通过借助fetch解除不能自定义header、url长度等限制、跨域等限制。

Fetch

  • Fecth API是一种新规范,用来取代XMLHTTPRequest对象
  • 由于XMLHTTPRequest 所有输入、输出、状态都在一个接口管理,容易写出非常混乱的代码,而 fetch将它们分散在几个不同的对象上(设计更合理化),另外返回Promise对象(避免嵌套的回调函数)
  • Fetch的另一个好处是支持大文件传输,而传统的XMLHTTPRequest对象不支持数据流,所有数据必须放在缓存里,等到全部都拿到后,再一次性吐出来。
  • Fetch API 引入三个新的对象(也是构造函数):Headers, RequestResponse

MessageChannel

  • 它是react时间切片的实现原理, 因此它是一个宏任务
  • 它的功能很类似EventEmmiter,区别是异步(宏任务)执行

作用

  • 实现两个worker的直接通信
  • 数据copy 利用消息在发送和接收过程需要序列化和反序列化

更多关于 MessageChannel 推荐阅读 《浅谈MessageChannel》

浏览器多个tab页如何通信

  1. 前端跨页面通信如同CORS跨域请求一样需要双发都配合才能进行
  2. 非同源页面的通信可以借助iframe中转转换为同源页面之间的通信
  3. 同源页面之间的通信 大致可以分为三类 1>共享存储 2>中介者模式 3>服务端推送

共享存储

  1. localStorage
    • A页面window.addEventListener('storage')
      B页面 localStorage.setItem
  2. IndexDB + JS定时器查询
  3. cookie + JS定时器查询
    • cookie方案的优缺点都非常明显,缺点 1> 单个cookie最多4KB 2> 增加网络带宽 优点:同源限制程度低 可借助根域名存储 不受协议和端口限制

中介者模式

  • navigator.serviceWorker serviceWorker优势也十分明显,它位于浏览器主控进程,生存周期比页面长,因此可以存储离线消息
  • BroadCast Channel 该方式很简便,但兼容性方面不乐观
  • SharedWorker + 定时器

服务端推送

  • SSE 有跨域限制, 但可以借助fetch规避
  • websocket 无跨域限制
  • 长轮询

其他 如果是后者通过前者打开 还可以使用 open/opener方式

关于具体实现方案,推荐阅读 《面试官:前端跨页面通信,你知道哪些方法?》  

主页面和iframe之间的通信

MutationObserver

在DOM3中定义了Mutation events该API是同步执行,由于它本身是事件所以捕获采用事件冒泡的机制,如果捕获期间由触发了其他的Mutation Events的话,很可能就导致JS主线程阻塞,甚至是浏览器崩溃。

未解决 Mutation events 同步执行和事件冒泡的问题, DOM4定义了Mutation Observer用于取代Mutation events。它采用异步执行,每次变更记录在消息队列中,最后统一执行

更多关于 MutationObserver 推荐阅读 《MutationObserver用法总结》

文件压缩上传、下载

这里主要是借助 前端文件压缩上传、下载的案例来学习 Blob File FileReader Blob URL Canvas FormData之间的联系

先来看一下它们之间的关系图

blob.webp

  • BlobFile
    • Blob 二进制大文件对象 只读, File 继承自 Blob
    • <input type='file'>得到的就是File对象
    • 通过new File 可转换为File文件
  • Blob URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等的 URL 源。
    • 形式blob:<origin>/<uuid> 可借助<a>下载
    • 通过URL.createObjectURL(file) 转换为Blob URL
    • Blob URL可以借助new File([Blob], fileName, {type})转换为 File
    • fileData.slice(0, fileData.size, fileData.type); FileBlob
  • Canvas
    • 可以通过ctx.drawImage(imgEle)对图像进行复制、压缩尺寸、剪裁展示等
    • 在通过canvas.toBlob进行图片格式转换及压缩。
  • FileReader
    • FileBlob转换为base64
  • FormData 表单上传的一种方式 Content-Type: multipart/form-data 用于上传含有文件的表单
  • 上传文件有两种方式
    • FormData formData.append("file", file);
    • base64 通过 FileReaderFileBlob转换为base64
  • 保存图片 借助<a>Blob URL下载
  • ArrayBuffer 累了,以后在学

我写了一个 图片压缩上传下载 案例 ,有兴趣的同学可以点进去看一下,敲一下代码,可以帮你快速掌握这些API

废弃的API

  • offline应用
  • WebSQL
  • Mutation events

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!