Cookie API

Cookie 是一小段保存在用户端的信息,用于改善用户的网络浏览体验,通常包括用户的选项或识别信息,用户可以选择网站使用 Cookie 的方式

可用使用 navigator.cookieEnabled 判断页面是否允许使用 Cookie

可以通过 Set-Cookie 响应头设置 Cookie 或使用 JS 脚本 document.cookie 设置 Cookie

1
2
HTTP/2.0 200 OK
Set-Cookie: cookie_a=a
1
document.cookie = 'cookie_b=b';

执行请求时可以通过 Cookie 请求头自动带上 Cookie

JS 脚本中可以通过 document.cookie 读取 Cookie

1
const cookies = document.cookie;
  • Expires 属性

    指定 Cookie 在给定时间后失效

  • Max-Age 属性

    指定 Cookie 在超出给定时长后失效

  • Secure 属性

    指定 Cookie 仅在 Secure Context 下才发送;并且非 Secure Context 下无法设置该属性

  • HttpOnly 属性

    指定 Cookie 无法通过 document.cookie 读取和修改

    常用于防御 XSS 攻击

  • Domain 属性

    指定 Cookie 可用的域名(及子域名),默认会包含当前的域名

  • Path 属性

    指定 Cookie 可用的路径(及子路径)

  • SameSite 属性

    指定 Cookie 是否在跨域请求中发送

    常用于防御 CSRF 攻击

    Strict 值指定仅在同域请求中发送

    Lax 值允许在用户导航至 Cookie 的域名时发送,该值是默认的行为

    None 值指定允许在跨域请求中发送,但需同时指定 Secure 属性

1
Set-Cookie: cookie_b=b; Expires=Thu, 31 Oct 2021 07:28:00 GMT; Secure; HttpOnly; Domain=example.com; Path=/; SameSite=Strict
  • __Host- 前缀

    指定对应的 Cookie 需指定 Secure 属性(即需要在 Secure Context 发送),不得指定 Domain 属性,Path 属性需指定为 /

  • __Secure- 前缀

    指定对应的 Cookie 需指定 Secure 属性(即需要在 Secure Context 发送)

链接

Geolocation API

Geolocation API 提供了访问用户地理位置的方法

通过 navigator.geolocation 暴露的 Geolocation 接口实例访问

使用 API 需要向用户请求授权并获得允许,并且仅在 Secure Context 环境下启用

获取地理位置

使用 Geolocation 接口的 getCurrentPosition() 方法获取地理位置

方法需要传入一个在获取地理位置成功时调用的回调函数,该回调函数会被传递一个 GeolocationPosition 参数

方法可以传入一个在获取地理位置失败时调用的回调函数,该回调函数会被传递一个 GeolocationPositionError 参数

方法同样可以传入一个可选的配置项:

maximumAge 可选选项接收一个正数值,指定使用缓存的地理位置的允许的最长的期限,默认为 0

timeout 可选选项接收一个正数值,指定获取地理位置等待的超时时间,默认为 Infinity

enableHighAccuracy 可选选项接收一个布尔值,指定是否尝试获取最精确的可能值,默认值 false

权限策略

该 API 调用受到 geolocation 权限策略的控制,可以通过 Permissions-Policy 响应头指定,或通过 <iframe> 标签的 allow 属性指定

默认值是 *,即允许任意源的浏览上下文使用该 API

权限 API

该 API 调用需要获得用户 geolocation 权限的允许,可以调用 Permission.query() 方法检查用户是否已授予了该权限

1
2
3
4
5
6
7
8
9
10
11
12
13
navigator.geolocation.getCurrentPosition(
(position) => {
// todo something with the position data
},
(error) => {
// todo something when getting position failed
},
{
maximumAge: 0,
timeout: Infinity,
enableHighAccuracy: false,
}
)

监听地理位置

使用 Geolocation 接口的 watchPosition() 方法注册监听地理位置的更新

方法的参数类似于 getCurrentPosition() 方法

方法返回一个数字,代表注册回调的 ID,可用于 clearWatch() 方法取消监听

使用 Geolocation 接口的 clearWatch() 方法取消注册监听地理位置的更新

地理位置信息

GeolocationPosition 接口用于表示一组地址位置信息记录

coords 只读属性代表一个 GeolocationCoordinates 实例,表示地址位置信息的具体内容

timestamp 只读属性返回一个时间戳,代表获取地址位置信息的时间点

GeolocationCoordinates 接口用于表示一个地址位置信息详情

latitude 只读属性代表纬度

longitude 只读属性代表经度

accuracy 只读属性代表经纬度精度

altitude 只读属性代表海平面高度,设备不支持时返回 null

altitudeAccuracy 只读属性代表海平面高度精度,设备不支持时返回 null

heading 只读属性代表设备方向,设备不支持时返回 null

speed 只读属性代表设备移动速度,设备不支持时返回 null

异常处理

GeolocationPositionError 接口表示获取地理位置失败的异常

code 只读属性代表错误状态码,可能为常量枚举 PERMISSION_DENIEDPOSITION_UNAVAILABLETIMEOUT 之一,常量枚举可以通过 GeolocationPositionError 本身或其实例访问

message 只读属性代表错误信息,通常是用于调试目的而非直接向用户展示

权限策略

该 API 调用受到 geolocation 权限策略的控制,可以通过 Permissions-Policy 响应头指定,或通过 <iframe> 标签的 allow 属性指定

默认为 self,即允许在当前上下文或内嵌的其他同源上下文中使用

权限 API

该 API 调用需要用户授予 geolocation 权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
interface Navigator {
readonly geolocation: Geolocation
}

interface Geolocation {
clearWatch(watchId: number): void
getCurrentPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback | null, options?: PositionOptions): void
watchPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback | null, options?: PositionOptions): number
}

interface PositionCallback {
(position: GeolocationPosition): void
}

interface PositionErrorCallback {
(positionError: GeolocationPositionError): void
}

interface PositionOptions {
enableHighAccuracy?: boolean
maximumAge?: number
timeout?: number
}

interface GeolocationPosition {
readonly coords: GeolocationCoordinates
readonly timestamp: EpochTimeStamp
}

interface GeolocationPositionError {
readonly code: number
readonly message: string
readonly PERMISSION_DENIED: 1
readonly POSITION_UNAVAILABLE: 2
readonly TIMEOUT: 3
}

interface GeolocationCoordinates {
readonly accuracy: number
readonly altitude: number | null
readonly altitudeAccuracy: number | null
readonly heading: number | null
readonly latitude: number
readonly longitude: number
readonly speed: number | null
}

链接

File System API

File System API 允许使用内置文件系统,包含读取、修改和移除文件,扩展的 File System Access API 允许访问本地文件系统

  • FileSystemHandle 接口作为文件句柄或目录句柄的通用接口
  • FileSystemFileHandle 接口作为文件句柄的接口
  • FileSystemDirectoryHandle 接口作为目录句柄的接口
  • FileSystemSyncAccessHandle 接口作为同步访问句柄的接口
  • FileSystemWritableFileStream 接口作为读写本地文件流的接口
  • StorageManager 接口上的 getDirectory() 方法用于获取 Origin Private File System 的根目录句柄

通常而言,File System API 需要用户明确的许可,但 OPFS —— Origin Private File System 机制除外。

通常 File System API 的大多数操作是异步的,支持同步的 FileSystemSyncAccessHandle 接口仅在 Web Worker 内可用且仅允许使用于 OPFS。

初始化

获取 OPFS 的根目录句柄

使用 StorageManager 接口的 getDirectory() 方法获取 OPFS 的根目录句柄

返回一个 Promise 的 FileSystemDirectoryHandle 对象

并在用户代理无法使用请求的文件目录建立本地 OPFS 索引时抛出 SecurityError 异常

文件及目录操作

通过 FileSystemHandle 接口进行文件目录相关的操作,该接口是 FileSystemFileHandle 接口与 FileSystemDirectoryHandle 接口的父接口

句柄类型

FileSystemHandle 接口的 kind 属性返回一个字符串枚举值,代表句柄的类型

当当前句柄为 FileSystemFileHandle 时,返回 'file'

当当前句柄为 FileSystemDirectoryHandle 时,返回 'directory'

句柄名称

FileSystemHandle 接口的 name 属性返回一个字符串,代表句柄对应的文件或目录的名称

句柄比较

FileSystemHandle 接口的 isSameEntry() 方法判断当前句柄与传入的句柄是否指向同一个文件或目录

方法接收一个 FileSystemHandle

方法返回一个 Promise 的 boolean

权限操作

FileSystemHandle 接口的 queryPermission() 方法用于枚举当前句柄的指定权限

