本文讲述浏览器一些实用的API。包含如下内容 Worker
、localStorage
、 Servlet
、Canvas
、 Svg
。由于浏览器API知识确实很多,本文将以系统概括和deme展示的方式带你快速学习理解这些API。深入掌握请阅读demo源码+多动手练习。
- 由于JS一开始设计的很简单 -- 单线程, JS执行与DOM互斥
- 再加上JS开发的web环境中 -- 使用解释型语言, 运行较慢
- 为了web体验,提出了 Worker方案,
- 将复杂运算 不涉及dom操作的任务放在其它线程中并行处理,进而解决渲染卡顿的问题。
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');
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()
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);
})
jsimportScripts('script1.js');
// 也可以加载多个脚本
importScripts('script1.js', 'script2.js');
window
document
parent
这些对象navigator
和 location
alert()
方法和confirm()
方法,但可以使用XMLHttpRequest对象发出ajax请求file://
),它所加载的脚本必须来自网络更多关于WebWorker: 《Web Worker使用教程》
sessionStorage
与 localStorage
的区别是:localStorage
可以长期保留, sessionStorage
当前会话结束(页面被关闭时)会被清除。 除此之外,它们两的用法及特性几乎一致
localStorage.setItem('myCat', 'tom');
或localStorage.myCat='tom';
localStorage.getItem('myCat');
或 localStorage.myCat
js// index.html
CSS.paintWorklet.addModule('paint-grid.js');
// paint-grid.js
registerPaint('transparent-grid', class {
// ...
}}
- 现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;
- LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。
- 所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景
特点:
indexedDB相关概念
对比mysql数据库, 快速上手indexdb
用途:重客户端应用,客户端游戏、chrome扩展开发
入门示例
这个AI助手项目,就是使用indexedDB存储,关于index部分代码很独立,你可以看一些这文件,帮你快速入门indexedDB
本段参考资料:
Canvas 是html5 新增的API
坐标 Canvas画布提供了一个作图的平面空间,该空间每个点都有自己的坐标。
画布
width
和 height
默认画布宽度300px 高度150pxwidth
和 height
属性一致,否则会出现拉伸路径
save()
和 restore()
).store()
和 .fill()
方法会自动闭合路径事件
动画
关于Canvas学习重点在于练习, 先推荐一些学习资料
主要参考上述文章,我写了一些极简demo,demo有详细的注释
感兴趣的同学 点这里, 打开开发者工具看一下源码。
为了使用编辑器API提示效果,demo是使用TS写的,熟悉TS的同学 可以修改文件后缀名就可以查看ts代码, 如 canvas.js canvas.ts
Canvas的用途
SVG是指可伸缩矢量图形
相对于传统图片它有体积小和放大不失真的优势
对比 canvas与svg
- | Canvas | Svg |
---|---|---|
描述图形方式 | 使用JavaScript描述2D图形 绘制完成浏览器不在关注 位置变更需要重新渲染 | 使用XML描述2D图形。 每个被绘制的图形被称为对象, 对象属性发生变化会重新渲染 |
分辨率 | 基于像素绘制,依赖分辨率 | 基于图形绘制,放大不失真 |
事件 | 不支持事件处理器,需要手动编程 用户交互到像素 | 支持事件编程 用户交互到路径 |
元素 | 单个Canvas元素 | 多种图形元素 |
操作 | 只能脚本渲染 | 支持脚本和css |
- | 能够以 .png 或 .jpg 格式保存结果图像 | 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快) |
使用场景 | 适合小面积,大数量应用场景(图像密集型游戏) | 适合大面积小数量应用场景(如google地图) |
主要参考 《终于会用SVG画图了》我写了一些demo
感兴趣的同学 点这里, 打开开发者工具看一下源码。
移动端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
是采用 requestIdleCallback()
实现,也就是说该API异步执行且优先级很低,如果你滑动过快可能不被触发写不如练, 我这边写了四个关于IntersectionObserver的demo,代码极简且有详尽注释,希望能带你快速掌握IntersectionObserver。
感兴趣的同学 点这里, 打开开发者工具看一下源码。
本段内容主要参考资料 阮一峰 WebAPI 教程
WebComponents 不是单一的规范,而是由一些列技术组成
Custom Elements
Template
Shadow DOM
HTML Import
已被废弃使用时,并不一定上面四种 API 都要用到。其中,Custom Element
和 Shadow DOM
比较重要,Template
和 HTML Import
只起到辅助作用。
Custom Elements
customElement.define('my-ele', MyELement)
定义组件customElement.get('my-ele')
获取类, 可以通过new再次创建customElement.whenDefine('my-ele', callback)
定义组件后触发static get observedAttributes
和 attributedChangedCallback
监听属性变化this.dispatchEvent(new CustomEvent('myevent', {detail: ''}))
constructor
创建时被调用attributeChangedCallback
当自定义元素的一个属性被添加、移除或修改时,这个方法会被调用。connectedCallback
第一次被连接到文档 DOM 时被调用'adoptedCallback
当自定义元素被移入新文档后调用(需借助iframe演示)disconnectedCallback
当自定义元素从文档中移除时,这个方法会被调用。这时可以进行一些资源清理操作。Template
Shadow DOM
open
closed
设置为 open
则JS可以通过element.shadowRoot
属性来访问ShadowDOM内部的结构服务器向客户端推送数据,有很多解决方案。除了“轮询” 和 WebSocket,HTML 5 还提供了 Server-Sent Events(以下简称 SSE)。
服务端向客户端声明,接下来要发送的是流信息,这时客户端就不会关闭连接,会一直等待服务端发送过来的新的数据流
与websocket比较。总体来说WebSocket更强大灵活。因为他是全双工通信
- | Websocket | SSE |
---|---|---|
通信方式 | 全双工通信 | 单向通信 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长度等限制、跨域等限制。
XMLHTTPRequest
对象XMLHTTPRequest
所有输入、输出、状态都在一个接口管理,容易写出非常混乱的代码,而 fetch
将它们分散在几个不同的对象上(设计更合理化),另外返回Promise对象(避免嵌套的回调函数)XMLHTTPRequest
对象不支持数据流,所有数据必须放在缓存里,等到全部都拿到后,再一次性吐出来。Headers
, Request
和Response
作用
更多关于 MessageChannel 推荐阅读 《浅谈MessageChannel》
共享存储
localStorage
IndexDB
+ JS定时器查询cookie
+ JS定时器查询
中介者模式
navigator.serviceWorker
serviceWorker优势也十分明显,它位于浏览器主控进程,生存周期比页面长,因此可以存储离线消息BroadCast Channel
该方式很简便,但兼容性方面不乐观SharedWorker
+ 定时器服务端推送
SSE
有跨域限制, 但可以借助fetch规避websocket
无跨域限制其他
如果是后者通过前者打开 还可以使用 open/opener
方式
关于具体实现方案,推荐阅读 《面试官:前端跨页面通信,你知道哪些方法?》
主页面和iframe之间的通信
postMessage
MessageChange
在DOM3中定义了Mutation events
该API是同步执行,由于它本身是事件所以捕获采用事件冒泡的机制,如果捕获期间由触发了其他的Mutation Events的话,很可能就导致JS主线程阻塞,甚至是浏览器崩溃。
未解决 Mutation events
同步执行和事件冒泡的问题, DOM4定义了Mutation Observer
用于取代Mutation events
。它采用异步执行,每次变更记录在消息队列中,最后统一执行
更多关于 MutationObserver 推荐阅读 《MutationObserver用法总结》
这里主要是借助 前端文件压缩上传、下载的案例来学习 Blob
File
FileReader
Blob URL
Canvas
FormData
之间的联系
先来看一下它们之间的关系图
Blob
与 File
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);
File
转 Blob
Canvas
ctx.drawImage(imgEle)
对图像进行复制、压缩尺寸、剪裁展示等canvas.toBlob
进行图片格式转换及压缩。FileReader
File
或Blob
转换为base64FormData
表单上传的一种方式 Content-Type: multipart/form-data
用于上传含有文件的表单FormData
formData.append("file", file);
base64
通过 FileReader
将File
或Blob
转换为base64<a>
和Blob URL
下载ArrayBuffer
累了,以后在学我写了一个 图片压缩上传下载 案例 ,有兴趣的同学可以点进去看一下,敲一下代码,可以帮你快速掌握这些API
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!