加载中,请稍等...

NPM Dependencies

dependencies

当前项目在自身开发阶段使用,且作为其他项目的依赖也应使用的依赖

devDependencies

仅在开发阶段使用的依赖,即仅当前项目在自身开发阶段使用,在作为其他项目的依赖时不应使用的依赖

peerDependencies

当前项目在自身开发阶段使用,且作为其他项目的依赖也应使用的依赖;但期望其由用户主动提供而非当前项目直接指定

可以通过 peerDependenciesMeta 字段更进一步指定其元信息,如对应依赖是否可选

bundleDependencies

当前项目在自身开发阶段使用,且作为其他项目的依赖也应使用的依赖;但期望该依赖在打包当前项目时一并合入

optionalDependencies

当前项目在自身开发阶段可选使用,且作为其他项目的依赖也可选使用的依赖

CSS New Feature

Safari 18.2 (2024/12/11)

  • @view-transition
  • view-transition-class
  • view-transition-name: auto
  • ruby-align
  • ruby-overhang
  • ruby-position
  • text-box-edge
  • text-box-trim
  • text-box
  • text-underline-position: left & text-underline-position: right
  • background-clip: border-area
  • scrollbar-gutter
  • scrollbar-width
  • @page @margin-*
  • @page { size: jis-b4 & size: jis-b5 }
  • closest-corner & farthest-corner
  • ::target-text
  • calc() unit value support
  • line-clamp: none
  • text-underline-offset percentage value support
  • <text-edge>

see Safari 18.2 Release Notes

Firefox 132 (2024/10/29)

  • text-emphasis-position: auto
  • nested declaration rule
  • -moz-user-modify removal

see Firefox 132 for developers

Firefox 131 (2024/10/01)

  • position-area renamed from inset-area

see Firefox 131 for developers

Safari 18.0 (2024/09/16)

  • content-visibility
  • view-transition-name
  • ::view-transition-group()
  • ::view-transition-image-pair()
  • ::view-transition-new()
  • ::view-transition-old()
  • ::view-transition
  • backdrop-filter
  • :active-view-transition
  • offset-path
  • hsl() hsla() update
  • rgb() rgba() update
  • hwb() update
  • <color> update

see Safari 18.2 Release Notes

Firefox 130 (2024/09/03)

  • hyphens Czech and Slovak support

see Firefox 130 for developers

CSS Meta Data

CSS 元属性(在规范定义中使用)

  • Name
    • 特性的名称
  • Value
    • 特性的合法的值的格式
  • Initial
    • 特性的初始值
  • Applies to
    • 特性作用的元素范围
  • Inherited
    • 特性是否可继承
  • Percentages
    • 特性是否可接受百分比值
  • Computed value
    • 特性指定值解析为计算值方式
  • Canonical order
    • 特性指定值序列化的规则
  • Animation type
    • 特性在动画中变换的类型
    • not animatable 不支持动画
    • discrete 无法有意义地组合变换
    • by computed value 使用该值类型指定的方式进行组合(插值,添加或累积)
    • repeatable list 同上,但优先重复至最小公倍数再按计算值组合;若不可组合则使用离散值

WebContainer

WebContainer 是一个基于 WebAssembly 的能在浏览器内执行 Node.js 应用程序和操作系统命令的库

基本使用

准备工作

由于 WebContainer 需要使用 SharedArrayBuffer 功能,而该功能需要设置跨域隔离 cross-origin isolated,因此需要设置 COOP 和 COEP 标头

1
2
3
4
Cross-Origin-Opener-Policy: same-origin

Cross-Origin-Embedder-Policy: require-corp
// or Cross-Origin-Embedder-Policy: credentialless

启动系统

调用 WebContainer 类的 boot 静态方法启动系统

1
2
3
import { WebContainer } from '@webcontainer/api'

const webcontainerInstance = await WebContainer.boot()

此方法只能被调用一次,在系统启动之后,系统注销之前,调用该方法会报错

挂载文件

调用 WebContainer 实例的 mount 方法一次性挂载大量文件到文件系统中,这比直接调用文件系统方法单次挂载文件更有校性能优势

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
const files = {
'index.js': {
file: {
contents: `
import express from 'express'

const app = express()

app.get('/', (req, res) => {
res.send('Welcome to a WebContainer app!')
})

app.listen(8888, () => {
console.log(\`App is live at http://localhost:8888\`)
})`,
},
},
'package.json': {
file: {
contents: `
{
"name": "webcontainer",
"type": "module",
"dependencies": {
"express": "latest",
"nodemon": "latest"
},
"scripts": {
"start": "nodemon index.js"
}
}`,
},
},
}

await webcontainerInstance.mount(files)

执行命令

调用 WebContainer 实例的 spawn 方法执行命令,如下载 npm 依赖

1
const installProcess = await webcontainerInstance.spawn('npm', ['install'])

或运行本地服务器

1
await webcontainerInstance.spawn('npm', ['run', 'start'])

监听事件

可以监听 WebContainer 实例上一系列事件以执行对应的操作

1
2
3
webcontainerInstance.on('server-ready', (port, url) => {
el.src = url
})

注销系统

调用 WebContainer 实例的 teardown 方法注销系统运行

1
webcontainerInstance.teardown()

文件系统机制

文件系统结构

文件系统结构中,包括文件,目录和链接,其数据结构如下所示:

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
{
// This is a file - provide its path as a key:
'package.json': {
// Because it's a file, add the "file" key
file: {
// Now add its contents
contents: `
{
"name": "vite-starter",
"private": true,
// ...
"devDependencies": {
"vite": "^4.0.4"
}
}
`,
},
},
src: {
// Because it's a directory, add the "directory" key
directory: {
// Here we will add files
// This is a file - provide its path as a key:
'main.js': {
// Because it's a file, add the "file" key
file: {
contents: `
console.log('Hello from WebContainers!')
`,
},
},
'main.cache.js': {
file: {
symlink: './main.js',
},
},
},
},
}

对于文件,通过 file.contents 键指定文件的内容

对于目录,通过 directory 键指定目录中包含的文件

对于链接,通过 file.contents 键指定链接所指向的目标

文件系统挂载

使用 WebContainer 实例的 mount 方法一次性挂载大量文件(及目录)到文件系统中

1
await webcontainerInstance.mount(files)

文件系统数据需要遵循 FileSystemTree 类型的结构(可以自 @webcontainer/api 中导出)

1
/** @type {import('@webcontainer/api').FileSystemTree} */

默认情况下,文件会被挂载到根目录下;可以指定 mountPoint 参数以挂载到其他目录下

1
await webcontainerInstance.mount(files, { mountPoint: 'custom-mount-point' })

文件系统操作

主要的文件系统操作包括读取文件、读取目录、删除文件目录、写入文件、创建目录,通过 WebContainer 实例的 fs 属性使用

  1. 读取文件 readFile

    1
    2
    const file = await webcontainerInstance.fs.readFile('/package.json')
    const file = await webcontainerInstance.fs.readFile('/package.json', 'utf-8')
  2. 读取目录 readdir

    1
    2
    3
    4
    5
    const files = await webcontainerInstance.fs.readdir('/src')

    for (const file of files) {
    console.log('file or dir name: ', file)
    }

    withFileTypes 参数指定返回值的是否为 Dirent 类型对象数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const files = await webcontainerInstance.fs.readdir('/src', {
    withFileTypes: true,
    })

    for (const file of files) {
    if (file.isFile()) {
    console.log('file name: ', file.name)
    }
    if (file.isDirectory()) {
    console.log('dir name: ', file.name)
    }
    }

    encoding 参数指定返回值的类型

    1
    const files = await webcontainerInstance.fs.readdir('/src', { encoding: 'buffer' })
  3. 删除文件目录 rm

    1
    await webcontainerInstance.fs.rm('/src/main.js');

    删除非空目录时需要指定 recursive 参数以递归删除

    1
    await webcontainerInstance.fs.rm('/src', { recursive: true });
  4. 写入文件 writeFile

    1
    2
    3
    4
    5
    6
    7
    await webcontainerInstance.fs.writeFile('/src/main.js', 'console.log("Hello from WebContainers!")')

    await webcontainerInstance.fs.writeFile(
    '/src/main.js',
    '\xE5\x8D\x83\xE8\x91\x89\xE5\xB8\x82\xE3\x83\x96\xE3\x83\xAB\xE3\x83\xBC\xE3\x82\xB9',
    { encoding: 'latin1' }
    )
  5. 创建目录 mkdir

    1
    await webcontainerInstance.fs.mkdir('src')

    传递 recursive 参数以递归创建目录

    1
    await webcontainerInstance.fs.mkdir('this/is/my/nested/folder', { recursive: true })