FileSystemHandle 接口的 requestPermission() 方法用于请求当前句柄的指定权限

方法均支持传入一组可选的描述符,唯一 mode 参数可以为 'read''readwrite' 之一,默认为 'read'

方法均返回一个 Promise 的 PermissionStatus 接口实例

文件操作

通过 FileSystemFileHandle 接口进行文件相关的操作,该接口继承自 FileSystemHandle 接口

获取文件

FileSystemFileHandle 接口的 getFile() 方法可以用于读取文件相关信息及用于网络传输

返回一个 Promise 的 File 对象

抛出一个 NotAllowedError 若未授予访问权限

获取同步访问句柄

FileSystemFileHandle 接口的 createSyncAccessHandle() 方法可以用于同步读写文件内容,但仅允许在专属 Worker 内部使用,且目前仅适用于 OPFS 机制

返回一个 Promise 的 FileSystemSyncAccessHandle 对象

抛出一个 NotAllowedError 若未授予访问权限

抛出一个 NoModificationAllowedError 若无法建立文件锁

抛出一个 InvalidStateError 若句柄无法表示 OPFS 中的文件

获取可写文件流

FileSystemFileHandle 接口的 createWritable() 方法可以用于修改文件内容

可以传入一组可选的配置项,唯一选项 keepExistingData 指定

返回一个 Promise 的 FileSystemWritableFileStream 对象

抛出一个 NotAllowedError 若未授予访问权限

目录操作

通过 FileSystemDirectoryHandle 接口进行目录相关的操作,该接口继承自 FileSystemHandle 接口

目录遍历

FileSystemDirectoryHandle 接口支持异步遍历,包含 [@@asyncIterator]() 以及 entries()keys()values() 等方法

获取路径

FileSystemDirectoryHandle 接口的 resolve() 方法用于获取当前句柄到指定句柄的相对路径

需要传入一个 FileSystemHandle,代表目标句柄

返回一个 Promise 的字符串数组或 null

获取子目录

FileSystemDirectoryHandle 接口的 getDirectoryHandle() 方法用于获取当前目录下的指定名称的子目录的句柄

需要传入一个字符串参数,代表目录的名称,与 FileSystemHandle.name 相符

可以传入一组配置项,唯一参数 create 为一个布尔值,指定目录不存在情况下是否创建目录,默认值为 false

返回一个 Promise 的 FileSystemDirectoryHandle 对象

抛出一个 TypeErrorname 参数不是字符串或为非法的文件系统名称

抛出一个 TypeMismatchError 若匹配到的为文件而非目录

抛出一个 NotFoundError 若未找到目录且 create 选项设定为 false

抛出一个 NotAllowedError 若未授予访问权限

获取子文件

FileSystemDirectoryHandle 接口的 getFileHandle() 方法用于获取当前目录下的指定名称的子文件的句柄

需要传入一个字符串参数,代表目录的名称,与 FileSystemHandle.name 相符

可以传入一组配置项,唯一参数 create 为一个布尔值,指定目录不存在情况下是否创建目录,默认值为 false

返回一个 Promise 的 FileSystemFileHandle 对象

抛出一个 TypeErrorname 参数不是字符串或为非法的文件系统名称

抛出一个 TypeMismatchError 若匹配到的为目录而非文件

抛出一个 NotFoundError 若未找到文件且 create 选项设定为 false

抛出一个 NotAllowedError 若未授予访问权限

删除子目录或子文件

FileSystemDirectoryHandle 接口的 removeEntry() 方法用于删除当前目录下的指定名称的子目录或子文件的句柄

需要传入一个字符串参数,代表目录的名称,与 FileSystemHandle.name 相符

可以传入一组配置项,唯一参数 recursive 为一个布尔值,指定是否递归删除,默认值为 false

返回一个 Promise 的 undefined

抛出一个 TypeErrorname 参数不是字符串或为非法的文件系统名称

抛出一个 InvalidModificationError 若目标为目录,并且包含子文件或子目录,并且 recursive 设置为 false

抛出一个 NotFoundError 若未找到文件或目录

抛出一个 NotAllowedError 若未授予访问权限

文件读取

文件读取通过 FileSystemFileHandle 接口的 getFile() 方法获取到对应的 File 实例实现

文件修改

文件修改通过 FileSystemFileHandle 接口的 createWritable() 方法获取到对应的 FileSystemWritableFileStream 实例实现

FileSystemWritableFileStream 接口继承自 WritableStream 接口

