JSBox的学习路线图和知识点精要

JSBox的学习路线图和知识点精要

JSBox是一个iOS上的JavaScript集成开发环境,可以用JavaScript开发iOS原生应用和小工具。我需要创建一个全面的学习指南,包括:

  1. JSBox简介
  1. 基础知识
  1. UI组件
  1. API详解
  1. 高级功能
  1. 实战项目
  1. 最佳实践
  1. 常见问题

我需要确保内容详实、结构清晰,达到10000字的要求。

JSBox 完整学习路线图及知识点精要

目录

  1. JSBox 概述与发展历程

  2. 环境搭建与基础配置

  3. JavaScript 基础知识回顾

  4. JSBox 核心概念与架构

  5. UI 组件系统详解

  6. API 接口全面解析

  7. 数据存储与管理

  8. 网络请求与通信

  9. 系统集成与扩展

  10. 高级功能与技巧

  11. 实战项目开发

  12. 性能优化与最佳实践

  13. 发布与分享

  14. 常见问题与解决方案

  15. 未来发展与社区资源

第一章:JSBox 概述与发展历程

1.1 什么是 JSBox

JSBox 是一个革命性的 iOS 平台集成开发环境,它允许开发者使用 JavaScript 语言开发原生 iOS 应用和实用工具。作为一个强大的生产力工具,JSBox 打破了传统 iOS 开发需要 Mac 电脑和 Xcode 的限制,让开发者可以直接在 iPhone 或 iPad 上进行编程。

JSBox 的核心理念是"简单、高效、强大"。它提供了丰富的原生 API 接口,使得 JavaScript 开发者可以轻松调用 iOS 系统功能,创建具有原生体验的应用。无论是简单的脚本工具,还是复杂的应用程序,JSBox 都能胜任。

1.2 JSBox 的特点与优势

JSBox 具有以下显著特点:

移动开发新体验:JSBox 让你可以随时随地在 iOS 设备上编写代码,无需电脑即可完成从开发到调试的全过程。内置的代码编辑器支持语法高亮、自动补全、代码折叠等专业功能,提供了接近桌面 IDE 的开发体验。

强大的 API 支持:JSBox 封装了大量 iOS 原生 API,包括但不限于文件系统、网络请求、UI 组件、系统服务、硬件接口等。这些 API 经过精心设计,既保持了 JavaScript 的简洁性,又充分发挥了 iOS 平台的能力。

丰富的 UI 组件:JSBox 提供了完整的 UI 框架,支持创建各种原生界面。从基础的按钮、文本框,到复杂的列表、网格、图表,都可以通过简单的 JavaScript 代码实现。所有 UI 组件都支持自定义样式和交互。

即时运行与调试:代码编写完成后,可以立即运行查看效果。JSBox 提供了完善的调试工具,包括控制台输出、断点调试、性能分析等,帮助开发者快速定位和解决问题。

社区与生态系统:JSBox 拥有活跃的开发者社区,用户可以分享自己的作品,学习他人的代码。官方商店提供了大量优质脚本和应用,涵盖了生产力工具、娱乐应用、系统增强等各个领域。

1.3 JSBox 的应用场景

JSBox 的应用场景非常广泛,以下是一些典型的使用案例:

自动化工具:创建各种自动化脚本,如批量处理图片、自动整理文件、定时任务等。JSBox 可以访问系统相册、文件系统,实现复杂的自动化流程。

生产力应用:开发个人定制的生产力工具,如待办事项管理、笔记应用、时间追踪器等。这些应用可以充分利用 iOS 的通知、小组件等系统特性。

数据处理:编写数据分析和处理脚本,支持 JSON、CSV、XML 等多种数据格式。可以创建数据可视化工具,生成各种图表和报告。

网络工具:开发网络相关的工具,如 API 测试客户端、网页爬虫、下载管理器等。JSBox 提供了强大的网络请求功能,支持各种 HTTP 方法和自定义请求头。

系统增强:创建系统功能的增强工具,如剪贴板管理器、快捷启动器、系统信息查看器等。通过 JSBox 的系统 API,可以实现很多原本需要越狱才能实现的功能。

第二章:环境搭建与基础配置

2.1 安装 JSBox

JSBox 的安装过程非常简单,但有一些注意事项需要了解:

App Store 下载:JSBox 可以直接从 App Store 下载安装。搜索"JSBox"即可找到官方应用。需要注意的是,JSBox 是付费应用,但考虑到其强大的功能,这个投资是非常值得的。

系统要求:JSBox 需要 iOS 10.0 或更高版本。建议使用较新的 iOS 版本,以获得最佳的性能和兼容性。某些高级功能可能需要特定的 iOS 版本支持。

存储空间:虽然 JSBox 本身占用空间不大,但随着脚本和数据的增加,可能需要较多存储空间。建议预留至少 1GB 的可用空间。

2.2 界面介绍与基本操作

JSBox 的界面设计简洁而功能强大,主要包括以下几个部分:

主界面:打开 JSBox 后,首先看到的是脚本列表界面。这里显示了所有已安装的脚本和应用。可以通过滑动操作进行删除、编辑等操作。底部工具栏提供了快速访问常用功能的入口。

代码编辑器:JSBox 的代码编辑器是其核心组件之一。编辑器支持语法高亮、代码自动补全、括号匹配、代码折叠等专业功能。顶部工具栏提供了运行、保存、查找替换等常用操作。键盘上方的快捷键栏可以快速输入常用符号和代码片段。

运行环境:点击运行按钮后,脚本会在独立的运行环境中执行。运行界面会显示脚本创建的 UI 界面或输出结果。底部有控制台按钮,可以查看日志输出和错误信息。

文件管理器:JSBox 内置了强大的文件管理器,可以管理脚本文件、资源文件、数据文件等。支持创建文件夹、移动复制文件、查看文件属性等操作。文件管理器还支持与其他应用共享文件。

设置界面:在设置中可以配置编辑器主题、字体大小、代码风格等个性化选项。还可以管理扩展插件、查看文档、访问社区等。

2.3 创建第一个脚本

让我们创建一个简单的"Hello World"脚本,熟悉 JSBox 的基本开发流程:

  
// 创建一个简单的界面
  
$ui.render({
  
  views: [{
  
    type: "label",
  
    props: {
  
      text: "Hello, JSBox!",
  
      align: $align.center,
  
      font: $font(32)
  
    },
  
    layout: function(make, view) {
  
      make.center.equalTo(view.super)
  
      make.width.equalTo(view.super)
  
    }
  
  }]
  
})
  

这个简单的例子展示了 JSBox 的几个核心概念:

  1. $ui.render() 用于渲染用户界面

  2. views 数组定义了界面中的组件

  3. props 对象设置组件的属性

  4. layout 函数定义组件的布局

运行这个脚本,你会看到屏幕中央显示"Hello, JSBox!"的文字。

2.4 项目结构与文件组织

一个完整的 JSBox 项目通常包含以下文件和目录:

主脚本文件:通常命名为 main.jsindex.js,是项目的入口文件。这个文件负责初始化应用、设置界面、处理用户交互等核心逻辑。

模块文件:将功能模块化是良好的编程习惯。可以创建多个 .js 文件,每个文件负责特定的功能模块。通过 require() 函数可以在主脚本中引用这些模块。

资源文件夹:通常命名为 assetsresources,用于存放图片、音频、数据文件等资源。JSBox 支持多种资源格式,可以通过相对路径引用这些资源。

配置文件config.json 文件用于存储应用配置信息,如版本号、作者信息、应用描述等。这些信息会显示在脚本列表中。

文档文件:建议创建 README.md 文件,记录项目的使用说明、功能介绍、更新日志等信息。良好的文档有助于他人理解和使用你的脚本。

第三章:JavaScript 基础知识回顾

3.1 JavaScript 语法基础

虽然 JSBox 的目标用户可能已经具备 JavaScript 基础,但有必要回顾一些在 JSBox 开发中特别重要的语法知识:

变量声明与作用域:在 JSBox 中,建议使用 letconst 声明变量,避免使用 var。这样可以避免作用域相关的问题,使代码更加清晰和可维护。

  
// 推荐的变量声明方式
  
const API_URL = "https://api.example.com"  // 常量
  
let userName = "JSBox User"  // 可变变量
  
let userAge = 25
  

  
// 块级作用域
  
if (userAge > 18) {
  
  let message = "成年用户"
  
  console.log(message)  // 正常输出
  
}
  
// console.log(message)  // 错误:message 在此处不可访问
  

函数定义与箭头函数:JSBox 支持多种函数定义方式,箭头函数在处理回调时特别有用:

  
// 传统函数定义
  
function calculateSum(a, b) {
  
  return a + b
  
}
  

  
// 函数表达式
  
const calculateProduct = function(a, b) {
  
  return a * b
  
}
  

  
// 箭头函数
  
const calculateDifference = (a, b) => a - b
  

  
// 在 JSBox 的事件处理中常用箭头函数
  
$ui.render({
  
  views: [{
  
    type: "button",
  
    props: { title: "点击我" },
  
    events: {
  
      tapped: () => {
  
        $ui.alert("按钮被点击了!")
  
      }
  
    }
  
  }]
  
})
  

对象和数组操作:JSBox 开发中会频繁使用对象和数组,掌握现代 JavaScript 的操作方法很重要:

  
// 对象解构
  
const user = { name: "张三", age: 25, city: "北京" }
  
const { name, age } = user
  

  
// 数组解构
  
const numbers = [1, 2, 3, 4, 5]
  
const [first, second, ...rest] = numbers
  

  
// 扩展运算符
  
const newUser = { ...user, email: "zhangsan@example.com" }
  
const moreNumbers = [...numbers, 6, 7, 8]
  

  
// 数组方法
  
const doubled = numbers.map(n => n * 2)
  
const evens = numbers.filter(n => n % 2 === 0)
  
const sum = numbers.reduce((acc, n) => acc + n, 0)
  

3.2 异步编程

JSBox 中的很多操作都是异步的,如网络请求、文件读写、动画等。理解和正确使用异步编程模式非常重要:

回调函数:这是最基础的异步模式,JSBox 的很多 API 使用回调函数:

  
// 使用回调函数读取文件
  
$file.read("data.txt", content => {
  
  if (content) {
  
    console.log("文件内容:", content)
  
  } else {
  
    console.log("读取文件失败")
  
  }
  
})
  

Promise:Promise 提供了更优雅的异步处理方式:

  
// 将回调包装成 Promise
  
function readFilePromise(path) {
  
  return new Promise((resolve, reject) => {
  
    $file.read(path, content => {
  
      if (content) {
  
        resolve(content)
  
      } else {
  
        reject(new Error("读取文件失败"))
  
      }
  
    })
  
  })
  
}
  

  
// 使用 Promise
  
readFilePromise("data.txt")
  
  .then(content => console.log("文件内容:", content))
  
  .catch(error => console.error("错误:", error))
  

async/await:这是最现代的异步编程方式,使异步代码看起来像同步代码:

  
// 使用 async/await
  
async function processFile() {
  
  try {
  
    const content = await readFilePromise("data.txt")
  
    console.log("文件内容:", content)
  
    
  
    // 可以连续进行多个异步操作
  
    const processedContent = await processContent(content)
  
    await $file.write("output.txt", processedContent)
  
    
  
    console.log("处理完成")
  
  } catch (error) {
  
    console.error("处理失败:", error)
  
  }
  
}
  

3.3 错误处理

良好的错误处理是稳定应用的基础。在 JSBox 中,需要特别注意以下几点:

try-catch 语句:用于捕获同步代码中的错误:

  
try {
  
  const data = JSON.parse(jsonString)
  
  processData(data)
  
} catch (error) {
  
  console.error("JSON 解析失败:", error)
  
  $ui.alert({
  
    title: "错误",
  
    message: "数据格式不正确"
  
  })
  
}
  

Promise 错误处理:异步操作的错误需要通过 .catch() 或在 async/await 中使用 try-catch:

  
// Promise 错误处理
  
fetchData()
  
  .then(data => processData(data))
  
  .catch(error => {
  
    console.error("获取数据失败:", error)
  
    showErrorMessage(error)
  
  })
  

  
// async/await 错误处理
  
async function loadData() {
  
  try {
  
    const data = await fetchData()
  
    await processData(data)
  
  } catch (error) {
  
    console.error("操作失败:", error)
  
    showErrorMessage(error)
  
  }
  
}
  

全局错误处理:JSBox 提供了全局错误处理机制:

  
$app.errorHandler = function(error) {
  
  console.error("全局错误:", error)
  
  // 可以在这里上报错误到服务器
  
  // 或者显示用户友好的错误提示
  
}
  

第四章:JSBox 核心概念与架构

4.1 JSBox 运行时环境

理解 JSBox 的运行时环境对于开发高质量的应用至关重要。JSBox 基于 JavaScriptCore 引擎,这是 Safari 浏览器使用的同一个 JavaScript 引擎,提供了优秀的性能和兼容性。

JavaScript 引擎特性:JSBox 使用的 JavaScriptCore 支持 ES6+ 的大部分特性,包括但不限于:类、模块、解构赋值、模板字符串、Symbol、Map/Set、Promise、async/await 等。这使得开发者可以使用现代 JavaScript 语法编写代码。

内存管理:JSBox 的内存管理是自动的,使用垃圾回收机制。但开发者仍需注意避免内存泄漏,特别是在处理大量数据或创建复杂 UI 时。要注意及时释放不再使用的资源,避免循环引用。

线程模型:JSBox 的 JavaScript 代码运行在主线程上,这意味着长时间运行的同步操作会阻塞 UI。对于耗时操作,应该使用异步方法或考虑使用 Web Worker(如果支持)。

4.2 模块系统

JSBox 支持 CommonJS 风格的模块系统,这允许将代码组织成可重用的模块:

创建模块

  
// math.js - 数学工具模块
  
function add(a, b) {
  
  return a + b
  
}
  

  
function multiply(a, b) {
  
  return a * b
  
}
  

  
module.exports = {
  
  add: add,
  
  multiply: multiply,
  
  PI: 3.14159
  
}
  

使用模块

  
// main.js - 主程序
  
const math = require('./math')
  

  
console.log(math.add(5, 3))  // 输出: 8
  
console.log(math.multiply(4, math.PI))  // 输出: 12.56636
  

模块路径解析:JSBox 的模块路径解析遵循以下规则:

  1. 如果路径以 ./../ 开头,视为相对路径

  2. 如果路径不以 . 开头,JSBox 会在内置模块和 node_modules 目录中查找

  3. 可以省略 .js 扩展名

4.3 全局对象与 API 命名空间

JSBox 提供了一系列全局对象,通过 $ 前缀访问。这些对象组织了 JSBox 的所有 API:

核心全局对象

  • $app:应用程序相关功能

  • $ui:用户界面相关功能

  • $http:网络请求功能

  • $file:文件系统操作

  • $cache:缓存管理

  • $device:设备信息和功能

  • $system:系统功能调用

每个全局对象都包含了相关的方法和属性,例如:

  
// 应用信息
  
console.log($app.info.version)  // 应用版本
  
console.log($app.info.bundleID)  // Bundle ID
  

  
// 设备信息
  
console.log($device.info.model)  // 设备型号
  
console.log($device.info.language)  // 系统语言
  

  
// 系统功能
  
$system.call("tel:10086")  // 拨打电话
  
$system.mailto("example@email.com")  // 发送邮件
  

4.4 事件系统

JSBox 采用事件驱动的编程模型。理解事件系统对于创建响应式的应用至关重要:

UI 事件

  
$ui.render({
  
  views: [{
  
    type: "button",
  
    props: {
  
      title: "点击计数"
  
    },
  
    layout: $layout.center,
  
    events: {
  
      tapped: function(sender) {
  
        sender.title = `点击了 ${++clickCount} 次`
  
      },
  
      longPressed: function(sender) {
  
        $ui.alert("长按检测到!")
  
      }
  
    }
  
  }]
  
})
  

自定义事件

  
// 创建事件发射器
  
const EventEmitter = require('events')
  
const emitter = new EventEmitter()
  

  
// 监听事件
  
emitter.on('data-loaded', (data) => {
  
  console.log('数据加载完成:', data)
  
})
  

  
// 触发事件
  
emitter.emit('data-loaded', { items: [1, 2, 3] })
  

生命周期事件

  
$app.autoKeyboardEnabled = true  // 自动键盘管理
  

  
// 应用启动
  
$app.listen({
  
  ready: function() {
  
    console.log("应用已就绪")
  
  },
  
  exit: function() {
  
    console.log("应用即将退出")
  
    // 保存数据等清理工作
  
  }
  
})
  

第五章:UI 组件系统详解

5.1 基础 UI 组件

JSBox 提供了丰富的 UI 组件,覆盖了 iOS 开发中的常用控件。每个组件都有其特定的属性和事件:

Label(标签):用于显示文本信息

  
{
  
  type: "label",
  
  props: {
  
    text: "这是一个标签",
  
    font: $font(18),  // 字体大小
  
    textColor: $color("#333333"),  // 文字颜色
  
    align: $align.center,  // 对齐方式
  
    lines: 0,  // 行数限制,0 表示不限制
  
    autoFontSize: true  // 自动调整字体大小
  
  },
  
  layout: function(make, view) {
  
    make.center.equalTo(view.super)
  
    make.width.equalTo(200)
  
  }
  
}
  

Button(按钮):用于触发用户操作

  
{
  
  type: "button",
  
  props: {
  
    title: "提交",
  
    bgcolor: $color("#007AFF"),
  
    titleColor: $color("white"),
  
    font: $font("bold", 16),
  
    cornerRadius: 8,
  
    borderWidth: 1,
  
    borderColor: $color("#0051D5")
  
  },
  
  layout: function(make, view) {
  
    make.center.equalTo(view.super)
  
    make.size.equalTo($size(120, 44))
  
  },
  
  events: {
  
    tapped: function(sender) {
  
      handleSubmit()
  
    }
  
  }
  
}
  

Input(输入框):用于接收用户输入

  
{
  
  type: "input",
  
  props: {
  
    placeholder: "请输入用户名",
  
    font: $font(16),
  
    textColor: $color("#333"),
  
    borderWidth: 1,
  
    borderColor: $color("#E0E0E0"),
  
    cornerRadius: 4,
  
    secure: false,  // 是否为密码输入
  
    type: $kbType.default,  // 键盘类型
  
    autoFontSize: false
  
  },
  
  layout: function(make, view) {
  
    make.left.right.inset(20)
  
    make.top.equalTo(100)
  
    make.height.equalTo(44)
  
  },
  
  events: {
  
    changed: function(sender) {
  
      console.log("输入内容:", sender.text)
  
    },
  
    returned: function(sender) {
  
      sender.blur()  // 收起键盘
  
    }
  
  }
  
}
  

Image(图片):用于显示图片

  
{
  
  type: "image",
  
  props: {
  
    src: "https://example.com/image.jpg",  // 网络图片
  
    // src: "assets/local_image.png",  // 本地图片
  
    contentMode: $contentMode.scaleAspectFit,
  
    cornerRadius: 10,
  
    borderWidth: 2,
  
    borderColor: $color("#E0E0E0")
  
  },
  
  layout: function(make, view) {
  
    make.center.equalTo(view.super)
  
    make.size.equalTo($size(200, 200))
  
  },
  
  events: {
  
    tapped: function(sender) {
  
      previewImage(sender.src)
  
    }
  
  }
  
}
  

Switch(开关):用于切换状态

  
{
  
  type: "switch",
  
  props: {
  
    on: true,  // 默认状态
  
    onColor: $color("#4CD964"),  // 开启时的颜色
  
    thumbColor: $color("white")  // 滑块颜色
  
  },
  
  layout: function(make, view) {
  
    make.right.equalTo(-20)
  
    make.centerY.equalTo(view.super)
  
  },
  
  events: {
  
    changed: function(sender) {
  
      updateSetting("notifications", sender.on)
  
    }
  
  }
  
}
  

5.2 容器组件

容器组件用于组织和布局其他组件:

View(视图容器):最基础的容器组件

  
{
  
  type: "view",
  
  props: {
  
    bgcolor: $color("#F5F5F5"),
  
    cornerRadius: 10,
  
    clipsToBounds: true,  // 裁剪超出边界的内容
  
    shadow: {
  
      color: $color("#000000", 0.1),
  
      offset: $size(0, 2),
  
      radius: 4
  
    }
  
  },
  
  layout: function(make, view) {
  
    make.edges.inset(10)
  
  },
  
  views: [
  
    // 子视图
  
  ]
  
}
  

Scroll(滚动视图):当内容超出屏幕时使用

  
{
  
  type: "scroll",
  
  props: {
  
    contentSize: $size(0, 1000),  // 内容大小
  
    showsVerticalIndicator: true,
  
    showsHorizontalIndicator: false,
  
    pagingEnabled: false,  // 分页滚动
  
    bounces: true  // 回弹效果
  
  },
  
  layout: $layout.fill,
  
  views: [
  
    // 滚动内容
  
  ],
  
  events: {
  
    didScroll: function(sender) {
  
      console.log("滚动位置:", sender.contentOffset)
  
    }
  
  }
  
}
  

Stack(堆栈视图):自动布局子视图

  
{
  
  type: "stack",
  
  props: {
  
    axis: $stackViewAxis.vertical,  // 垂直排列
  
    distribution: $stackViewDistribution.fillEqually,
  
    alignment: $stackViewAlignment.fill,
  
    spacing: 10,  // 间距
  
    bgcolor: $color("white")
  
  },
  
  layout: function(make, view) {
  
    make.edges.inset(20)
  
  },
  
  views: [
  
    // 自动排列的子视图
  
  ]
  
}
  

5.3 列表组件

列表是移动应用中最常用的组件之一:

List(列表)

  
{
  
  type: "list",
  
  props: {
  
    data: [
  
      {
  
        title: "第一项",
  
        subtitle: "这是第一项的描述"
  
      },
  
      {
  
        title: "第二项", 
  
        subtitle: "这是第二项的描述"
  
      }
  
    ],
  
    rowHeight: 60,
  
    separatorInset: $insets(0, 15, 0, 0),
  
    template: {
  
      views: [
  
        {
  
          type: "label",
  
          props: {
  
            id: "title",
  
            font: $font("bold", 16)
  
          },
  
          layout: function(make, view) {
  
            make.left.equalTo(15)
  
            make.top.equalTo(10)
  
          }
  
        },
  
        {
  
          type: "label",
  
          props: {
  
            id: "subtitle",
  
            font: $font(14),
  
            textColor: $color("#666")
  
          },
  
          layout: function(make, view) {
  
            make.left.equalTo(15)
  
            make.bottom.equalTo(-10)
  
          }
  
        }
  
      ]
  
    }
  
  },
  
  layout: $layout.fill,
  
  events: {
  
    didSelect: function(sender, indexPath, data) {
  
      console.log(`选中了第 ${indexPath.row} 项`)
  
      showDetail(data)
  
    },
  
    didLongPress: function(sender, indexPath, data) {
  
      showContextMenu(data)
  
    }
  
  }
  
}
  

Matrix(矩阵/网格):用于显示网格布局

  
{
  
  type: "matrix",
  
  props: {
  
    columns: 3,  // 列数
  
    itemHeight: 100,  // 项目高度
  
    spacing: 10,  // 间距
  
    data: generateGridData(),
  
    template: {
  
      views: [
  
        {
  
          type: "image",
  
          props: {
  
            id: "image",
  
            contentMode: $contentMode.scaleAspectFill
  
          },
  
          layout: $layout.fill
  
        },
  
        {
  
          type: "label",
  
          props: {
  
            id: "label",
  
            align: $align.center,
  
            font: $font(12)
  
          },
  
          layout: function(make, view) {
  
            make.left.right.bottom.inset(0)
  
            make.height.equalTo(30)
  
          }
  
        }
  
      ]
  
    }
  
  },
  
  layout: $layout.fill,
  
  events: {
  
    didSelect: function(sender, indexPath, data) {
  
      handleGridItemClick(data)
  
    }
  
  }
  
}
  

5.4 高级 UI 组件

JSBox 还提供了一些高级组件,用于实现复杂的交互:

Web(网页视图)

  
{
  
  type: "web",
  
  props: {
  
    url: "https://www.example.com",
  
    // 或使用 HTML 内容
  
    html: "<html><body><h1>Hello</h1></body></html>",
  
    showsProgress: true,  // 显示加载进度
  
    ua: "Mozilla/5.0 ...",  // 自定义 User Agent
  
    script: `
  
      // 注入的 JavaScript 代码
  
      document.body.style.backgroundColor = '#f0f0f0';
  
    `
  
  },
  
  layout: $layout.fill,
  
  events: {
  
    didStart: function(sender, navigation) {
  
      console.log("开始加载:", navigation.url)
  
    },
  
    didFinish: function(sender, navigation) {
  
      console.log("加载完成")
  
    },
  
    didFail: function(sender, navigation, error) {
  
      console.error("加载失败:", error)
  
    }
  
  }
  
}
  

Chart(图表)

  
{
  
  type: "chart",
  
  props: {
  
    type: "line",  // 图表类型:line, bar, pie 等
  
    data: {
  
      labels: ["一月", "二月", "三月", "四月"],
  
      datasets: [{
  
        label: "销售额",
  
        data: [65, 59, 80, 81],
  
        borderColor: $color("#007AFF"),
  
        backgroundColor: $color("#007AFF", 0.1)
  
      }]
  
    },
  
    options: {
  
      responsive: true,
  
      maintainAspectRatio: false,
  
      scales: {
  
        y: {
  
          beginAtZero: true
  
        }
  
      }
  
    }
  
  },
  
  layout: function(make, view) {
  
    make.edges.inset(20)
  
    make.height.equalTo(300)
  
  }
  
}
  

第六章:API 接口全面解析

6.1 文件系统 API

