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]
相关推荐
火星数据-Tina4 分钟前
让电竞数据实时跳动:Spring Boot 后端 + Vue 前端的完美融合实践
前端·vue.js·spring boot
fruge15 分钟前
前端可视化家庭账单:用 ECharts 实现支出统计与趋势分析
前端·javascript·echarts
IT_陈寒34 分钟前
Vue3性能优化实战:5个被低估的Composition API技巧让你的应用快30%
前端·人工智能·后端
嘻嘻哈哈猿人42 分钟前
从 0 到 1 实现一个支持 @ 提及用户的输入框组件(Vue3 实战)
前端·vue.js
东土也43 分钟前
Vue 项目 Nginx 部署路径差异分析与部署指南
前端
云枫晖1 小时前
Vue3 响应式原理:手写实现 ref 函数
前端·vue.js
合作小小程序员小小店1 小时前
web网页开发,在线%宠物销售%系统,基于Idea,html,css,jQuery,java,ssh,mysql。
java·前端·数据库·mysql·jdk·intellij-idea·宠物
荔枝吖1 小时前
html2canvas+pdfjs 打印html
前端·javascript·html
文心快码BaiduComate1 小时前
全运会,用文心快码做个微信小程序帮我找「观赛搭子」
前端·人工智能·微信小程序
合作小小程序员小小店1 小时前
web网页开发,在线%档案管理%系统,基于Idea,html,css,jQuery,java,ssh,mysql。
java·前端·mysql·jdk·html·ssh·intellij-idea