FileSystemWritableFileStream 接口的更改不会立即反映到实际的文件上,仅在关闭流之后才会同步其产生的更改;原因是对流的更改,至少会存储到一个临时文件中,仅在流关闭之后,才会将更改同步到实际的文件中

移动指针

使用 FileSystemWritableFileStream 接口的 seek() 方法移动文件指针的位置

需要传入一个正整数 position 参数,代表文件指针的位置

返回一个 Promise

抛出一个 NotAllowedError 若未授予访问权限

抛出一个 TypeErrorposition 参数不是正整数或未传递

文件写入

使用 FileSystemWritableFileStream 接口的 write() 方法用于向文件中写入内容

可以传入一个 data 参数,代表需要写入文件的内容,可以是 ArrayBuffer TypedArray DataView Blobstring

亦可以传入一组配置项:

type 选项传入一组字符串枚举,指定操作的模式,可以是 "write" "seek""truncate"

data 选项,代表需要写入文件的内容,可以是 ArrayBuffer TypedArray DataView Blobstring,在 "write" 模式下是必须的

position 选项,代表需要移动的文件指针的目标位置,是一个正整数,在 "seek" 模式下是必须的;同样可以在 "write" 模式下使用,此时代表写入内容的目标位置

size 选项,代表需要文件流的大小,是一个正整数,在 "truncate" 模式下是必须的

返回一个 Promise

抛出一个 NotAllowedError 若未授予访问权限

抛出一个 TypeError 若传入参数非法

抛出一个 InvalidStateErrorposition 选项的值超出文件大小

文件尺寸修改

使用 FileSystemWritableFileStream 接口的 truncate() 方法改变文件的尺寸;方法可能会改变文件指针的位置

需要传入一个正整数 size 参数,代表目标的文件尺寸;若参数超出原有尺寸,则扩大当前文件并使用空内容填充扩大的部分,反之会裁剪当前文件

返回一个 Promise

抛出一个 NotAllowedError 若未授予访问权限

抛出一个 TypeErrorsize 参数不是正整数或未传递

文件同步读写

文件同步读写通过 FileSystemFileHandle 接口的 createSyncAccessHandle() 方法获取到对应的 FileSystemSyncAccessHandle 实例实现

FileSystemSyncAccessHandle 实例仅支持在专属 Worker 中使用,且目前仅适用于 OPFS 机制

因为其无需进行权限检查,其相对而言具有更好的性能

创建 FileSystemSyncAccessHandle 实例会创建与之对应的文件锁,阻止对该文件创建其他的 FileSystemSyncAccessHandle 实例或 FileSystemWritableFileStream 实例,直到 FileSystemSyncAccessHandle 实例被销毁

FileSystemSyncAccessHandle 接口的 read() 方法读取文件内容

方法传入一个 buffer 参数,可以是 ArrayBuffer SharedArrayBuffer TypedArrayDataView,代表用于存储读取文件内容的缓存区

方法支持传入一个可选的配置项:其 at 参数指定开始读取文件内容的起始位置

方法返回一个正整数,代表读取的文件内容的字节数

方法在若对应的句柄已关闭的情况下,抛出一个 InvalidStateError

FileSystemSyncAccessHandle 接口的 write() 方法向文件写入内容

方法传入一个 buffer 参数,可以是 ArrayBuffer SharedArrayBuffer TypedArrayDataView,代表将用于写入的文件内容

方法可以传入一个可选的配置项:其 at 参数指定开始写入文件内容的起始位置

方法返回一个正整数,代表写入的文件内容的字节数

方法在若对应的句柄已关闭的情况下,抛出一个 InvalidStateError

读取尺寸

FileSystemSyncAccessHandle 接口的 getSize() 方法返回文件的尺寸

方法返回一个正整数,代表文件内容的字节数

方法在若对应的句柄已关闭的情况下,抛出一个 InvalidStateError

更改尺寸

FileSystemSyncAccessHandle 接口的 truncate() 方法用于更改文件的尺寸

方法传入一个 newSize 参数,需要是一个正整数,代表将更改的文件的目标大小

方法在若对应的句柄已关闭的情况下,抛出一个 InvalidStateError

方法在若对应的句柄已关闭的情况下,抛出一个 InvalidStateError

刷新缓冲区

FileSystemSyncAccessHandle 接口的 flush() 方法用于将缓冲的更改同步至存储,通常只在特定时间段需要将缓冲的更改同步至存储时使用,否则可以让底层自行处理