JSBox 提供了完整的文件系统访问能力,可以读写文件、管理目录等:

基本文件操作

  
// 读取文本文件
  
const content = $file.read("data.txt")
  
if (content) {
  
  console.log("文件内容:", content.string)
  
}
  

  
// 写入文本文件
  
const success = $file.write({
  
  data: $data({string: "Hello, JSBox!"}),
  
  path: "output.txt"
  
})
  
console.log("写入成功:", success)
  

  
// 检查文件是否存在
  
const exists = $file.exists("data.txt")
  
console.log("文件存在:", exists)
  

  
// 删除文件
  
const deleted = $file.delete("temp.txt")
  
console.log("删除成功:", deleted)
  

  
// 复制文件
  
$file.copy({
  
  src: "source.txt",
  
  dst: "destination.txt"
  
})
  

  
// 移动文件
  
$file.move({
  
  src: "old_path.txt",
  
  dst: "new_path.txt"
  
})
  

目录操作

  
// 创建目录
  
const created = $file.mkdir("documents/images")
  
console.log("目录创建成功:", created)
  

  
// 列出目录内容
  
const files = $file.list("documents")
  
files.forEach(file => {
  
  console.log("文件:", file)
  
})
  

  
// 获取文件信息
  
const info = $file.info("document.pdf")
  
if (info) {
  
  console.log("文件大小:", info.size)
  
  console.log("修改时间:", info.modificationDate)
  
  console.log("是否为目录:", info.isDirectory)
  
}
  

  
// 递归遍历目录
  
function traverseDirectory(path) {
  
  const items = $file.list(path)
  
  items.forEach(item => {
  
    const fullPath = path + "/" + item
  
    const info = $file.info(fullPath)
  
    if (info.isDirectory) {
  
      traverseDirectory(fullPath)
  
    } else {
  
      console.log("文件:", fullPath)
  
    }
  
  })
  
}
  

文件共享

  
// 分享文件
  
$share.sheet({
  
  items: [$file.read("document.pdf")],
  
  handler: function(success) {
  
    console.log("分享成功:", success)
  
  }
  
})
  

  
// 从其他应用导入文件
  
$drive.open({
  
  handler: function(data) {
  
    if (data) {
  
      $file.write({
  
        data: data,
  
        path: "imported_file"
  
      })
  
    }
  
  }
  
})
  

6.2 网络请求 API

网络功能是现代应用的核心,JSBox 提供了强大的网络 API:

基本 HTTP 请求

  
// GET 请求
  
$http.get({
  
  url: "https://api.example.com/users",
  
  header: {
  
    "Authorization": "Bearer token123"
  
  },
  
  handler: function(resp) {
  
    if (resp.error) {
  
      console.error("请求失败:", resp.error)
  
    } else {
  
      const data = resp.data
  
      console.log("响应数据:", data)
  
    }
  
  }
  
})
  

  
// POST 请求
  
$http.post({
  
  url: "https://api.example.com/users",
  
  header: {
  
    "Content-Type": "application/json"
  
  },
  
  body: {
  
    name: "张三",
  
    email: "zhangsan@example.com"
  
  },
  
  handler: function(resp) {
  
    if (resp.response.statusCode === 201) {
  
      console.log("创建成功")
  
    }
  
  }
  
})
  

  
// 带进度的下载
  
$http.download({
  
  url: "https://example.com/large_file.zip",
  
  progress: function(percentage) {
  
    console.log(`下载进度:${(percentage * 100).toFixed(2)}%`)
  
    updateProgressBar(percentage)
  
  },
  
  handler: function(resp) {
  
    if (resp.data) {
  
      $file.write({
  
        data: resp.data,
  
        path: "downloads/large_file.zip"
  
      })
  
    }
  
  }
  
})
  

高级网络功能

  
// 并发请求
  
async function fetchMultipleData() {
  
  const urls = [
  
    "https://api.example.com/data1",
  
    "https://api.example.com/data2",
  
    "https://api.example.com/data3"
  
  ]
  
  
  
  const promises = urls.map(url => 
  
    new Promise((resolve, reject) => {
  
      $http.get({
  
        url: url,
  
        handler: resp => {
  
          if (resp.error) {
  
            reject(resp.error)
  
          } else {
  
            resolve(resp.data)
  
          }
  
        }
  
      })
  
    })
  
  )
  
  
  
  try {
  
    const results = await Promise.all(promises)
  
    console.log("所有数据:", results)
  
  } catch (error) {
  
    console.error("请求失败:", error)
  
  }
  
}
  

  
// WebSocket 连接
  
const ws = $websocket.new({
  
  url: "wss://echo.websocket.org",
  
  handler: function(message) {
  
    console.log("收到消息:", message)
  
  },
  
  eventHandler: {
  
    open: function() {
  
      console.log("WebSocket 已连接")
  
      ws.send("Hello Server!")
  
    },
  
    error: function(error) {
  
      console.error("WebSocket 错误:", error)
  
    },
  
    close: function(code, reason) {
  
      console.log("WebSocket 已关闭:", code, reason)
  
    }
  
  }
  
})
  

6.3 系统功能 API

JSBox 可以调用多种 iOS 系统功能:

剪贴板操作

  
// 复制文本到剪贴板
  
$clipboard.text = "这是复制的文本"
  

  
// 读取剪贴板文本
  
const clipboardText = $clipboard.text
  
console.log("剪贴板内容:", clipboardText)
  

  
// 复制图片到剪贴板
  
$clipboard.image = image
  

  
// 清空剪贴板
  
$clipboard.clear()
  

系统分享

  
// 分享文本
  
$share.sheet({
  
  items: ["分享这段文字"],
  
  handler: function(success) {
  
    if (success) {
  
      console.log("分享成功")
  
    }
  
  }
  
})
  

  
// 分享图片和文字
  
$share.sheet({
  
  items: [
  
    "看看这张图片",
  
    $("imageView").snapshot  // 获取视图截图
  
  ]
  
})
  

  
// 分享文件
  
$share.sheet({
  
  items: [$file.read("document.pdf")]
  
})
  

通知功能

  
// 发送本地通知
  
$push.schedule({
  
  title: "提醒",
  
  body: "该休息一下了",
  
  delay: 5,  // 5秒后触发
  
  handler: function(result) {
  
    console.log("通知已安排")
  
  }
  
})
  

  
// 重复通知
  
$push.schedule({
  
  title: "每日提醒",
  
  body: "记得喝水",
  
  date: new Date(2023, 0, 1, 9, 0),  // 指定时间
  
  repeat: "day",  // 每天重复
  
  handler: function(result) {
  
    console.log("重复通知已设置")
  
  }
  
})
  

  
// 取消通知
  
$push.cancel({
  
  title: "提醒"
  
})
  

设备功能

  
// 震动反馈
  
$device.taptic(0)  // 0: light, 1: medium, 2: heavy
  

  
// 获取电池信息
  
console.log("电池电量:", $device.info.battery.level)
  
console.log("充电状态:", $device.info.battery.state)
  

  
// 屏幕亮度
  
$device.brightness = 0.8  // 设置亮度 (0-1)
  
console.log("当前亮度:", $device.brightness)
  

  
// 手电筒
  
$device.torch = true  // 打开手电筒
  

  
// 获取网络状态
  
console.log("网络类型:", $device.networkType)
  
// 0: 无网络, 1: Wi-Fi, 2: 蜂窝网络
  

6.4 数据存储 API

JSBox 提供了多种数据存储方案:

缓存系统

  
// 设置缓存
  
$cache.set("user_name", "张三")
  
$cache.set("user_data", {
  
  id: 123,
  
  email: "zhangsan@example.com"
  
})
  

  
// 读取缓存
  
const userName = $cache.get("user_name")
  
const userData = $cache.get("user_data")
  

  
// 删除缓存
  
$cache.remove("user_name")
  

  
// 清空所有缓存
  
$cache.clear()
  

  
// 设置带过期时间的缓存
  
$cache.setAsync({
  
  key: "temp_token",
  
  value: "abc123",
  
  ttl: 3600  // 1小时后过期
  
})
  

数据库操作

  
// 创建/打开数据库
  
const db = $sqlite.open("app.db")
  

  
// 创建表
  
db.update({
  
  sql: `CREATE TABLE IF NOT EXISTS users (
  
    id INTEGER PRIMARY KEY AUTOINCREMENT,
  
    name TEXT NOT NULL,
  
    email TEXT UNIQUE,
  
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  
  )`
  
})
  

  
// 插入数据
  
db.update({
  
  sql: "INSERT INTO users (name, email) VALUES (?, ?)",
  
  args: ["李四", "lisi@example.com"]
  
})
  

  
// 查询数据
  
const result = db.query({
  
  sql: "SELECT * FROM users WHERE name LIKE ?",
  
  args: ["%李%"]
  
})
  

  
result.forEach(row => {
  
  console.log(`ID: ${row.id}, Name: ${row.name}, Email: ${row.email}`)
  
})
  

  
// 事务处理
  
db.transaction(() => {
  
  db.update("INSERT INTO users (name) VALUES ('王五')")
  
  db.update("INSERT INTO users (name) VALUES ('赵六')")
  
  // 如果任何操作失败,所有更改都会回滚
  
})
  

  
// 关闭数据库
  
db.close()
  

第七章:数据存储与管理

7.1 数据持久化策略

在开发 JSBox 应用时,选择合适的数据持久化策略非常重要:

选择存储方案的考虑因素

  1. 数据量大小:小量数据使用缓存或文件,大量结构化数据使用数据库

  2. 访问频率:高频访问的数据应该缓存在内存中

  3. 数据结构:简单键值对使用缓存,复杂关系使用数据库

  4. 安全性需求:敏感数据需要加密存储

混合存储策略示例

  
class DataManager {
  
  constructor() {
  
    this.cache = new Map()  // 内存缓存
  
    this.db = $sqlite.open("app.db")
  
    this.initDatabase()
  
  }
  
  
  
  initDatabase() {
  
    this.db.update({
  
      sql: `CREATE TABLE IF NOT EXISTS cache (
  
        key TEXT PRIMARY KEY,
  
        value TEXT,
  
        expire_at INTEGER
  
      )`
  
    })
  
  }
  
  
  
  // 多级缓存策略
  
  async get(key) {
  
    // 1. 检查内存缓存
  
    if (this.cache.has(key)) {
  
      return this.cache.get(key)
  
    }
  
    
  
    // 2. 检查持久化缓存
  
    const result = this.db.query({
  
      sql: "SELECT value, expire_at FROM cache WHERE key = ?",
  
      args: [key]
  
    })
  
    
  
    if (result.length > 0) {
  
      const { value, expire_at } = result[0]
  
      if (!expire_at || expire_at > Date.now()) {
  
        const data = JSON.parse(value)
  
        this.cache.set(key, data)  // 写入内存缓存
  
        return data
  
      } else {
  
        // 已过期,删除
  
        this.db.update({
  
          sql: "DELETE FROM cache WHERE key = ?",
  
          args: [key]
  
        })
  
      }
  
    }
  
    
  
    return null
  
  }
  
  
  
  async set(key, value, ttl = null) {
  
    // 写入内存缓存
  
    this.cache.set(key, value)
  
    
  
    // 写入持久化存储
  
    const expire_at = ttl ? Date.now() + ttl * 1000 : null
  
    this.db.update({
  
      sql: `INSERT OR REPLACE INTO cache (key, value, expire_at) 
  
            VALUES (?, ?, ?)`,
  
      args: [key, JSON.stringify(value), expire_at]
  
    })
  
  }
  
}
  

7.2 文件管理最佳实践

文件组织结构

  
const FileManager = {
  
  // 定义标准目录结构
  
  paths: {
  
    documents: "documents",
  
    images: "documents/images",
  
    cache: "cache",
  
    temp: "temp",
  
    backups: "backups"
  
  },
  
  
  
  // 初始化目录结构
  
  initialize() {
  
    Object.values(this.paths).forEach(path => {
  
      if (!$file.exists(path)) {
  
        $file.mkdir(path)
  
      }
  
    })
  
  },
  
  
  
  // 获取文件路径
  
  getPath(type, filename) {
  
    return `${this.paths[type]}/${filename}`
  
  },
  
  
  
  // 清理临时文件
  
  cleanTemp() {
  
    const tempFiles = $file.list(this.paths.temp)
  
    tempFiles.forEach(file => {
  
      $file.delete(`${this.paths.temp}/${file}`)
  
    })
  
  },
  
  
  
  // 文件大小管理
  
  getDirectorySize(path) {
  
    let totalSize = 0
  
    const files = $file.list(path)
  
    
  
    files.forEach(file => {
  
      const fullPath = `${path}/${file}`
  
      const info = $file.info(fullPath)
  
      
  
      if (info.isDirectory) {
  
        totalSize += this.getDirectorySize(fullPath)
  
      } else {
  
        totalSize += info.size
  
      }
  
    })
  
    
  
    return totalSize
  
  },
  
  
  
  // 自动清理策略
  
  autoClean(maxSize = 50 * 1024 * 1024) {  // 50MB
  
    const cacheSize = this.getDirectorySize(this.paths.cache)
  
    
  
    if (cacheSize > maxSize) {
  
      // 删除最老的文件
  
      const files = $file.list(this.paths.cache)
  
        .map(file => ({
  
          name: file,
  
          info: $file.info(`${this.paths.cache}/${file}`)
  
        }))
  
        .sort((a, b) => a.info.modificationDate - b.info.modificationDate)
  
      
  
      let deletedSize = 0
  
      for (const file of files) {
  
        if (deletedSize >= cacheSize - maxSize) break
  
        
  
        $file.delete(`${this.paths.cache}/${file.name}`)
  
        deletedSize += file.info.size
  
      }
  
    }
  
  }
  
}
  