文件系统快照

可以使用 @webcontainer/snapshot 包提供的 snapshot 方法来生成一个本地文件系统的快照

1
2
3
import { snapshot } from '@webcontainer/snapshot'

const folderSnapshot = await snapshot(SOURCE_CODE_FOLDER)

从而可以提供给客户端挂载到文件系统上

1
2
3
4
const snapshotResponse = await fetch('/snapshot');
const snapshot = await snapshotResponse.arrayBuffer();

await webcontainer.mount(snapshot)

线程机制

使用 WebContainer 实例的 spawn 方法执行命令

第一个参数表示命令的名称,第二个参数表示命令的参数,第三个参数表示执行命令的额外选项

方法返回的是一个 WebContainerProcess 实例

1
2
3
const installProcess = await webcontainerInstance.spawn('npm', ['install'])

await webcontainerInstance.spawn('ls', ['src', '-l'])

可以通过 WebContainerProcess 实例的 exit 属性,其返回一个兑现 number 值的 Promise,兑现后返回线程执行结果的状态码

线程输入输出

WebContainerProcess 实例的 input 属性返回 WritableStream<string> 值,向线程传入输入数据

WebContainerProcess 实例的 output 属性返回 ReadableStream<string> 值,接收对于线程终端的输出

1
2
3
4
5
installProcess.output.pipeTo(new WritableStream({
write(data) {
console.log(data)
}
}))

中止线程

调用 WebContainerProcess 实例的 kill 方法中止线程的执行

1
installProcess.kill()

状态事件

系统包含多个事件响应

server-ready 事件,当服务完全可接收请求时触发
port 事件,当端口被线程开启或关闭时触发
error 事件,当内部错误抛出时触发

1
2
3
webcontainerInstance.on('server-ready', (port, url) => {
el.src = url
})

通常可以通过 WebContainer 实例的 on 方法监听对应的事件并绑定事件回调函数

Web性能指标

LCP

LCP,即最大内容绘制(Largest Contentful Paint),是视口中可见的最大图片、文本块或视频的呈现时间,其目标是 < 2.5s

其是 FMP 和 SI 的替代

其考虑的元素包括 <img> 元素、<svg> 元素内的 <image> 元素、<video> 元素(封面图)、使用 url() 函数加载背景图片的元素、包含文本节点或其他内嵌级文本元素子级的块级元素

其报告的元素的尺寸通常是用户在视口内可见的尺寸,超出的元素的尺寸会被裁剪

浏览器会在绘制第一帧后立即报告 LCP 指标。但是,在渲染后续帧后,只要最大的内容元素发生变化,新的 LCP 指标就会报告;直到用户开始与网页互动为止

JavaScript API 示例如下(用于计算指标时,应当忽略后台标签页、单独计算往返缓存标签页、考虑 iframe 中的标签页、预渲染网页需从 activationStart 开始计算):

1
2
3
4
5
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});

CLS

CLS,即累积布局偏移(Cumulative Layout Shift),测量页面整个生命周期内发生的每次意外布局偏移中的最大布局偏移分数,其目标是 < 0.1

JavaScript API 示例如下:

1
2
3
4
5
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('Layout shift:', entry);
}
}).observe({type: 'layout-shift', buffered: true});

INP

INP,即 Interaction to Next Paint,测量用户访问网页期间发生的所有交互行为的延迟时间,其目标是 < 200ms

它是 FID 的替代

FID

FID,即首次输入延迟(First Input Delay),测量从用户首次与页面交互到浏览器对交互作出响应所经过的时间,其目标是 < 100ms

通常该指标在 FCP 和 TTI 之间

FCP

FCP,即首次内容绘制(First Contentful Paint),测量页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间,其目标是 < 1.8s

TTI

TTI,即可交互时间(Time to Interactive),测量页面从开始加载到主要子资源完成渲染并能够响应用户交互所需的时间,其目标是尽可能降低 FCP 与 TTI 的差值,其目标是 < 3.8s

TBT

TBT,即总阻塞时间(Total Blocking Time),测量 FCP 与 TTI 之间的总时间,其目标是 < 30ms

SI

SI,即速度指数(Speed Index),测量页面可视区域中内容的填充速度,其目标是 > 75

OpenSources

  

在 Git 中使用 GPG 密钥

生成及查看 GPG 密钥

  1. 下载并安装 GPG 命令行工具(需根据操作系统来选择适合的版本下载)

  2. 运行如下命令生成 GPG 密钥对

    1
    gpg --full-generate-key
  3. 在生成时,会依次提示选择要生成的密钥类型、密钥大小、密钥有效时长及确认选择,以上均选择默认值即可;随后需输入用户 ID 信息,需保证信息与 Github 保存的信息一致;接着需输入一个安全密码;最后完成生成过程

  4. 完成生成后,可以运行如下命令查看已生成的 GPG 密钥

    1
    gpg --list-secret-keys --keyid-format=long
  5. 从已生成的 GPG 密钥中选择一个,并复制密钥 ID;例如,如下示例的密钥 ID 为 3AA5C34371567BD2

    Text
    1
    2
    3
    4
    5
    /Users/hubot/.gnupg/secring.gpg
    ------------------------------------
    sec 4096R/3AA5C34371567BD2 2016-03-10 [expires: 2017-03-10]
    uid Hubot <hubot@example.com>
    ssb 4096R/4BB6D45482678BE3 2016-03-10
  6. 运行如下命令生成 GPG 密钥的公钥文本,需将 <GPG-ID> 替换为对应的 GPG 密钥 ID

    1
    gpg --armor --export <GPG-ID>
  7. 复制生成结果,即以 -----BEGIN PGP PUBLIC KEY BLOCK----- 开头,以 -----END PGP PUBLIC KEY BLOCK----- 结尾的部分

添加 GPG 密钥至 Github

  1. 前往 https://github.com/settings/keys 页面,或通过个人资料照片-设置-SSH 和 GPG 密钥前往对应页面
  2. 点击新建 GPG 密钥,在标题字段输入 GPG 密钥的名称,在密钥字段中输入复制的生成的 GPG 密钥的公钥文本,最后点击添加 GPG 密钥
  3. 完成后续身份验证等步骤以完成操作

添加 GPG 密钥至 Git

  1. 运行如下命令以在 Git 中设置 GPG 签名主键

    1
    git config --global user.signingkey <GPG-ID>
  2. 运行如下命令指定安装的 GPG 命令行工具的路径

    1
    git config --global gpg.program <path/to/gpg>
  3. 可以运行如下命令以配置默认对所有提交使用 GPG 签名

    1
    git config --global commit.gpgsign true

参见

MDN Translations