关闭句柄

FileSystemSyncAccessHandle 接口的 close() 方法用于关闭当前句柄,释放文件锁

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
interface FileSystemHandle {
readonly kind: FileSystemHandleKind
readonly name: string
isSameEntry(other: FileSystemHandle): Promise<boolean>
}

interface FileSystemFileHandle extends FileSystemHandle {
readonly kind: 'file'
createSyncAccessHandle(): Promise<FileSystemSyncAccessHandle>
createWritable(options?: FileSystemCreateWritableOptions): Promise<FileSystemWritableFileStream>
getFile(): Promise<File>
}

interface FileSystemDirectoryHandle extends FileSystemHandle {
readonly kind: 'directory'
getDirectoryHandle(name: string, options?: FileSystemGetDirectoryOptions): Promise<FileSystemDirectoryHandle>
getFileHandle(name: string, options?: FileSystemGetFileOptions): Promise<FileSystemFileHandle>
removeEntry(name: string, options?: FileSystemRemoveOptions): Promise<void>
resolve(possibleDescendant: FileSystemHandle): Promise<string[] | null>
}

interface FileSystemWritableFileStream extends WritableStream {
seek(position: number): Promise<void>
truncate(size: number): Promise<void>
write(data: FileSystemWriteChunkType): Promise<void>
}

interface FileSystemSyncAccessHandle {
close(): void;
flush(): void;
getSize(): number;
read(buffer: AllowSharedBufferSource, options?: FileSystemReadWriteOptions): number;
truncate(newSize: number): void;
write(buffer: AllowSharedBufferSource, options?: FileSystemReadWriteOptions): number;
}

interface FileSystemCreateWritableOptions {
keepExistingData?: boolean
}

interface FileSystemGetDirectoryOptions {
create?: boolean
}

interface FileSystemGetFileOptions {
create?: boolean
}

interface FileSystemReadWriteOptions {
at?: number
}

interface FileSystemRemoveOptions {
recursive?: boolean
}

type FileSystemWriteChunkType = BufferSource | Blob | string | WriteParams

链接

Window Controls Overlay API

Window Controls Overlay API 给 PWA 应用提供了管理默认系统的应用标题栏的能力,允许应用完全掌控应用窗口的区域,不过仅支持 PC 端 PWA 应用

该 API 通过 WindowControlsOverlay 接口提供了相关功能,并通过 navigator.windowControlsOverlay 对外暴露该接口实例

使用该 API 需要在 PWA 应用的 Manifest 文件的 display_override 选项指定 window-controls-overlay

WindowControlsOverlay 的信息

WindowControlsOverlay 接口的 visible 只读属性表示了应用标题栏的可见性

WindowControlsOverlay 接口的 getTitlebarAreaRect() 方法返回了应用标题栏的几何信息,方法返回一个 DOMRect 接口实例

WindowControlsOverlay 接口的 geometrychange 事件在应用标题栏的可见性和几何信息变化时触发,事件返回一个 WindowControlsOverlayGeometryChangeEvent 事件实例

1
2
3
4
5
6
7
8
9
10
11
navigator.windowControlsOverlay.addEventListener('geometrychange', (e) => {
const { visible, titlebarAreaRect: rect } = e

if (visible) {
console.log('visible')

console.log('rect info', rect)
} else {
console.log('not visible')
}
})

相关的 CSS 环境变量

  • titlebar-area-x 应用标题栏左上角横坐标

  • titlebar-area-y 应用标题栏左上角纵坐标

  • titlebar-area-width 应用标题栏宽度

  • titlebar-area-height 应用标题栏高度

可以通过 env() CSS 函数使用 CSS 环境变量

1
2
3
4
env(titlebar-area-x)
env(titlebar-area-y)
env(titlebar-area-width)
env(titlebar-area-height)

示例

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Navigator {
readonly windowControlsOverlay: WindowControlsOverlay
}

interface WindowControlsOverlay extends EventTarget {
readonly visible: boolean
getTitlebarAreaRect(): DOMRect
ongeometrychange: ((this: WindowControlsOverlay, ev: WindowControlsOverlayGeometryChangeEvent) => any) | null
}

interface WindowControlsOverlayGeometryChangeEvent extends Event {
readonly titlebarAreaRect: DOMRect
readonly visible: boolean
}

链接

Badging API