7.3 数据同步与备份

自动备份机制

  
class BackupManager {
  
  constructor() {
  
    this.backupPath = "backups"
  
    this.maxBackups = 5
  
  }
  
  
  
  // 创建备份
  
  async createBackup() {
  
    const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
  
    const backupName = `backup_${timestamp}`
  
    const backupDir = `${this.backupPath}/${backupName}`
  
    
  
    // 创建备份目录
  
    $file.mkdir(backupDir)
  
    
  
    // 备份数据库
  
    if ($file.exists("app.db")) {
  
      $file.copy({
  
        src: "app.db",
  
        dst: `${backupDir}/app.db`
  
      })
  
    }
  
    
  
    // 备份配置文件
  
    const config = $cache.get("app_config")
  
    if (config) {
  
      $file.write({
  
        data: $data({string: JSON.stringify(config, null, 2)}),
  
        path: `${backupDir}/config.json`
  
      })
  
    }
  
    
  
    // 备份用户数据
  
    const userData = await this.collectUserData()
  
    $file.write({
  
      data: $data({string: JSON.stringify(userData, null, 2)}),
  
      path: `${backupDir}/userdata.json`
  
    })
  
    
  
    // 清理旧备份
  
    this.cleanOldBackups()
  
    
  
    return backupName
  
  }
  
  
  
  // 恢复备份
  
  async restoreBackup(backupName) {
  
    const backupDir = `${this.backupPath}/${backupName}`
  
    
  
    if (!$file.exists(backupDir)) {
  
      throw new Error("备份不存在")
  
    }
  
    
  
    // 恢复数据库
  
    if ($file.exists(`${backupDir}/app.db`)) {
  
      $file.copy({
  
        src: `${backupDir}/app.db`,
  
        dst: "app.db"
  
      })
  
    }
  
    
  
    // 恢复配置
  
    const configData = $file.read(`${backupDir}/config.json`)
  
    if (configData) {
  
      const config = JSON.parse(configData.string)
  
      $cache.set("app_config", config)
  
    }
  
    
  
    // 恢复用户数据
  
    const userData = $file.read(`${backupDir}/userdata.json`)
  
    if (userData) {
  
      await this.restoreUserData(JSON.parse(userData.string))
  
    }
  
    
  
    return true
  
  }
  
  
  
  // 清理旧备份
  
  cleanOldBackups() {
  
    const backups = $file.list(this.backupPath)
  
      .filter(name => name.startsWith("backup_"))
  
      .sort()
  
      .reverse()
  
    
  
    if (backups.length > this.maxBackups) {
  
      backups.slice(this.maxBackups).forEach(backup => {
  
        this.deleteBackup(backup)
  
      })
  
    }
  
  }
  
  
  
  // 删除备份
  
  deleteBackup(backupName) {
  
    const backupDir = `${this.backupPath}/${backupName}`
  
    if ($file.exists(backupDir)) {
  
      // 递归删除目录
  
      this.deleteDirectory(backupDir)
  
    }
  
  }
  
  
  
  deleteDirectory(path) {
  
    const items = $file.list(path)
  
    items.forEach(item => {
  
      const fullPath = `${path}/${item}`
  
      const info = $file.info(fullPath)
  
      
  
      if (info.isDirectory) {
  
        this.deleteDirectory(fullPath)
  
      } else {
  
        $file.delete(fullPath)
  
      }
  
    })
  
    $file.delete(path)
  
  }
  
}
  

第八章:网络请求与通信

8.1 RESTful API 客户端

创建一个功能完整的 API 客户端:

  
class APIClient {
  
  constructor(baseURL) {
  
    this.baseURL = baseURL
  
    this.headers = {
  
      "Content-Type": "application/json",
  
      "Accept": "application/json"
  
    }
  
    this.timeout = 30  // 秒
  
  }
  
  
  
  // 设置认证令牌
  
  setAuthToken(token) {
  
    if (token) {
  
      this.headers["Authorization"] = `Bearer ${token}`
  
    } else {
  
      delete this.headers["Authorization"]
  
    }
  
  }
  
  
  
  // 通用请求方法
  
  async request(method, endpoint, options = {}) {
  
    const url = `${this.baseURL}${endpoint}`
  
    const config = {
  
      method: method,
  
      url: url,
  
      header: { ...this.headers, ...options.headers },
  
      timeout: options.timeout || this.timeout
  
    }
  
    
  
    // 添加请求体
  
    if (options.body) {
  
      if (method === "GET") {
  
        // GET 请求使用查询参数
  
        const params = new URLSearchParams(options.body)
  
        config.url = `${url}?${params.toString()}`
  
      } else {
  
        config.body = options.body
  
      }
  
    }
  
    
  
    // 处理文件上传
  
    if (options.files) {
  
      config.files = options.files
  
      delete config.header["Content-Type"]  // 让系统自动设置
  
    }
  
    
  
    return new Promise((resolve, reject) => {
  
      $http.request({
  
        ...config,
  
        handler: (resp) => {
  
          if (resp.error) {
  
            reject(this.handleError(resp))
  
          } else {
  
            resolve(this.handleResponse(resp))
  
          }
  
        }
  
      })
  
    })
  
  }
  
  
  
  // 处理响应
  
  handleResponse(resp) {
  
    const response = {
  
      data: resp.data,
  
      status: resp.response.statusCode,
  
      headers: resp.response.headers
  
    }
  
    
  
    // 自动解析 JSON
  
    if (response.headers["Content-Type"]?.includes("application/json")) {
  
      try {
  
        response.data = JSON.parse(response.data)
  
      } catch (e) {
  
        console.warn("JSON 解析失败")
  
      }
  
    }
  
    
  
    return response
  
  }
  
  
  
  // 处理错误
  
  handleError(resp) {
  
    const error = new Error(resp.error.localizedDescription || "网络请求失败")
  
    error.response = resp.response
  
    error.code = resp.error.code
  
    
  
    // 尝试解析错误响应
  
    if (resp.data) {
  
      try {
  
        error.data = JSON.parse(resp.data)
  
      } catch (e) {
  
        error.data = resp.data
  
      }
  
    }
  
    
  
    return error
  
  }
  
  
  
  // 便捷方法
  
  get(endpoint, params) {
  
    return this.request("GET", endpoint, { body: params })
  
  }
  
  
  
  post(endpoint, data) {
  
    return this.request("POST", endpoint, { body: data })
  
  }
  
  
  
  put(endpoint, data) {
  
    return this.request("PUT", endpoint, { body: data })
  
  }
  
  
  
  delete(endpoint) {
  
    return this.request("DELETE", endpoint)
  
  }
  
  
  
  // 文件上传
  
  upload(endpoint, files, data = {}) {
  
    return this.request("POST", endpoint, {
  
      files: files,
  
      body: data
  
    })
  
  }
  
}
  

  
// 使用示例
  
const api = new APIClient("https://api.example.com")
  
api.setAuthToken("your-auth-token")
  

  
// 获取用户列表
  
api.get("/users", { page: 1, limit: 20 })
  
  .then(response => {
  
    console.log("用户列表:", response.data)
  
  })
  
  .catch(error => {
  
    console.error("获取失败:", error)
  
  })
  

8.2 WebSocket 实时通信

实现一个完整的 WebSocket 管理器:

  
class WebSocketManager {
  
  constructor(url) {
  
    this.url = url
  
    this.ws = null
  
    this.reconnectAttempts = 0
  
    this.maxReconnectAttempts = 5
  
    this.reconnectDelay = 1000
  
    this.heartbeatInterval = 30000
  
    this.messageHandlers = new Map()
  
    this.eventHandlers = {}
  
  }
  
  
  
  // 连接 WebSocket
  
  connect() {
  
    return new Promise((resolve, reject) => {
  
      this.ws = $websocket.new({
  
        url: this.url,
  
        header: this.headers,
  
        handler: (message) => {
  
          this.handleMessage(message)
  
        },
  
        eventHandler: {
  
          open: () => {
  
            console.log("WebSocket 已连接")
  
            this.reconnectAttempts = 0
  
            this.startHeartbeat()
  
            resolve()
  
            this.emit("open")
  
          },
  
          error: (error) => {
  
            console.error("WebSocket 错误:", error)
  
            this.emit("error", error)
  
            reject(error)
  
          },
  
          close: (code, reason) => {
  
            console.log("WebSocket 已关闭:", code, reason)
  
            this.stopHeartbeat()
  
            this.emit("close", { code, reason })
  
            this.attemptReconnect()
  
          }
  
        }
  
      })
  
    })
  
  }
  
  
  
  // 发送消息
  
  send(type, data) {
  
    if (!this.ws || this.ws.readyState !== 1) {
  
      throw new Error("WebSocket 未连接")
  
    }
  
    
  
    const message = {
  
      type: type,
  
      data: data,
  
      timestamp: Date.now()
  
    }
  
    
  
    this.ws.send(JSON.stringify(message))
  
  }
  
  
  
  // 处理接收到的消息
  
  handleMessage(rawMessage) {
  
    try {
  
      const message = JSON.parse(rawMessage)
  
      
  
      // 处理心跳响应
  
      if (message.type === "pong") {
  
        return
  
      }
  
      
  
      // 调用对应的消息处理器
  
      const handler = this.messageHandlers.get(message.type)
  
      if (handler) {
  
        handler(message.data)
  
      } else {
  
        console.warn("未处理的消息类型:", message.type)
  
      }
  
      
  
      // 触发通用消息事件
  
      this.emit("message", message)
  
    } catch (error) {
  
      console.error("消息解析失败:", error)
  
    }
  
  }
  
  
  
  // 注册消息处理器
  
  on(messageType, handler) {
  
    this.messageHandlers.set(messageType, handler)
  
  }
  
  
  
  // 注册事件处理器
  
  addEventListener(event, handler) {
  
    if (!this.eventHandlers[event]) {
  
      this.eventHandlers[event] = []
  
    }
  
    this.eventHandlers[event].push(handler)
  
  }
  
  
  
  // 触发事件
  
  emit(event, data) {
  
    const handlers = this.eventHandlers[event]
  
    if (handlers) {
  
      handlers.forEach(handler => handler(data))
  
    }
  
  }
  
  
  
  // 心跳机制
  
  startHeartbeat() {
  
    this.heartbeatTimer = setInterval(() => {
  
      this.send("ping", {})
  
    }, this.heartbeatInterval)
  
  }
  
  
  
  stopHeartbeat() {
  
    if (this.heartbeatTimer) {
  
      clearInterval(this.heartbeatTimer)
  
      this.heartbeatTimer = null
  
    }
  
  }
  
  
  
  // 重连机制
  
  attemptReconnect() {
  
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
  
      console.error("WebSocket 重连失败")
  
      this.emit("reconnectFailed")
  
      return
  
    }
  
    
  
    this.reconnectAttempts++
  
    const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)
  
    
  
    console.log(`${delay}ms 后尝试第 ${this.reconnectAttempts} 次重连`)
  
    
  
    setTimeout(() => {
  
      this.connect().catch(error => {
  
        console.error("重连失败:", error)
  
      })
  
    }, delay)
  
  }
  
  
  
  // 断开连接
  
  disconnect() {
  
    this.stopHeartbeat()
  
    if (this.ws) {
  
      this.ws.close()
  
      this.ws = null
  
    }
  
  }
  
}
  

  
// 使用示例
  
const wsManager = new WebSocketManager("wss://example.com/socket")
  

  
// 注册消息处理器
  
wsManager.on("chat", (data) => {
  
  console.log("收到聊天消息:", data)
  
  appendChatMessage(data)
  
})
  

  
wsManager.on("notification", (data) => {
  
  showNotification(data)
  
})
  

  
// 注册事件处理器
  
wsManager.addEventListener("open", () => {
  
  console.log("连接已建立")
  
  wsManager.send("subscribe", { channel: "updates" })
  
})
  

  
// 连接
  
wsManager.connect()
  

8.3 网络状态监控

实现网络状态监控和自适应策略:

  
class NetworkMonitor {
  
  constructor() {
  
    this.isOnline = true
  
    this.networkType = $device.networkType
  
    this.listeners = []
  
    this.startMonitoring()
  
  }
  
  
  
  // 开始监控
  
  startMonitoring() {
  
    // 定期检查网络状态
  
    this.monitorTimer = setInterval(() => {
  
      this.checkNetworkStatus()
  
    }, 5000)
  
    
  
    // 立即检查一次
  
    this.checkNetworkStatus()
  
  }
  
  
  
  // 检查网络状态
  
  async checkNetworkStatus() {
  
    const currentType = $device.networkType
  
    const wasOnline = this.isOnline
  
    
  
    // 网络类型变化
  
    if (currentType !== this.networkType) {
  
      this.networkType = currentType
  
      this.notifyListeners("typeChanged", {
  
        oldType: this.networkType,
  
        newType: currentType
  
      })
  
    }
  
    
  
    // 测试网络连通性
  
    try {
  
      const response = await this.pingServer()
  
      this.isOnline = response.success
  
    } catch (error) {
  
      this.isOnline = false
  
    }
  
    
  
    // 网络状态变化
  
    if (wasOnline !== this.isOnline) {
  
      this.notifyListeners("statusChanged", {
  
        online: this.isOnline
  
      })
  
    }
  
  }
  
  
  
  // 测试服务器连通性
  
  pingServer() {
  
    return new Promise((resolve) => {
  
      $http.get({
  
        url: "https://www.apple.com/library/test/success.html",
  
        timeout: 3,
  
        handler: (resp) => {
  
          resolve({
  
            success: !resp.error,
  
            latency: resp.response ? resp.response.suggestedFilename : null
  
          })
  
        }
  
      })
  
    })
  
  }
  
  
  
  // 添加监听器
  
  addListener(listener) {
  
    this.listeners.push(listener)
  
  }
  
  
  
  // 通知监听器
  
  notifyListeners(event, data) {
  
    this.listeners.forEach(listener => {
  
      listener(event, data)
  
    })
  
  }
  
  
  
  // 获取网络质量
  
  getNetworkQuality() {
  
    if (!this.isOnline) return "offline"
  
    
  
    switch (this.networkType) {
  
      case 1: return "excellent"  // Wi-Fi
  
      case 2: return "good"       // 蜂窝网络
  
      default: return "unknown"
  
    }
  
  }
  
  
  
  // 根据网络状态调整策略
  
  getAdaptiveStrategy() {
  
    const quality = this.getNetworkQuality()
  
    
  
    return {
  
      // 图片质量
  
      imageQuality: quality === "excellent" ? "high" : "medium",
  
      // 是否预加载
  
      enablePreload: quality === "excellent",
  
      // 缓存策略
  
      cacheStrategy: quality === "offline" ? "cacheOnly" : "networkFirst",
  
      // 同步频率
  
      syncInterval: quality === "excellent" ? 30 : 300,  // 秒
  
      // 批量大小
  
      batchSize: quality === "excellent" ? 100 : 20
  
    }
  
  }
  
  
  
  // 停止监控
  
  stopMonitoring() {
  
    if (this.monitorTimer) {
  
      clearInterval(this.monitorTimer)
  
      this.monitorTimer = null
  
    }
  
  }
  
}
  

  
// 使用网络监控
  
const networkMonitor = new NetworkMonitor()
  

  
networkMonitor.addListener((event, data) => {
  
  if (event === "statusChanged") {
  
    if (data.online) {
  
      console.log("网络已恢复")
  
      // 执行离线期间的同步任务
  
      syncOfflineData()
  
    } else {
  
      console.log("网络已断开")
  
      // 切换到离线模式
  
      enableOfflineMode()
  
    }
  
  } else if (event === "typeChanged") {
  
    console.log(`网络类型变化:${data.oldType} -> ${data.newType}`)
  
    // 调整应用策略
  
    updateAppStrategy(networkMonitor.getAdaptiveStrategy())
  
  }
  
})
  

第九章:系统集成与扩展

9.1 系统服务调用

JSBox 可以调用多种 iOS 系统服务:

相机和相册

  
// 拍照
  
$photo.take({
  
  handler: function(resp) {
  
    if (resp.image) {
  
      processImage(resp.image)
  
    }
  
  }
  
})
  

  
// 选择照片
  
$photo.pick({
  
  multi: true,  // 允许多选
  
  max: 9,       // 最多选择9张
  
  handler: function(resp) {
  
    if (resp.results) {
  
      resp.results.forEach(result => {
  
        console.log("选择了图片:", result.filename)
  
        processImage(result.image)
  
      })
  
    }
  
  }
  
})
  

  
// 保存图片到相册
  
$photo.save({
  
  image: processedImage,
  
  handler: function(success) {
  
    if (success) {
  
      $ui.toast("图片已保存到相册")
  
    }
  
  }
  
})
  

  
// 扫描二维码
  
$qrcode.scan({
  
  handler: function(text) {
  
    if (text) {
  
      console.log("扫描结果:", text)
  
      handleQRCodeData(text)
  
    }
  
  }
  
})
  

  
// 生成二维码
  
const qrcodeImage = $qrcode.encode({
  
  text: "https://example.com",
  
  size: $size(200, 200),
  
  color: $color("black"),
  
  background: $color("white"),
  
  correction: $qrcode.correction.high
  
})
  

位置服务

  
// 获取当前位置
  
$location.fetch({
  
  handler: function(resp) {
  
    if (resp.error) {
  
      console.error("获取位置失败:", resp.error)
  
    } else {
  
      const { lat, lng, alt } = resp
  
      console.log(`位置:${lat}, ${lng}, 海拔:${alt}`)
  
      
  
      // 反向地理编码
  
      reverseGeocode(lat, lng)
  
    }
  
  }
  
})
  

  
// 持续监听位置变化
  
$location.start({
  
  accuracy: "best",  // 精度:best, nearestTenMeters, hundredMeters, kilometer
  
  distance: 10,      // 最小移动距离(米)
  
  handler: function(resp) {
  
    updateUserLocation(resp.lat, resp.lng)
  
  }
  
})
  

  
// 停止位置监听
  
$location.stop()
  

  
// 计算两点间距离
  
function calculateDistance(lat1, lng1, lat2, lng2) {
  
  const R = 6371  // 地球半径(公里)
  
  const dLat = (lat2 - lat1) * Math.PI / 180
  
  const dLng = (lng2 - lng1) * Math.PI / 180
  
  const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
  
    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
  
    Math.sin(dLng/2) * Math.sin(dLng/2)
  
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
  
  return R * c
  
}
  

音频播放

  
// 播放系统声音
  
$device.taptic(1)  // 触觉反馈
  
$system.vibrate()  // 震动
  

  
// 播放音频文件
  
const audio = $audio.play({
  
  path: "assets/sound.mp3",
  
  events: {
  
    didStart: function() {
  
      console.log("开始播放")
  
    },
  
    didFinish: function() {
  
      console.log("播放完成")
  
    },
  
    didPause: function() {
  
      console.log("播放暂停")
  
    }
  
  }
  
})
  

  
// 控制播放
  
audio.pause()
  
audio.resume()
  
audio.stop()
  

  
// 设置音量
  
audio.volume = 0.8
  

  
// 文字转语音
  
$text.speech({
  
  text: "你好,这是语音合成示例",
  
  rate: 0.5,  // 语速
  
  language: "zh-CN",  // 语言
  
  handler: function() {
  
    console.log("语音播放完成")
  
  }
  
})
  

9.2 Widget 开发

JSBox 支持创建 iOS 小组件:

  
// Widget 配置
  
$widget.setTimeline({
  
  entries: [
  
    {
  
      date: new Date(),
  
      info: {
  
        title: "今日任务",
  
        content: "完成 5 个待办事项",
  
        progress: 0.6
  
      }
  
    }
  
  ],
  
  policy: {
  
    atEnd: true
  
  },
  
  render: function(ctx) {
  
    const info = ctx.entry.info
  
    
  
    return {
  
      type: "vstack",
  
      props: {
  
        alignment: $widget.alignment.leading,
  
        spacing: 10,
  
        padding: 15,
  
        background: $color("#F0F0F0")
  
      },
  
      views: [
  
        {
  
          type: "text",
  
          props: {
  
            text: info.title,
  
            font: $font("bold", 18),
  
            color: $color("#333333")
  
          }
  
        },
  
        {
  
          type: "text",
  
          props: {
  
            text: info.content,
  
            font: $font(14),
  
            color: $color("#666666")
  
          }
  
        },
  
        {
  
          type: "hstack",
  
          props: {
  
            spacing: 5
  
          },
  
          views: [
  
            {
  
              type: "progress",
  
              props: {
  
                value: info.progress,
  
                color: $color("#007AFF")
  
              }
  
            },
  
            {
  
              type: "text",
  
              props: {
  
                text: `${Math.round(info.progress * 100)}%`,
  
                font: $font(12),
  
                color: $color("#999999")
  
              }
  
            }
  
          ]
  
        }
  
      ]
  
    }
  
  }
  
})
  

  
// 更新 Widget 数据
  
function updateWidget(data) {
  
  $widget.setTimeline({
  
    entries: data.map(item => ({
  
      date: item.date,
  
      info: item
  
    })),
  
    policy: {
  
      after: new Date(Date.now() + 3600 * 1000)  // 1小时后更新
  
    }
  
  })
  
}
  

9.3 快捷指令集成

JSBox 可以与 iOS 快捷指令集成:

  
// 注册快捷指令
  
$shortcuts.register({
  
  name: "运行 JSBox 脚本",
  
  description: "运行指定的 JSBox 脚本",
  
  icon: {
  
    glyph: "command",
  
    color: "#FF6B6B"
  
  },
  
  params: [
  
    {
  
      name: "scriptName",
  
      type: "text",
  
      default: "",
  
      required: true
  
    },
  
    {
  
      name: "params",
  
      type: "dictionary",
  
      default: {}
  
    }
  
  ],
  
  handler: function(params) {
  
    const { scriptName, params: scriptParams } = params
  
    
  
    // 运行指定脚本
  
    $addin.run(scriptName, scriptParams)
  
    
  
    // 返回结果
  
    return {
  
      success: true,
  
      result: "脚本运行完成"
  
    }
  
  }
  
})
  

  
// 从快捷指令接收数据
  
if ($context.query) {
  
  const { action, data } = $context.query
  
  
  
  switch (action) {
  
    case "process":
  
      processShortcutData(data)
  
      break
  
    case "share":
  
      handleSharedData(data)
  
      break
  
    default:
  
      console.log("未知操作:", action)
  
  }
  
}
  

  
// 调用快捷指令
  
$shortcuts.run({
  
  name: "发送通知",
  
  input: {
  
    title: "任务提醒",
  
    message: "您有新的任务待处理"
  
  },
  
  handler: function(result) {
  
    console.log("快捷指令执行结果:", result)
  
  }
  
})
  

第十章:高级功能与技巧

10.1 性能优化

内存优化

  
class MemoryManager {
  
  constructor() {
  
    this.cache = new Map()
  
    this.maxCacheSize = 50 * 1024 * 1024  // 50MB
  
    this.currentSize = 0
  
  }
  
  
  
  // 添加缓存(LRU 策略)
  
  set(key, value, size) {
  
    // 如果已存在,先删除旧的
  
    if (this.cache.has(key)) {
  
      const oldItem = this.cache.get(key)
  
      this.currentSize -= oldItem.size
  
      this.cache.delete(key)
  
    }
  
    
  
    // 检查是否需要清理
  
    while (this.currentSize + size > this.maxCacheSize && this.cache.size > 0) {
  
      const firstKey = this.cache.keys().next().value
  
      const item = this.cache.get(firstKey)
  
      this.currentSize -= item.size
  
      this.cache.delete(firstKey)
  
    }
  
    
  
    // 添加新项
  
    this.cache.set(key, { value, size, lastAccess: Date.now() })
  
    this.currentSize += size
  
  }
  
  
  
  // 获取缓存
  
  get(key) {
  
    const item = this.cache.get(key)
  
    if (item) {
  
      // 更新访问时间(LRU)
  
      item.lastAccess = Date.now()
  
      // 移到末尾
  
      this.cache.delete(key)
  
      this.cache.set(key, item)
  
      return item.value
  
    }
  
    return null
  
  }
  
  
  
  // 清理缓存
  
  clear() {
  
    this.cache.clear()
  
    this.currentSize = 0
  
  }
  
  
  
  // 获取内存使用情况
  
  getMemoryInfo() {
  
    return {
  
      used: this.currentSize,
  
      max: this.maxCacheSize,
  
      items: this.cache.size,
  
      percentage: (this.currentSize / this.maxCacheSize * 100).toFixed(2) + '%'
  
    }
  
  }
  
}
  

  
// 图片缓存优化
  
class ImageCache {
  
  constructor() {
  
    this.memoryManager = new MemoryManager()
  
  }
  
  
  
