Environment 源码解读

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 环境中:

  1. 首先检查相应的全局作用域类型是否已定义(typeof ... !== 'undefined'
  2. 然后检查当前的全局对象 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'

操作系统检测采用了多重条件判断的方式,综合考虑了不同环境下的检测方法:

  1. 首先尝试使用现代浏览器提供的 navigator.userAgentData.platform API
  2. 如果不可用,则回退到传统的 navigator.platformnavigator.userAgent 检测
  3. 在 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 设备检测:

    1. 检查 platform 是否为 'iOS'
    2. 检测 iPad Pro 等设备(在新版 iPadOS 中,platform 为 'MacIntel',但 maxTouchPoints > 1
    3. 使用正则表达式检测设备类型是否为 iPad、iPhone 或 iPod
  • Android 设备检测:

    1. 检查 platform 是否为 'Android'
    2. 检查 navigator.platform 是否为 'Android'
    3. 检查 navigator.userAgent 是否包含 'Android'
    4. 在 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]
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax