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 小时前
别再傻傻分不清!从axios、ElementPlus深入理解SDK与API的区别
前端
CodeSheep6 小时前
稚晖君官宣,全球首个0代码机器人创作平台来了!
前端·后端·程序员
向上的车轮6 小时前
Actix Web 入门与实战
前端·rust·actix web
Mintopia6 小时前
🧬 生物识别 + AIGC:Web端个性化服务的技术协同路径
前端·javascript·aigc
Mintopia6 小时前
🧠 Next.js 安全防线:从 CSRF 到 XSS 的黑魔法防护 🌐⚔️
前端·javascript·全栈
用户6120414922136 小时前
基于JSP+Servlet+JDBC学生成绩管理系统
java·前端·javascript
错把套路当深情6 小时前
Kotlin Map扩展函数使用指南
服务器·前端·kotlin
i_am_a_div_日积月累_6 小时前
vue打包路径敏感解决;vue路径大小写引入检查与修复
前端·javascript·vue.js
知了一笑7 小时前
项目效率翻倍,做对了什么?
前端·人工智能·后端