Environment 源码解读
库功能概述
environment 是一个轻量级的 JavaScript 运行环境检测库,它提供了一系列布尔值变量,用于判断当前代码运行的环境类型。该库的主要用途是帮助开发者识别代码运行的平台和环境,从而可以根据不同环境编写适配性代码,实现跨平台兼容。
核心功能包括:
- 检测代码是否运行在浏览器、Node.js、Bun、Deno 等 JavaScript 运行时环境
- 识别代码是否运行在 Web Worker、Service Worker 等特殊执行上下文
- 判断操作系统类型(macOS、Windows、Linux 等)
- 检测移动设备平台(iOS、Android)
核心实现解析
运行时环境检测
javascript
export const isBrowser = globalThis.window?.document !== undefined
export const isNode = globalThis.process?.versions?.node !== undefined
export const isBun = globalThis.process?.versions?.bun !== undefined
export const isDeno = globalThis.Deno?.version?.deno !== undefined
export const isElectron = globalThis.process?.versions?.electron !== undefined
export const isJsDom = globalThis.navigator?.userAgent?.includes('jsdom') === true
这部分代码通过检查全局对象上的特定属性来判断当前的运行环境:
isBrowser:检查window.document是否存在,这是浏览器环境的标志isNode:检查process.versions.node是否存在,这是 Node.js 环境的标志isBun:检查process.versions.bun是否存在,这是 Bun 运行时的标志isDeno:检查Deno.version.deno是否存在,这是 Deno 环境的标志isElectron:检查process.versions.electron是否存在,这是 Electron 环境的标志isJsDom:检查navigator.userAgent是否包含 'jsdom',这是 JSDom 环境的标志
这些检测都使用了可选链操作符(?.),确保在属性不存在时不会抛出错误,而是返回 undefined。
Worker 环境检测
javascript
export const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && globalThis instanceof WorkerGlobalScope
export const isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== 'undefined' && globalThis instanceof DedicatedWorkerGlobalScope
export const isSharedWorker = typeof SharedWorkerGlobalScope !== 'undefined' && globalThis instanceof SharedWorkerGlobalScope
export const isServiceWorker = typeof ServiceWorkerGlobalScope !== 'undefined' && globalThis instanceof ServiceWorkerGlobalScope
这部分代码检测代码是否运行在各种 Web Worker 环境中:
- 首先检查相应的全局作用域类型是否已定义(
typeof ... !== 'undefined') - 然后检查当前的全局对象
globalThis是否是该作用域类型的实例
这种两步检查确保了在不支持这些 Worker 类型的环境中不会抛出错误。
操作系统检测
javascript
const platform = globalThis.navigator?.userAgentData?.platform
export const isMacOs = platform === 'macOS'
|| globalThis.navigator?.platform === 'MacIntel' // Even on Apple silicon Macs.
|| globalThis.navigator?.userAgent?.includes(' Mac ') === true
|| globalThis.process?.platform === 'darwin'
export const isWindows = platform === 'Windows'
|| globalThis.navigator?.platform === 'Win32'
|| globalThis.process?.platform === 'win32'
export const isLinux = platform === 'Linux'
|| globalThis.navigator?.platform?.startsWith('Linux') === true
|| globalThis.navigator?.userAgent?.includes(' Linux ') === true
|| globalThis.process?.platform === 'linux'
操作系统检测采用了多重条件判断的方式,综合考虑了不同环境下的检测方法:
- 首先尝试使用现代浏览器提供的
navigator.userAgentData.platformAPI - 如果不可用,则回退到传统的
navigator.platform或navigator.userAgent检测 - 在 Node.js 等环境中,使用
process.platform进行检测
这种多重检测方式提高了检测的准确性和兼容性。
移动设备检测
javascript
export const isIos = platform === 'iOS'
|| (globalThis.navigator?.platform === 'MacIntel' && globalThis.navigator?.maxTouchPoints > 1)
|| /iPad|iPhone|iPod/.test(globalThis.navigator?.platform)
export const isAndroid = platform === 'Android'
|| globalThis.navigator?.platform === 'Android'
|| globalThis.navigator?.userAgent?.includes(' Android ') === true
|| globalThis.process?.platform === 'android'
移动设备检测同样采用了多重条件判断:
-
iOS 设备检测:
- 检查
platform是否为 'iOS' - 检测 iPad Pro 等设备(在新版 iPadOS 中,
platform为 'MacIntel',但maxTouchPoints > 1) - 使用正则表达式检测设备类型是否为 iPad、iPhone 或 iPod
- 检查
-
Android 设备检测:
- 检查
platform是否为 'Android' - 检查
navigator.platform是否为 'Android' - 检查
navigator.userAgent是否包含 'Android' - 在 Node.js 环境中,检查
process.platform是否为 'android'
- 检查
代码示例
基本使用示例
javascript
import { isBrowser, isNode, isMacOs, isIos } from './environment'
// 根据环境执行不同的初始化逻辑
if (isBrowser) {
// 浏览器特定的初始化
document.addEventListener('DOMContentLoaded', init)
} else if (isNode) {
// Node.js 特定的初始化
process.nextTick(init)
}
// 根据操作系统提供不同的快捷键提示
function getShortcut(action) {
const shortcuts = {
copy: isMacOs ? '⌘+C' : 'Ctrl+C',
paste: isMacOs ? '⌘+V' : 'Ctrl+V',
save: isMacOs ? '⌘+S' : 'Ctrl+S'
}
return shortcuts[action] || ''
}
// 移动设备适配
function setupTouchEvents() {
if (isIos || isAndroid) {
// 设置移动设备特定的触摸事件
element.addEventListener('touchstart', handleTouchStart)
} else {
// 设置桌面设备的鼠标事件
element.addEventListener('mousedown', handleMouseDown)
}
}
高级使用示例:创建环境感知的 API
javascript
import { isBrowser, isNode, isWebWorker } from './environment'
// 创建一个环境感知的存储 API
export function createStorage() {
if (isBrowser) {
return {
get: (key) => localStorage.getItem(key),
set: (key, value) => localStorage.setItem(key, value),
remove: (key) => localStorage.removeItem(key)
}
} else if (isNode) {
const fs = require('fs')
const path = require('path')
const storagePath = path.join(process.cwd(), '.storage.json')
// 确保存储文件存在
if (!fs.existsSync(storagePath)) {
fs.writeFileSync(storagePath, '{}')
}
return {
get: (key) => {
const data = JSON.parse(fs.readFileSync(storagePath, 'utf8'))
return data[key]
},
set: (key, value) => {
const data = JSON.parse(fs.readFileSync(storagePath, 'utf8'))
data[key] = value
fs.writeFileSync(storagePath, JSON.stringify(data))
},
remove: (key) => {
const data = JSON.parse(fs.readFileSync(storagePath, 'utf8'))
delete data[key]
fs.writeFileSync(storagePath, JSON.stringify(data))
}
}
} else if (isWebWorker) {
// Web Worker 环境使用内存存储
const storage = new Map()
return {
get: (key) => storage.get(key),
set: (key, value) => storage.set(key, value),
remove: (key) => storage.delete(key)
}
} else {
// 默认使用内存存储
const storage = new Map()
return {
get: (key) => storage.get(key),
set: (key, value) => storage.set(key, value),
remove: (key) => storage.delete(key)
}
}
}
// 使用环境感知的存储 API
const storage = createStorage()
storage.set('username', 'john_doe')
console.log(storage.get('username')) // 'john_doe'
环境检测流程图
graph TD
A[开始环境检测] --> B{检查运行时环境}
B -->|window.document 存在| C[isBrowser = true]
B -->|process.versions.node 存在| D[isNode = true]
B -->|process.versions.bun 存在| E[isBun = true]
B -->|Deno.version.deno 存在| F[isDeno = true]
A --> G{检查 Worker 环境}
G -->|globalThis instanceof WorkerGlobalScope| H[isWebWorker = true]
G -->|globalThis instanceof DedicatedWorkerGlobalScope| I[isDedicatedWorker = true]
G -->|globalThis instanceof SharedWorkerGlobalScope| J[isSharedWorker = true]
G -->|globalThis instanceof ServiceWorkerGlobalScope| K[isServiceWorker = true]
A --> L{检查操作系统}
L -->|检测 macOS 特征| M[isMacOs = true]
L -->|检测 Windows 特征| N[isWindows = true]
L -->|检测 Linux 特征| O[isLinux = true]
A --> P{检查移动设备}
P -->|检测 iOS 特征| Q[isIos = true]
P -->|检测 Android 特征| R[isAndroid = true]