  async loadImage(url) {
  
    // 先检查内存缓存
  
    let image = this.memoryManager.get(url)
  
    if (image) {
  
      return image
  
    }
  
    
  
    // 检查磁盘缓存
  
    const filename = this.urlToFilename(url)
  
    const cachePath = `cache/images/${filename}`
  
    
  
    if ($file.exists(cachePath)) {
  
      const data = $file.read(cachePath)
  
      if (data) {
  
        image = $image(data)
  
        // 添加到内存缓存
  
        this.memoryManager.set(url, image, data.length)
  
        return image
  
      }
  
    }
  
    
  
    // 从网络加载
  
    return new Promise((resolve, reject) => {
  
      $http.download({
  
        url: url,
  
        handler: (resp) => {
  
          if (resp.data) {
  
            // 保存到磁盘
  
            $file.write({
  
              data: resp.data,
  
              path: cachePath
  
            })
  
            
  
            // 创建图片对象
  
            image = $image(resp.data)
  
            
  
            // 添加到内存缓存
  
            this.memoryManager.set(url, image, resp.data.length)
  
            
  
            resolve(image)
  
          } else {
  
            reject(new Error("图片加载失败"))
  
          }
  
        }
  
      })
  
    })
  
  }
  
  
  
  urlToFilename(url) {
  
    // 将 URL 转换为安全的文件名
  
    return $text.MD5(url) + ".jpg"
  
  }
  
}
  

渲染优化

  
// 虚拟列表实现
  
class VirtualList {
  
  constructor(options) {
  
    this.itemHeight = options.itemHeight
  
    this.items = options.items
  
    this.visibleCount = Math.ceil($device.info.screen.height / this.itemHeight) + 2
  
    this.startIndex = 0
  
  }
  
  
  
  render() {
  
    return {
  
      type: "list",
  
      props: {
  
        id: "virtualList",
  
        rowHeight: this.itemHeight,
  
        data: this.getVisibleItems()
  
      },
  
      layout: $layout.fill,
  
      events: {
  
        didScroll: (sender) => {
  
          this.handleScroll(sender)
  
        },
  
        didSelect: (sender, indexPath, data) => {
  
          const realIndex = this.startIndex + indexPath.row
  
          this.options.onSelect(this.items[realIndex], realIndex)
  
        }
  
      }
  
    }
  
  }
  
  
  
  getVisibleItems() {
  
    const endIndex = Math.min(
  
      this.startIndex + this.visibleCount,
  
      this.items.length
  
    )
  
    return this.items.slice(this.startIndex, endIndex)
  
  }
  
  
  
  handleScroll(sender) {
  
    const offset = sender.contentOffset.y
  
    const newStartIndex = Math.floor(offset / this.itemHeight)
  
    
  
    if (newStartIndex !== this.startIndex) {
  
      this.startIndex = Math.max(0, newStartIndex)
  
      sender.data = this.getVisibleItems()
  
    }
  
  }
  
  
  
  updateItems(items) {
  
    this.items = items
  
    $("virtualList").data = this.getVisibleItems()
  
  }
  
}
  

  
// 防抖和节流
  
function debounce(func, delay) {
  
  let timeoutId
  
  return function (...args) {
  
    clearTimeout(timeoutId)
  
    timeoutId = setTimeout(() => func.apply(this, args), delay)
  
  }
  
}
  

  
function throttle(func, limit) {
  
  let inThrottle
  
  return function (...args) {
  
    if (!inThrottle) {
  
      func.apply(this, args)
  
      inThrottle = true
  
      setTimeout(() => inThrottle = false, limit)
  
    }
  
  }
  
}
  

  
// 使用示例
  
const searchHandler = debounce((text) => {
  
  performSearch(text)
  
}, 300)
  

  
const scrollHandler = throttle((offset) => {
  
  updateScrollPosition(offset)
  
}, 100)
  

10.2 安全性考虑

数据加密

  
class SecurityManager {
  
  // 使用 AES 加密
  
  encrypt(text, password) {
  
    return $text.AESEncrypt(text, password)
  
  }
  
  
  
  // 使用 AES 解密
  
  decrypt(encryptedText, password) {
  
    return $text.AESDecrypt(encryptedText, password)
  
  }
  
  
  
  // 生成安全的随机密钥
  
  generateKey(length = 32) {
  
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'
  
    let key = ''
  
    for (let i = 0; i < length; i++) {
  
      key += chars.charAt(Math.floor(Math.random() * chars.length))
  
    }
  
    return key
  
  }
  
  
  
  // 哈希密码
  
  hashPassword(password, salt) {
  
    return $text.SHA256(password + salt)
  
  }
  
  
  
  // 安全存储敏感数据
  
  secureStore(key, value, password) {
  
    const encrypted = this.encrypt(JSON.stringify(value), password)
  
    $keychain.set(key, encrypted)
  
  }
  
  
  
  // 安全读取敏感数据
  
  secureRetrieve(key, password) {
  
    const encrypted = $keychain.get(key)
  
    if (encrypted) {
  
      try {
  
        const decrypted = this.decrypt(encrypted, password)
  
        return JSON.parse(decrypted)
  
      } catch (error) {
  
        console.error("解密失败")
  
        return null
  
      }
  
    }
  
    return null
  
  }
  
  
  
  // 清理敏感数据
  
  clearSensitiveData() {
  
    // 清理内存中的敏感变量
  
    sensitiveData = null
  
    
  
    // 清理剪贴板
  
    if ($clipboard.text && $clipboard.text.includes("password")) {
  
      $clipboard.clear()
  
    }
  
    
  
    // 清理临时文件
  
    const tempFiles = $file.list("temp")
  
    tempFiles.forEach(file => {
  
      if (file.includes("sensitive")) {
  
        $file.delete(`temp/${file}`)
  
      }
  
    })
  
  }
  
}
  

  
// API 请求签名
  
class APISecurityManager {
  
  constructor(apiKey, apiSecret) {
  
    this.apiKey = apiKey
  
    this.apiSecret = apiSecret
  
  }
  
  
  
  // 生成请求签名
  
  generateSignature(method, path, params, timestamp) {
  
    // 1. 排序参数
  
    const sortedParams = Object.keys(params)
  
      .sort()
  
      .map(key => `${key}=${params[key]}`)
  
      .join('&')
  
    
  
    // 2. 构建签名字符串
  
    const signatureString = `${method}\n${path}\n${sortedParams}\n${timestamp}`
  
    
  
    // 3. 计算 HMAC-SHA256
  
    return $text.HMACSHA256(signatureString, this.apiSecret)
  
  }
  
  
  
  // 创建安全的请求头
  
  createSecureHeaders(method, path, params = {}) {
  
    const timestamp = Date.now()
  
    const signature = this.generateSignature(method, path, params, timestamp)
  
    
  
    return {
  
      'X-API-Key': this.apiKey,
  
      'X-Timestamp': timestamp.toString(),
  
      'X-Signature': signature
  
    }
  
  }
  
}
  

10.3 插件系统

创建可扩展的插件架构:

  
// 插件管理器
  
class PluginManager {
  
  constructor() {
  
    this.plugins = new Map()
  
    this.hooks = new Map()
  
  }
  
  
  
  // 注册插件
  
  register(plugin) {
  
    if (!plugin.name || !plugin.version) {
  
      throw new Error("插件必须包含 name 和 version")
  
    }
  
    
  
    console.log(`注册插件:${plugin.name} v${plugin.version}`)
  
    
  
    // 检查依赖
  
    if (plugin.dependencies) {
  
      for (const [dep, version] of Object.entries(plugin.dependencies)) {
  
        if (!this.checkDependency(dep, version)) {
  
          throw new Error(`缺少依赖:${dep} >= ${version}`)
  
        }
  
      }
  
    }
  
    
  
    // 初始化插件
  
    if (plugin.init) {
  
      plugin.init(this)
  
    }
  
    
  
    // 注册钩子
  
    if (plugin.hooks) {
  
      for (const [hookName, handler] of Object.entries(plugin.hooks)) {
  
        this.addHook(hookName, handler)
  
      }
  
    }
  
    
  
    this.plugins.set(plugin.name, plugin)
  
  }
  
  
  
  // 检查依赖
  
  checkDependency(name, requiredVersion) {
  
    const plugin = this.plugins.get(name)
  
    if (!plugin) return false
  
    
  
    return this.compareVersions(plugin.version, requiredVersion) >= 0
  
  }
  
  
  
  // 版本比较
  
  compareVersions(v1, v2) {
  
    const parts1 = v1.split('.').map(Number)
  
    const parts2 = v2.split('.').map(Number)
  
    
  
    for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
  
      const part1 = parts1[i] || 0
  
      const part2 = parts2[i] || 0
  
      
  
      if (part1 > part2) return 1
  
      if (part1 < part2) return -1
  
    }
  
    
  
    return 0
  
  }
  
  
  
  // 添加钩子
  
  addHook(name, handler) {
  
    if (!this.hooks.has(name)) {
  
      this.hooks.set(name, [])
  
    }
  
    this.hooks.get(name).push(handler)
  
  }
  
  
  
  // 执行钩子
  
  async executeHook(name, ...args) {
  
    const handlers = this.hooks.get(name) || []
  
    const results = []
  
    
  
    for (const handler of handlers) {
  
      try {
  
        const result = await handler(...args)
  
        results.push(result)
  
      } catch (error) {
  
        console.error(`钩子执行错误 (${name}):`, error)
  
      }
  
    }
  
    
  
    return results
  
  }
  
  
  
  // 获取插件
  
  getPlugin(name) {
  
    return this.plugins.get(name)
  
  }
  
  
  
  // 卸载插件
  
  unregister(name) {
  
    const plugin = this.plugins.get(name)
  
    if (!plugin) return
  
    
  
    // 执行清理
  
    if (plugin.cleanup) {
  
      plugin.cleanup()
  
    }
  
    
  
    // 移除钩子
  
    if (plugin.hooks) {
  
      // 这里需要更复杂的实现来准确移除特定插件的钩子
  
    }
  
    
  
    this.plugins.delete(name)
  
    console.log(`插件已卸载:${name}`)
  
  }
  
}
  

  
// 示例插件
  
const analyticsPlugin = {
  
  name: "analytics",
  
  version: "1.0.0",
  
  description: "应用分析插件",
  
  
  
  init(pluginManager) {
  
    this.startTime = Date.now()
  
    this.events = []
  
  },
  
  
  
  hooks: {
  
    beforeRequest: async (config) => {
  
      // 在请求前记录
  
      this.logEvent("api_request", {
  
        url: config.url,
  
        method: config.method
  
      })
  
    },
  
    
  
    afterRender: async (view) => {
  
      // 界面渲染后记录
  
      this.logEvent("view_rendered", {
  
        viewType: view.type
  
      })
  
    }
  
  },
  
  
  
  logEvent(name, data) {
  
    this.events.push({
  
      name,
  
      data,
  
      timestamp: Date.now()
  
    })
  
  },
  
  
  
  getReport() {
  
    return {
  
      sessionDuration: Date.now() - this.startTime,
  
      totalEvents: this.events.length,
  
      events: this.events
  
    }
  
  },
  
  
  
  cleanup() {
  
    // 发送分析数据
  
    this.sendAnalytics(this.getReport())
  
  }
  
}
  

第十一章:实战项目开发

11.1 项目一:任务管理应用

创建一个功能完整的任务管理应用:

  
// 主应用类
  
class TaskManager {
  
  constructor() {
  
    this.tasks = []
  
    this.categories = ["工作", "生活", "学习", "其他"]
  
    this.db = $sqlite.open("tasks.db")
  
    this.initDatabase()
  
    this.loadTasks()
  
  }
  
  
  
  initDatabase() {
  
    this.db.update({
  
      sql: `CREATE TABLE IF NOT EXISTS tasks (
  
        id INTEGER PRIMARY KEY AUTOINCREMENT,
  
        title TEXT NOT NULL,
  
        description TEXT,
  
        category TEXT,
  
        priority INTEGER DEFAULT 0,
  
        completed BOOLEAN DEFAULT 0,
  
        due_date INTEGER,
  
        created_at INTEGER DEFAULT (strftime('%s', 'now')),
  
        updated_at INTEGER DEFAULT (strftime('%s', 'now'))
  
      )`
  
    })
  
  }
  
  
  
  loadTasks() {
  
    const results = this.db.query("SELECT * FROM tasks ORDER BY priority DESC, due_date ASC")
  
    this.tasks = results.map(row => ({
  
      id: row.id,
  
      title: row.title,
  
      description: row.description,
  
      category: row.category,
  
      priority: row.priority,
  
      completed: Boolean(row.completed),
  
      dueDate: row.due_date ? new Date(row.due_date * 1000) : null,
  
      createdAt: new Date(row.created_at * 1000),
  
      updatedAt: new Date(row.updated_at * 1000)
  
    }))
  
  }
  
  
  
  addTask(task) {
  
    const result = this.db.update({
  
      sql: `INSERT INTO tasks (title, description, category, priority, due_date) 
  
            VALUES (?, ?, ?, ?, ?)`,
  
      args: [
  
        task.title,
  
        task.description || "",
  
        task.category || "其他",
  
        task.priority || 0,
  
        task.dueDate ? Math.floor(task.dueDate.getTime() / 1000) : null
  
      ]
  
    })
  
    
  
    if (result.error) {
  
      throw new Error("添加任务失败")
  
    }
  
    
  
    task.id = result.insertId
  
    task.completed = false
  
    task.createdAt = new Date()
  
    task.updatedAt = new Date()
  
    
  
    this.tasks.push(task)
  
    this.notifyUpdate()
  
    
  
    return task
  
  }
  
  
  