Badging API 用于设置 PWA 应用的图标上的徽章信息

该 API 需要在 Secure Context 下使用

某些情况下该 API 需要请求用户授予 notifications 权限,并可以调用 Notification.requestPermission() 方法来请求获取相关权限

设置 Badge

使用 Navigator 接口上的 setAppBadge() 方法给图标设置徽章

方法支持传递一个可选的数字参数,徽章将显示为对应的数字;若未传递,徽章将显示为对应的点

方法返回一个 Promise 的 undefined

方法不支持时会抛出 NotSupportedError 异常

1
2
navigator.setAppBadge()
navigator.setAppBadge(10)

清除 Badge

使用 Navigator 接口的 clearAppBadge() 方法清除图标上设置的徽章

方法返回一个 Promise 的 undefined

方法不支持时会抛出 NotSupportedError 异常

1
2
navigator.setAppBadge(0)
navigator.clearAppBadge()

使用 Navigator 接口的 setAppBadge() 方法并传递参数 0 同样具有类似的效果

权限 API

该 API 调用需要用户授予 notifications 权限,可以调用 Permission.query() 方法或读取 Notification.permission 属性检查用户是否已授予了该权限

示例

类型

1
2
3
4
5
6
7
8
9
interface Navigator {
clearAppBadge(): Promise<void>
setAppBadge(contents?: number): Promise<void>
}

interface WorkerNavigator {
clearAppBadge(): Promise<void>
setAppBadge(contents?: number): Promise<void>
}

链接

Content Index API

Content Index API 允许网站注册离线启用的内容,向用户告知网站支持的离线内容并允许开发者对其进行管理

该 API 仅支持 HTML 文档对应的 URL,不支持如媒体资源等类型的 URL

该 API 仅支持列举已注册的内容,不支持进行查找等操作

一般的使用方式是利用一个列表页,用于展示已注册的内容

需要注意的是,该 API 并非直接缓存内容,实际的缓存需要利用 Cache Storage 等策略实施

介于该 API 依赖于 ServiceWorker,因此该 API 同样需在 Secure Context 下使用,且需遵循同源策略

通过 ServiceWorkerRegistration.index 暴露 ContentIndex 接口实例使用

添加离线内容

通过 ContentIndex 接口的 add() 方法添加离线内容