API 开始时间 结束时间 最近复核时间
Local Font Access API 2023/11/30 2023/12/10 2024/09/17
Barcode Detection API 2023/12/13 2023/12/20 2024/09/17
Web Locks API 2023/12/19 2024/01/09 2024/09/17
Badging API 2024/01/10 2024/01/31 2024/09/17
Notifications API 2024/01/31 2024/02/14 2024/09/16
Battery Status API 2024/02/08 2024/03/31 2024/09/16
Network Information API 2024/03/30 2024/04/04 2024/09/14
User-Agent Client Hints API 2024/04/03 2024/04/15 2024/09/14
Vibrate API 2024/04/17 2024/04/23 2024/09/17
Web Share API 2024/04/18 2024/04/22 2024/09/17
Permissions API 2024/04/20 2024/05/07 2024/09/09
File System API 2024/05/02 2024/05/15 2024/09/06
File API 2024/05/09 2024/06/06 2024/09/06
Broadcast Channel API 2024/06/12 2024/09/18 2024/09/18
Channel Messaging API 2024/09/13 2024/09/17 2024/09/17
Picture-in-Picture API 2024/09/14 2024/09/27 2024/09/27
Document Picture-in-Picture API 2024/09/17 2024/10/10 2024/10/10
Fullscreen API 2024/09/23 2024/09/27 2024/09/27
Pointer Lock API 2024/09/26 2024/10/04 2024/10/04
Keyboard API 2024/09/29 2024/11/02 2024/11/02
VirtualKeyboard API 2024/09/29 2024/11/07 2024/11/07
Navigation API 2024/11/08
  

WebOTP API

WebOTP API 是对 Credential Management API 的扩展,允许开发者请求用户访问短信验证码,以便方便地完成表单等填写

使用

使用 CredentialsContainer.get() 方法并指定 otp 选项请求短信验证凭证

其中 otp 选项传递一个对象,其唯一选项 transport 接收一个字符串数组,其唯一合法项为 sms

方法返回一个 Promise,其兑现一个 OTPCredential 实例

方法在传入的 signal 选项对应的 AbortSignal 已终止时抛出 AbortError 异常;在方法被权限策略拒绝时抛出 SecurityError 异常

OTPCredential 接口表示短信验证凭证

code 只读属性代表一个字符串,表示收到的短信验证码

1
2
3
4
5
6
7
8
9
10
11
12
navigator.credentials
.get({
otp: {
transport: ["sms"],
},
})
.then((otp) => {
console.log(otp.code)
})
.catch((err) => {
console.error(err)
})

步骤

  1. 首先用户填写其手机号码,网页将其发送到应用服务器
  2. 随后调用 navigator.credentials.get() 方法执行相关操作
  3. 接着应用服务器向指定的手机号发送验证码短信
  4. 随后用户代理会弹窗提醒用户是否允许网页访问验证码
  5. 用户授权后可通过 navigator.credentials.get() 方法执行结果获取到验证码并可将其填写在网页上
  6. 最终填写结果会发送给应用服务器进行验证,并进行后续一些操作

短信格式限制

限制短信最后一行必须以 @ 开头,后接调用 API 的网页的域名;随后以空格分隔,以 # 开头,后接短信验证码

1
2
3
Your verification code is 123456.

@www.example.com #123456

若其在内嵌第三方网页中调用;且第一段需为顶层网页的域名;最后一行还需要增加以 # 开头,后接内嵌第三方网页的域名

1
2
3
Your verification code is 123456.

@top-level.example.com #123456 @embedded.com

替代策略

可以利用 autocomplete 属性的 one-time-code 值在某种程度上提示用户代理自动填充短信验证码

1
<input autocomplete="one-time-code" required/>

权限策略

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

默认值是 self,即允许同源的浏览上下文使用该 API

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface OTPCredential extends Credential {
readonly code: string
}

interface CredentialRequestOptions {
otp: OTPCredentialRequestOptions
}

interface OTPCredentialRequestOptions {
transport?: OTPCredentialTransportType[]
}

enum OTPCredentialTransportType {
"sms",
}

链接


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