  updateTask(id, updates) {
  
    const sets = []
  
    const args = []
  
    
  
    Object.entries(updates).forEach(([key, value]) => {
  
      if (key === 'dueDate') {
  
        sets.push("due_date = ?")
  
        args.push(value ? Math.floor(value.getTime() / 1000) : null)
  
      } else if (key === 'completed') {
  
        sets.push("completed = ?")
  
        args.push(value ? 1 : 0)
  
      } else {
  
        sets.push(`${key} = ?`)
  
        args.push(value)
  
      }
  
    })
  
    
  
    sets.push("updated_at = strftime('%s', 'now')")
  
    args.push(id)
  
    
  
    this.db.update({
  
      sql: `UPDATE tasks SET ${sets.join(', ')} WHERE id = ?`,
  
      args: args
  
    })
  
    
  
    // 更新内存中的任务
  
    const taskIndex = this.tasks.findIndex(t => t.id === id)
  
    if (taskIndex !== -1) {
  
      Object.assign(this.tasks[taskIndex], updates)
  
      this.tasks[taskIndex].updatedAt = new Date()
  
    }
  
    
  
    this.notifyUpdate()
  
  }
  
  
  
  deleteTask(id) {
  
    this.db.update({
  
      sql: "DELETE FROM tasks WHERE id = ?",
  
      args: [id]
  
    })
  
    
  
    this.tasks = this.tasks.filter(t => t.id !== id)
  
    this.notifyUpdate()
  
  }
  
  
  
  getTasksByCategory(category) {
  
    return this.tasks.filter(t => t.category === category)
  
  }
  
  
  
  getIncompleteTasks() {
  
    return this.tasks.filter(t => !t.completed)
  
  }
  
  
  
  getOverdueTasks() {
  
    const now = new Date()
  
    return this.tasks.filter(t => 
  
      !t.completed && t.dueDate && t.dueDate < now
  
    )
  
  }
  
  
  
  notifyUpdate() {
  
    // 触发界面更新
  
    $("taskList").data = this.formatTasksForList()
  
    this.updateStatistics()
  
  }
  
  
  
  formatTasksForList() {
  
    return this.tasks.map(task => ({
  
      id: task.id,
  
      title: {
  
        text: task.title
  
      },
  
      subtitle: {
  
        text: this.formatTaskSubtitle(task)
  
      },
  
      completed: {
  
        on: task.completed
  
      },
  
      priority: {
  
        color: this.getPriorityColor(task.priority)
  
      }
  
    }))
  
  }
  
  
  
  formatTaskSubtitle(task) {
  
    const parts = []
  
    if (task.category) parts.push(task.category)
  
    if (task.dueDate) {
  
      const dueText = this.formatDueDate(task.dueDate)
  
      parts.push(dueText)
  
    }
  
    return parts.join(" • ")
  
  }
  
  
  
  formatDueDate(date) {
  
    const now = new Date()
  
    const diff = date - now
  
    const days = Math.floor(diff / (1000 * 60 * 60 * 24))
  
    
  
    if (days < 0) return `已过期 ${-days} 天`
  
    if (days === 0) return "今天到期"
  
    if (days === 1) return "明天到期"
  
    if (days <= 7) return `${days} 天后到期`
  
    
  
    return date.toLocaleDateString()
  
  }
  
  
  
  getPriorityColor(priority) {
  
    switch (priority) {
  
      case 3: return $color("#FF3B30")  // 高优先级 - 红色
  
      case 2: return $color("#FF9500")  // 中优先级 - 橙色
  
      case 1: return $color("#FFCC00")  // 低优先级 - 黄色
  
      default: return $color("#34C759") // 无优先级 - 绿色
  
    }
  
  }
  
  
  
  updateStatistics() {
  
    const total = this.tasks.length
  
    const completed = this.tasks.filter(t => t.completed).length
  
    const overdue = this.getOverdueTasks().length
  
    
  
    $("statsLabel").text = `总计: ${total} | 完成: ${completed} | 过期: ${overdue}`
  
  }
  
}
  

  
// UI 渲染
  
function renderTaskManagerUI() {
  
  const taskManager = new TaskManager()
  
  
  
  $ui.render({
  
    props: {
  
      title: "任务管理"
  
    },
  
    views: [
  
      {
  
        type: "button",
  
        props: {
  
          id: "addButton",
  
          title: "添加任务",
  
          bgcolor: $color("#007AFF")
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo(view.super.safeAreaTop).offset(10)
  
          make.left.right.inset(15)
  
          make.height.equalTo(44)
  
        },
  
        events: {
  
          tapped: function() {
  
            showAddTaskDialog(taskManager)
  
          }
  
        }
  
      },
  
      {
  
        type: "label",
  
        props: {
  
          id: "statsLabel",
  
          font: $font(14),
  
          textColor: $color("#666666"),
  
          align: $align.center
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo($("addButton").bottom).offset(10)
  
          make.left.right.inset(15)
  
          make.height.equalTo(20)
  
        }
  
      },
  
      {
  
        type: "list",
  
        props: {
  
          id: "taskList",
  
          rowHeight: 80,
  
          template: {
  
            views: [
  
              {
  
                type: "switch",
  
                props: {
  
                  id: "completed",
  
                  onColor: $color("#34C759")
  
                },
  
                layout: function(make, view) {
  
                  make.left.equalTo(15)
  
                  make.centerY.equalTo(view.super)
  
                },
  
                events: {
  
                  changed: function(sender) {
  
                    const data = sender.info
  
                    taskManager.updateTask(data.id, { completed: sender.on })
  
                  }
  
                }
  
              },
  
              {
  
                type: "view",
  
                props: {
  
                  id: "priority"
  
                },
  
                layout: function(make, view) {
  
                  make.left.equalTo($("completed").right).offset(10)
  
                  make.centerY.equalTo(view.super)
  
                  make.size.equalTo($size(4, 40))
  
                }
  
              },
  
              {
  
                type: "label",
  
                props: {
  
                  id: "title",
  
                  font: $font("bold", 16)
  
                },
  
                layout: function(make, view) {
  
                  make.left.equalTo($("priority").right).offset(10)
  
                  make.top.equalTo(15)
  
                  make.right.inset(15)
  
                }
  
              },
  
              {
  
                type: "label",
  
                props: {
  
                  id: "subtitle",
  
                  font: $font(14),
  
                  textColor: $color("#666666")
  
                },
  
                layout: function(make, view) {
  
                  make.left.equalTo($("title"))
  
                  make.bottom.equalTo(-15)
  
                  make.right.inset(15)
  
                }
  
              }
  
            ]
  
          }
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo($("statsLabel").bottom).offset(10)
  
          make.left.right.bottom.equalTo(view.super)
  
        },
  
        events: {
  
          didSelect: function(sender, indexPath, data) {
  
            showTaskDetail(taskManager, data.id)
  
          },
  
          didLongPress: function(sender, indexPath, data) {
  
            showTaskOptions(taskManager, data.id)
  
          }
  
        }
  
      }
  
    ]
  
  })
  
  
  
  taskManager.notifyUpdate()
  
}
  

  
// 添加任务对话框
  
function showAddTaskDialog(taskManager) {
  
  $input.text({
  
    type: $kbType.default,
  
    placeholder: "任务标题",
  
    handler: function(title) {
  
      if (!title) return
  
      
  
      // 选择分类
  
      $ui.menu({
  
        items: taskManager.categories,
  
        handler: function(title, idx) {
  
          const category = taskManager.categories[idx]
  
          
  
          // 选择优先级
  
          $ui.menu({
  
            items: ["无", "低", "中", "高"],
  
            handler: function(title, idx) {
  
              const task = {
  
                title: title,
  
                category: category,
  
                priority: idx
  
              }
  
              
  
              taskManager.addTask(task)
  
              $ui.toast("任务已添加")
  
            }
  
          })
  
        }
  
      })
  
    }
  
  })
  
}
  

  
// 启动应用
  
renderTaskManagerUI()
  

11.2 项目二:天气预报应用

创建一个天气预报应用,展示 API 集成和数据可视化:

  
// 天气应用主类
  
class WeatherApp {
  
  constructor() {
  
    this.apiKey = "your_api_key_here"
  
    this.baseURL = "https://api.openweathermap.org/data/2.5"
  
    this.cities = this.loadCities()
  
    this.currentCity = this.cities[0] || { name: "北京", lat: 39.9042, lon: 116.4074 }
  
    this.weatherData = null
  
    this.forecastData = null
  
  }
  
  
  
  loadCities() {
  
    const saved = $cache.get("weather_cities")
  
    return saved || [
  
      { name: "北京", lat: 39.9042, lon: 116.4074 },
  
      { name: "上海", lat: 31.2304, lon: 121.4737 },
  
      { name: "广州", lat: 23.1291, lon: 113.2644 }
  
    ]
  
  }
  
  
  
  saveCities() {
  
    $cache.set("weather_cities", this.cities)
  
  }
  
  
  
  async fetchWeather(city) {
  
    try {
  
      // 获取当前天气
  
      const currentWeather = await this.apiRequest("/weather", {
  
        lat: city.lat,
  
        lon: city.lon,
  
        units: "metric",
  
        lang: "zh_cn"
  
      })
  
      
  
      // 获取天气预报
  
      const forecast = await this.apiRequest("/forecast", {
  
        lat: city.lat,
  
        lon: city.lon,
  
        units: "metric",
  
        lang: "zh_cn",
  
        cnt: 40  // 5天预报
  
      })
  
      
  
      this.weatherData = this.processWeatherData(currentWeather)
  
      this.forecastData = this.processForecastData(forecast)
  
      
  
      return true
  
    } catch (error) {
  
      console.error("获取天气失败:", error)
  
      $ui.alert({
  
        title: "错误",
  
        message: "无法获取天气数据,请检查网络连接"
  
      })
  
      return false
  
    }
  
  }
  
  
  
  async apiRequest(endpoint, params) {
  
    return new Promise((resolve, reject) => {
  
      const url = `${this.baseURL}${endpoint}`
  
      const fullParams = { ...params, appid: this.apiKey }
  
      
  
      $http.get({
  
        url: url,
  
        parameters: fullParams,
  
        handler: function(resp) {
  
          if (resp.error) {
  
            reject(resp.error)
  
          } else if (resp.data.cod !== 200) {
  
            reject(new Error(resp.data.message))
  
          } else {
  
            resolve(resp.data)
  
          }
  
        }
  
      })
  
    })
  
  }
  
  
  
  processWeatherData(data) {
  
    return {
  
      city: data.name,
  
      country: data.sys.country,
  
      temp: Math.round(data.main.temp),
  
      feelsLike: Math.round(data.main.feels_like),
  
      tempMin: Math.round(data.main.temp_min),
  
      tempMax: Math.round(data.main.temp_max),
  
      pressure: data.main.pressure,
  
      humidity: data.main.humidity,
  
      weather: data.weather[0].description,
  
      icon: data.weather[0].icon,
  
      windSpeed: data.wind.speed,
  
      windDeg: data.wind.deg,
  
      clouds: data.clouds.all,
  
      visibility: data.visibility,
  
      sunrise: new Date(data.sys.sunrise * 1000),
  
      sunset: new Date(data.sys.sunset * 1000),
  
      dt: new Date(data.dt * 1000)
  
    }
  
  }
  
  
  
  processForecastData(data) {
  
    const dailyData = {}
  
    
  
    data.list.forEach(item => {
  
      const date = new Date(item.dt * 1000)
  
      const day = date.toLocaleDateString()
  
      
  
      if (!dailyData[day]) {
  
        dailyData[day] = {
  
          date: date,
  
          temps: [],
  
          weather: [],
  
          humidity: [],
  
          wind: []
  
        }
  
      }
  
      
  
      dailyData[day].temps.push(item.main.temp)
  
      dailyData[day].weather.push(item.weather[0])
  
      dailyData[day].humidity.push(item.main.humidity)
  
      dailyData[day].wind.push(item.wind.speed)
  
    })
  
    
  
    return Object.entries(dailyData).map(([day, data]) => ({
  
      date: data.date,
  
      tempMin: Math.round(Math.min(...data.temps)),
  
      tempMax: Math.round(Math.max(...data.temps)),
  
      weather: this.getMostFrequent(data.weather, 'main'),
  
      icon: this.getMostFrequent(data.weather, 'icon'),
  
      humidity: Math.round(data.humidity.reduce((a, b) => a + b) / data.humidity.length),
  
      windSpeed: Math.round(data.wind.reduce((a, b) => a + b) / data.wind.length * 10) / 10
  
    })).slice(0, 5)  // 只保留5天
  
  }
  
  
  