方法支持传入一组配置项

  • 参数 id 指定离线内容的唯一标识符
  • 参数 url 指定离线内容的 URL,需要与当前网页或脚本同源
  • 参数 title 指定离线内容的标题
  • 参数 description 指定离线内容的描述
  • 可选参数 icons 指定离线内容的图标组,每组图标对象支持指定 src 参数和 可选的 sizestype 参数,默认值是一个空数组
  • 可选参数 category 指定离线内容的类别,可选的值为 '''homepage''article''video''audio',默认值是 ''

方法返回一个 Promise 的 undefined

方法在以下情况下会抛出一个 TypeError 异常

  • 当前 ContentIndex 对应的 ServiceWorker 未激活或 ServiceWorker 未包含 FetchEvent
  • idtitledescriptionurl 参数未指定或参数类型不为字符串或参数为空串
  • icons 参数某个 icon 的 URL 的类型不是图像或获取对应 icon 出现网络异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.registration.index.add({
id: 'post',
url: '/posts/post.html',
title: 'Post',
description: 'Post Information',
icons: [
{
src: '/media/dark.png',
sizes: '128x128',
type: 'image/png',
},
],
category: 'article',
})

获取离线内容

通过 ContentIndex 接口的 getAll() 方法获取离线内容

方法返回一个 Promise 的代表离线内容的数组,结构同 ContentIndex.add() 方法的配置项参数

1
self.registration.index.getAll()

删除离线内容

通过 ContentIndex 接口的 delete() 方法删除离线内容

方法传入一个代表待删除的离线内容的 id 的字符串

方法返回一个 Promise 的 undefined

1
2
3
4
5
self.registration.index.delete(id).then(() => (
self.caches.open('v1').then((cache) => (
cache.delete(e.id)
))
))

需要注意的是,调用该方法同时,需要手动从存储中移除对应的离线内容

此外,当离线内容被通过用户代理移除而非手动调用 ContentIndex.delete() 方法移除时,会在 ServiceWorker 全局触发 contentdelete 事件,并返回一个 ContentIndexEvent 事件

ContentIndexEvent 事件继承自 ExtendableEvent 事件,其属性 id 反映了被删除的离线内容的 id

1
2
3
4
5
6
7
self.addEventListener('contentdelete', (e) => {
e.waitUntil(
self.caches.open('v1').then((cache) => (
cache.delete(e.id)
))
)
})

通常利用该事件同步移除与待移除页面的相关的资源存储

示例

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type ContentCategory = '' | 'homepage' | 'article' | 'video' | 'audio'

interface ContentDescription {
id: string
title: string
description: string
category?: ContentCategory
icons?: ImageResource[];
url: string
}

interface ContentIndex {
add(description: ContentDescription): Promise<undefined>
delete(id: string): Promise<undefined>
getAll(): Promise<ContentDescription[]>
};

链接

PWA

PWA 即 Progressive Web App ———— 渐进式网络应用

PWA 技术允许用户像 Native 应用一样使用 Web 应用,支持多平台和多设备访问,并且支持在线或离线访问

PWA 技术的核心是 ServiceWorker 技术,并基于 ServiceWorker 技术支持离线访问

PWA 特点

  • 可通过 URL 访问并被搜索引擎抓取
  • 可以安装到本地(A2HS)
  • 可以使用 URL 分享
  • 可以在离线状态访问
  • 适配老版浏览器访问,并支持在新浏览器中使用更多新特性
  • 支持在有新内容时更新
  • 能适配各种尺寸屏幕
  • 仅通过 HTTPS 提供服务

PWA 创建

  1. 页面通过 link 标签引入 manifest 文件

    文件后缀名可以是 json 或者 webapp、webmanifest

    1
    <link rel="manifest" href="manifest.json" />

    该文件是一个 JSON 的语法,必须指定的项为 nameicons(对于 Chromium 系浏览器 start_urldisplaydisplay_override 也是需要指定的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "name": "My PWA",
    "icons": [
    {
    "src": "icons/512.png",
    "type": "image/png",
    "sizes": "512x512"
    }
    ]
    }

    name 指定应用的名称,接受一个字符串

    icons 指定应用的图标,接受一个数组,数组各项可以指定 srcsizestype 项,分别代表图标的 URL、尺寸及 MIME 类型

    manifest.json 文件的详细配置可以参考相关的文档

  2. 页面中注册 ServiceWorker,且 ServiceWorker 中监听了 fetch 事件

  3. 页面须启用 Secure Context,即使用 HTTPS 协议或者为本地资源

PWA 下载

PWA 下载可以通过浏览器访问对应的网页实现

通常浏览器检测到网页支持下载为 PWA 时,会显示一个默认的“下载为 PWA”的按钮,也可以自定义“下载为 PWA”的按钮

通过监听全局的 beforeinstallprompt 事件获取到 BeforeInstallPromptEvent 事件实例并存储,通常该事件在页面加载时即触发;在必要时刻调用 BeforeInstallPromptEvent.prompt() 方法以使用户确认下载 PWA 应用;下载完成后会在全局触发 appinstalled 事件

PWA 下载亦可以通过应用商店等场景下载

PWA 原理

PWA 实质是仅将应用行为上类似于原生应用,如在桌面显示图标,在应用列表显示,支持卸载等;并不会将应用程序的资源文件主动下载至本地,具体策略由开发者通过 IndexedDB、ServiceWorker、Cache Storage 等开发实施

示例

ServiceWorker VI

ServiceWorker 导航预加载

导航预加载通过 NavigationPreloadManager 接口提供,并通过 ServiceWorkerRegistration.navigationPreload 属性暴露

对于使用 ServiceWorker 的页面,网页的网络请求会向 ServiceWorker 发送 fetch 事件直至返回响应,若此时 ServiceWorker 未启动,网页的网络请求会等待 ServiceWorker 激活后再进行处理;导航预加载允许网页的获取资源请求在 ServiceWorker 激活前提前开始下载,以避免阻碍页面的显示

启用导航预加载

NavigationPreloadManager 接口的 enable() 方法用于启用资源预加载管理

停用导航预加载

NavigationPreloadManager 接口的 disable() 方法用于停用资源预加载管理

管理导航预加载

NavigationPreloadManager 接口的 setHeaderValue() 方法用于设置导航预加载中发送的请求的请求头 Service-Worker-Navigation-Preload 的值

NavigationPreloadManager 接口的 getState() 方法用于获取导航预加载的状态

基本使用

启用导航预加载

1
2
3
4
5
self.addEventListener('activate', (e) => {
e.waitUntil(
self.registration.navigationPreload.enable()
)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
self.addEventListener('fetch', (e) => {
e.responseWith(
(async () => {
const cache = await self.caches.match(e.request)

if (cache != null) {
return cache
}

const preload = await e.preloadResponse

if (preload != null) {
return preload
}

return fetch(e.request)
})
)
})

链接

面试-工程

Git 常用指令

  • git init 创建一个空 git 代码库或初始化现有的 git 代码库
  • git clone 从远程克隆一个代码库到本地
  • git pull 从远程仓库或其他本地分支获取更新并与当前仓库分支合并
  • git push 将本地分支更新推送到远程仓库
  • git fetch 从远程仓库获取更新
  • git add 添加更新到暂存区
  • git commit 提交暂存区到仓库区

常见前端网络安全漏洞

  • XSS 攻击 - 跨站脚本攻击

攻击者向网站中注入恶意代码,使得其在浏览器中执行,并用于恶意目的如盗取客户端信息等

浏览器将恶意代码当成正常代码的一部分执行而导致

避免服务端拼接代码;充分转义将插入代码的内容;指定 CSP 策略,控制允许加载和执行的外部资源;敏感资源保护,如 Cookie 的 HttpOnly 策略、验证码等

  • CSRF 攻击 - 跨站请求伪造攻击

攻击者诱导用户进入第三方网站,随后第三方网站向被攻击网站发送跨站请求,利用用户保存的登录态执行恶意操作

Cookie 会在同源请求中自动携带并发送给服务器

进行同源检测,利用 origin 头和 referer 头;设置 Cookie 为 SameOrigin,阻止第三方页面使用该 Cookie;启用 Cookie 双重验证,将 Cookie 放入请求参数;使用 CSRF Token,请求参数中使用专属 token 验证身份

面试-JavaScript

宏任务与微任务

宏任务(普通脚本,setTimeout()setInterval(),I/O,UI 渲染)

微任务(PromiseMutationObserver

特殊任务 process.nextTick() 当前 event loop tick 将结束时且下一个 event loop tick 将开始时执行

特殊任务 setImmediate() 下一轮 event loop tick 的 macrotask 阶段将结束时执行

事件循环(Node)

  • 同步任务

  • 发出异步请求

  • 规划定时器生效的时间

  • 执行 process.nextTick() 的回调

  • timers - 处理 setTimeout()setInterval() 的回调函数

  • I/O callbacks - 剩余的回调函数(setTimeout()setInterval()setImmediate() 及关闭请求回调函数之外)

  • idle, prepare - 内部使用

  • poll - 轮询时间,等待还未返回的 I/O 事件

  • check - 执行 setImmediate() 的回调

  • close callbacks - 关闭请求的回调

继承模式

  • 原型链继承

    1
    2
    SubType.prototype = new SuperType()
    SubType.prototype.constructor = SubType
  • 构造函数继承

    1
    SuperType.call(this, superProp)
  • 组合式继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function SuperType(superProp){
    this.superProp = superProp
    }
    SuperType.prototype.printSuperProp = function(){
    console.log(this.superProp)
    }

    function SubType(superProp, subProp){
    SuperType.call(this, superProp)
    this.subProp = subProp
    }
    SubType.prototype = new SuperType()
    SubType.prototype.constructor = SubType
    SubType.prototype.printSubProp = function(){
    console.log(this.subProp)
    }
  • 寄生组合式继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function SuperType(superProp){
    this.superProp = superProp
    }
    SuperType.prototype.printSuperProp = function(){
    console.log(this.superProp)
    }

    function SubType(superProp, subProp){
    SuperType.call(this, superProp)
    this.subProp = subProp
    }
    SubType.prototype = Object.create(SuperType.prototype)
    SubType.prototype.constructor = SubType
    SubType.prototype.printSubProp = function(){
    console.log(this.subProp)
    }

Object 三种冻结方式

  • Object.preventExtensions() | Object.isExtensible() | 阻止添加属性,原型不可更改

  • Object.seal() | Object.isSealed() | 阻止添加属性,阻止移除属性,阻止配置属性(configurable 为 false),原型不可更改

  • Object.freeze() | Object.isFrozen() | 阻止添加属性,阻止移除属性,阻止修改属性值,阻止配置属性(writable、configurable 为 false),原型不可更改


:D 一言句子获取中...