  getMostFrequent(arr, key) {
  
    const frequency = {}
  
    let maxCount = 0
  
    let mostFrequent = null
  
    
  
    arr.forEach(item => {
  
      const value = item[key]
  
      frequency[value] = (frequency[value] || 0) + 1
  
      if (frequency[value] > maxCount) {
  
        maxCount = frequency[value]
  
        mostFrequent = value
  
      }
  
    })
  
    
  
    return mostFrequent
  
  }
  
  
  
  getWeatherIcon(iconCode) {
  
    const iconMap = {
  
      "01d": "☀️", "01n": "🌙",
  
      "02d": "⛅", "02n": "☁️",
  
      "03d": "☁️", "03n": "☁️",
  
      "04d": "☁️", "04n": "☁️",
  
      "09d": "🌧️", "09n": "🌧️",
  
      "10d": "🌦️", "10n": "🌧️",
  
      "11d": "⛈️", "11n": "⛈️",
  
      "13d": "🌨️", "13n": "🌨️",
  
      "50d": "🌫️", "50n": "🌫️"
  
    }
  
    return iconMap[iconCode] || "❓"
  
  }
  
  
  
  getWindDirection(deg) {
  
    const directions = ["北", "东北", "东", "东南", "南", "西南", "西", "西北"]
  
    const index = Math.round(deg / 45) % 8
  
    return directions[index]
  
  }
  
  
  
  formatTime(date) {
  
    return date.toLocaleTimeString('zh-CN', { 
  
      hour: '2-digit', 
  
      minute: '2-digit' 
  
    })
  
  }
  
}
  

  
// 渲染天气界面
  
function renderWeatherUI() {
  
  const app = new WeatherApp()
  
  
  
  $ui.render({
  
    props: {
  
      title: "天气预报",
  
      bgcolor: $color("#F0F0F0")
  
    },
  
    views: [
  
      // 城市选择器
  
      {
  
        type: "button",
  
        props: {
  
          id: "citySelector",
  
          title: app.currentCity.name,
  
          bgcolor: $color("clear"),
  
          titleColor: $color("#007AFF"),
  
          font: $font("bold", 18)
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo(view.super.safeAreaTop).offset(10)
  
          make.centerX.equalTo(view.super)
  
        },
  
        events: {
  
          tapped: function() {
  
            showCitySelector(app)
  
          }
  
        }
  
      },
  
      
  
      // 当前天气容器
  
      {
  
        type: "view",
  
        props: {
  
          id: "currentWeather",
  
          bgcolor: $color("white"),
  
          cornerRadius: 15,
  
          shadow: {
  
            color: $color("#000000", 0.1),
  
            offset: $size(0, 2),
  
            radius: 10
  
          }
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo($("citySelector").bottom).offset(20)
  
          make.left.right.inset(15)
  
          make.height.equalTo(200)
  
        },
  
        views: [
  
          {
  
            type: "label",
  
            props: {
  
              id: "tempLabel",
  
              font: $font("bold", 60),
  
              align: $align.center
  
            },
  
            layout: function(make, view) {
  
              make.centerX.equalTo(view.super)
  
              make.top.equalTo(20)
  
            }
  
          },
  
          {
  
            type: "label",
  
            props: {
  
              id: "weatherLabel",
  
              font: $font(20),
  
              align: $align.center,
  
              textColor: $color("#666666")
  
            },
  
            layout: function(make, view) {
  
              make.centerX.equalTo(view.super)
  
              make.top.equalTo($("tempLabel").bottom).offset(10)
  
            }
  
          },
  
          {
  
            type: "label",
  
            props: {
  
              id: "detailsLabel",
  
              font: $font(14),
  
              align: $align.center,
  
              lines: 0,
  
              textColor: $color("#999999")
  
            },
  
            layout: function(make, view) {
  
              make.left.right.inset(20)
  
              make.bottom.inset(20)
  
            }
  
          }
  
        ]
  
      },
  
      
  
      // 预报列表
  
      {
  
        type: "list",
  
        props: {
  
          id: "forecastList",
  
          rowHeight: 70,
  
          bgcolor: $color("clear"),
  
          separatorHidden: true,
  
          template: {
  
            props: {
  
              bgcolor: $color("white"),
  
              cornerRadius: 10
  
            },
  
            views: [
  
              {
  
                type: "label",
  
                props: {
  
                  id: "dateLabel",
  
                  font: $font("bold", 16)
  
                },
  
                layout: function(make, view) {
  
                  make.left.equalTo(15)
  
                  make.centerY.equalTo(view.super)
  
                }
  
              },
  
              {
  
                type: "label",
  
                props: {
  
                  id: "iconLabel",
  
                  font: $font(30),
  
                  align: $align.center
  
                },
  
                layout: function(make, view) {
  
                  make.center.equalTo(view.super)
  
                }
  
              },
  
              {
  
                type: "label",
  
                props: {
  
                  id: "tempRangeLabel",
  
                  font: $font(16),
  
                  align: $align.right
  
                },
  
                layout: function(make, view) {
  
                  make.right.inset(15)
  
                  make.centerY.equalTo(view.super)
  
                }
  
              }
  
            ]
  
          }
  
        },
  
        layout: function(make, view) {
  
          make.top.equalTo($("currentWeather").bottom).offset(20)
  
          make.left.right.inset(15)
  
          make.bottom.equalTo(view.super.safeAreaBottom)
  
        },
  
        events: {
  
          didSelect: function(sender, indexPath, data) {
  
            showForecastDetail(app, data)
  
          }
  
        }
  
      }
  
    ]
  
  })
  
  
  
  // 加载天气数据
  
  loadWeatherData(app)
  
}
  

  
// 加载天气数据
  
async function loadWeatherData(app) {
  
  $ui.loading(true)
  
  
  
  const success = await app.fetchWeather(app.currentCity)
  
  
  
  $ui.loading(false)
  
  
  
  if (success) {
  
    updateWeatherUI(app)
  
  }
  
}
  

  
// 更新UI
  
function updateWeatherUI(app) {
  
  const weather = app.weatherData
  
  const forecast = app.forecastData
  
  
  
  // 更新当前天气
  
  $("tempLabel").text = `${weather.temp}°`
  
  $("weatherLabel").text = `${app.getWeatherIcon(weather.icon)} ${weather.weather}`
  
  $("detailsLabel").text = `体感 ${weather.feelsLike}° | 湿度 ${weather.humidity}% | ${app.getWindDirection(weather.windDeg)}风 ${weather.windSpeed}m/s\n日出 ${app.formatTime(weather.sunrise)} | 日落 ${app.formatTime(weather.sunset)}`
  
  
  
  // 更新预报列表
  
  $("forecastList").data = forecast.map(day => ({
  
    dateLabel: {
  
      text: day.date.toLocaleDateString('zh-CN', { weekday: 'short', month: 'numeric', day: 'numeric' })
  
    },
  
    iconLabel: {
  
      text: app.getWeatherIcon(day.icon)
  
    },
  
    tempRangeLabel: {
  
      text: `${day.tempMin}° / ${day.tempMax}°`
  
    },
  
    forecast: day  // 保存完整数据
  
  }))
  
}
  

  
// 启动应用
  
renderWeatherUI()
  

第十二章:性能优化与最佳实践

12.1 代码优化技巧

模块化和代码组织

  
// 使用模块模式组织代码
  
const AppModules = {
  
  // 工具模块
  
  Utils: {
  
    formatDate(date, format = 'YYYY-MM-DD') {
  
      const year = date.getFullYear()
  
      const month = String(date.getMonth() + 1).padStart(2, '0')
  
      const day = String(date.getDate()).padStart(2, '0')
  
      
  
      return format
  
        .replace('YYYY', year)
  
        .replace('MM', month)
  
        .replace('DD', day)
  
    },
  
    
  
    debounce(func, wait) {
  
      let timeout
  
      return function executedFunction(...args) {
  
        const later = () => {
  
          clearTimeout(timeout)
  
          func(...args)
  
        }
  
        clearTimeout(timeout)
  
        timeout = setTimeout(later, wait)
  
      }
  
    },
  
    
  
    throttle(func, limit) {
  
      let inThrottle
  
      return function(...args) {
  
        if (!inThrottle) {
  
          func.apply(this, args)
  
          inThrottle = true
  
          setTimeout(() => inThrottle = false, limit)
  
        }
  
      }
  
    },
  
    
  
    memoize(fn) {
  
      const cache = new Map()
  
      return function(...args) {
  
        const key = JSON.stringify(args)
  
        if (cache.has(key)) {
  
          return cache.get(key)
  
        }
  
        const result = fn.apply(this, args)
  
        cache.set(key, result)
  
        return result
  
      }
  
    }
  
  },
  
  
  
  // 性能监控模块
  
  Performance: {
  
    markers: new Map(),
  
    
  
    mark(name) {
  
      this.markers.set(name, Date.now())
  
    },
  
    
  
    measure(name, startMark, endMark) {
  
      const start = this.markers.get(startMark)
  
      const end = endMark ? this.markers.get(endMark) : Date.now()
  
      
  
      if (!start) {
  
        console.warn(`Performance mark '${startMark}' not found`)
  
        return
  
      }
  
      
  
      const duration = end - start
  
      console.log(`${name}: ${duration}ms`)
  
      
  
      return duration
  
    },
  
    
  
    profile(name, fn) {
  
      const start = Date.now()
  
      const result = fn()
  
      const duration = Date.now() - start
  
      
  
      console.log(`${name} took ${duration}ms`)
  
      
  
      if (duration > 100) {
  
        console.warn(`${name} is taking too long!`)
  
      }
  
      
  
      return result
  
    }
  
  },
  
  
  
  // 资源管理模块
  
  ResourceManager: {
  
    resources: new Map(),
  
    maxSize: 50 * 1024 * 1024, // 50MB
  
    currentSize: 0,
  
    
  
    add(key, resource, size) {
  
      // 实现 LRU 缓存
  
      if (this.resources.has(key)) {
  
        this.remove(key)
  
      }
  
      
  
      while (this.currentSize + size > this.maxSize && this.resources.size > 0) {
  
        const firstKey = this.resources.keys().next().value
  
        this.remove(firstKey)
  
      }
  
      
  
      this.resources.set(key, {
  
        data: resource,
  
        size: size,
  
        lastAccess: Date.now()
  
      })
  
      
  
      this.currentSize += size
  
    },
  
    
  
    get(key) {
  
      const resource = this.resources.get(key)
  
      if (resource) {
  
        resource.lastAccess = Date.now()
  
        // 移到末尾(LRU)
  
        this.resources.delete(key)
  
        this.resources.set(key, resource)
  
        return resource.data
  
      }
  
      return null
  
    },
  
    
  
    remove(key) {
  
      const resource = this.resources.get(key)
  
      if (resource) {
  
        this.currentSize -= resource.size
  
        this.resources.delete(key)
  
      }
  
    },
  
    
  
    clear() {
  
      this.resources.clear()
  
      this.currentSize = 0
  
    },
  
    
  
    getStats() {
  
      return {
  
        itemCount: this.resources.size,
  
        totalSize: this.currentSize,
  
        maxSize: this.maxSize,
  
        usage: (this.currentSize / this.maxSize * 100).toFixed(2) + '%'
  
      }
  
    }
  
  }
  
}
  

  
// 使用示例
  
const debouncedSearch = AppModules.Utils.debounce((query) => {
  
  performSearch(query)
  
}, 300)
  

  
const memoizedCalculation = AppModules.Utils.memoize((n) => {
  
  // 复杂计算
  
  return fibonacci(n)
  
})
  

12.2 内存管理最佳实践

  
// 对象池模式 - 减少内存分配
  
class ObjectPool {
  
  constructor(createFn, resetFn, maxSize = 100) {
  
    this.createFn = createFn
  
    this.resetFn = resetFn
  
    this.maxSize = maxSize
  
    this.pool = []
  
    this.activeObjects = new Set()
  
  }
  
  
  
  acquire() {
  
    let obj
  
    if (this.pool.length > 0) {
  
      obj = this.pool.pop()
  
    } else {
  
      obj = this.createFn()
  
    }
  
    
  
    this.activeObjects.add(obj)
  
    return obj
  
  }
  
  
  
  release(obj) {
  
    if (!this.activeObjects.has(obj)) {
  
      return
  
    }
  
    
  
    this.activeObjects.delete(obj)
  
    this.resetFn(obj)
  
    
  
    if (this.pool.length < this.maxSize) {
  
      this.pool.push(obj)
  
    }
  
  }
  
  
  
  clear() {
  
    this.pool = []
  
    this.activeObjects.clear()
  
  }
  
  
  
  getStats() {
  
    return {
  
      poolSize: this.pool.length,
  
      activeCount: this.activeObjects.size,
  
      totalCreated: this.pool.length + this.activeObjects.size
  
    }
  
  }
  
}
  

  
// 使用对象池
  
const particlePool = new ObjectPool(
  
  // 创建函数
  
  () => ({
  
    x: 0,
  
    y: 0,
  
    vx: 0,
  
    vy: 0,
  
    life: 1.0,
  
    color: null
  
  }),
  
  // 重置函数
  
  (particle) => {
  
    particle.x = 0
  
    particle.y = 0
  
    particle.vx = 0