# MeNav Handlebars 助手函数说明文档

## 目录

- [助手函数概述](#助手函数概述)
- [助手函数分类](#助手函数分类)
  - [格式化函数](#格式化函数)
  - [条件判断函数](#条件判断函数)
  - [工具函数](#工具函数)
  - [核心函数](#核心函数)
- [使用方法](#使用方法)
- [函数详解](#函数详解)
- [扩展指南](#扩展指南)
- [最佳实践](#最佳实践)

## 助手函数概述

MeNav 项目使用 Handlebars 助手函数扩展模板功能，使模板更加强大和灵活。助手函数可用于：

- 数据格式化（日期、文本等）
- 条件判断和逻辑控制
- 数组与对象操作
- HTML 安全处理

所有助手函数都在 `src/helpers/` 目录下定义，并通过 `src/helpers/index.js` 统一注册到 Handlebars 实例。

## 助手函数分类

MeNav 的助手函数分为四类：

### 格式化函数

位置：`src/helpers/formatters.js`

提供各种数据格式化功能，包括：

- 日期格式化
- 文本长度限制
- 大小写转换
- 调试数据显示

### 条件判断函数

位置：`src/helpers/conditions.js`

提供条件判断与逻辑操作功能，包括：

- 相等与不等判断
- 通用比较操作
- 空值检查
- 逻辑运算（与、或、非）

#### ifHttpUrl

判断字符串是否为 http/https URL，用于在模板中分支渲染仅对外链生效的逻辑（如 favicon 加载）：

```handlebars
{{#ifHttpUrl url}}
  {{! 只有 http/https 才尝试加载 favicon }}
  <img
    src='https://t3.gstatic.com/faviconV2?url={{encodeURIComponent url}}&size=32&drop_404_icon=true'
    alt='{{name}} favicon'
  />
{{else}}
  <i class='fas fa-link'></i>
{{/ifHttpUrl}}
```

### 工具函数

位置：`src/helpers/utils.js`

提供各种实用工具功能，包括：

- 数组与字符串操作
- 集合长度计算
- 范围数组生成
- 对象属性选择

#### encodeURIComponent

对字符串进行 URL 组件编码（同名于浏览器 API，用作模板内联助手），适用于将动态 URL 参数安全拼接到查询串：

```handlebars
{{! 构造第三方 Favicon API 的 url 参数 }}
<img
  src='https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&url={{encodeURIComponent
    url
  }}&size=32&drop_404_icon=true'
  alt='favicon'
/>
```

### 核心函数

位置：`src/helpers/index.js`

提供基础的 HTML 处理功能：

- HTML 转义
- 安全输出 HTML

## 使用方法

在 Handlebars 模板中使用助手函数有多种方式：

### 1. 内联表达式

用于生成内容的助手函数：

```handlebars
{{formatDate created 'YYYY-MM-DD'}}
{{limit description 100}}
{{json data}}
```

### 2. 块级表达式

用于控制结构的助手函数：

```handlebars
{{#ifEquals type 'article'}}
  <span class='badge'>文章</span>
{{else}}
  <span class='badge'>页面</span>
{{/ifEquals}}

{{#each (range 1 5)}}
  <span>{{this}}</span>
{{/each}}
```

### 3. 助手函数组合

多个助手函数可以组合使用：

```handlebars
{{#each (slice items 0 5)}}
  <li>{{toUpperCase name}}</li>
{{/each}}
```

## 函数详解

### 格式化函数

#### formatDate

格式化日期：

```handlebars
{{formatDate date 'YYYY-MM-DD'}}
{{! 2023-05-15 }}
{{formatDate date 'YYYY年MM月DD日'}}
{{! 2023年05月15日 }}
{{formatDate date 'YYYY-MM-DD HH:mm:ss'}}
{{! 2023-05-15 14:30:00 }}
```

支持的格式：

- `YYYY`: 四位年份
- `MM`: 两位月份
- `DD`: 两位日期
- `HH`: 两位小时（24小时制）
- `mm`: 两位分钟
- `ss`: 两位秒数

#### limit

限制文本长度，超出部分显示省略号：

```handlebars
{{limit '这是一段很长的文本内容' 5}} {{! 这是一段... }}
```

#### toLowerCase / toUpperCase

转换文本大小写：

```handlebars
{{toLowerCase 'Hello'}}
{{! hello }}
{{toUpperCase 'world'}}
{{! WORLD }}
```

#### json

将对象转换为 JSON 字符串（用于调试）：

```handlebars
{{json this}}
```

#### extractDomain

从 URL 中提取“干净的域名”（不包含协议、路径与查询串），常用于站点描述兜底显示：

```handlebars
{{extractDomain url}}
```

### 条件判断函数

#### ifEquals / ifNotEquals

比较两个值是否相等/不相等：

```handlebars
{{#ifEquals status 'active'}}
  当前状态：活跃
{{else}}
  当前状态：非活跃
{{/ifEquals}}
```

#### ifCond

通用条件比较：

```handlebars
{{#ifCond count '>' 0}}
  有
  {{count}}
  个项目
{{else}}
  没有项目
{{/ifCond}}
```

支持的运算符：

- `==`, `===`, `!=`, `!==`
- `<`, `<=`, `>`, `>=`
- `&&`, `||`

#### isEmpty / isNotEmpty

检查值是否为空：

```handlebars
{{#isEmpty items}}
  <p>暂无数据</p>
{{else}}
  <ul>
    {{#each items}}
      <li>{{this}}</li>
    {{/each}}
  </ul>
{{/isEmpty}}
```

#### and / or / not

逻辑操作：

```handlebars
{{#and isPremium isActive}}
  高级活跃用户
{{/and}}

{{#or isPremium isAdmin}}
  有访问权限
{{/or}}

{{#not isDisabled}}
  此功能可用
{{/not}}
```

### 工具函数

#### slice

数组或字符串切片：

```handlebars
{{#each (slice array 0 3)}}
  <li>{{this}}</li>
{{/each}}
```

#### concat

合并数组：

```handlebars
{{#each (concat array1 array2)}}
  <li>{{this}}</li>
{{/each}}
```

#### size

获取数组、字符串或对象的长度/大小：

```handlebars
总共 {{size items}} 个项目
```

#### first / last

获取数组的第一个/最后一个元素：

```handlebars
第一项:
{{first items}}
最后一项:
{{last items}}
```

#### range

创建一个连续范围的数组：

```handlebars
{{#each (range 1 5)}}
  <span>{{this}}</span>
{{/each}}
```

#### pick

从对象中选择指定的属性：

```handlebars
{{json (pick user 'name' 'email')}}
```

#### keys

将对象的所有键转换为数组：

```handlebars
{{#each (keys object)}}
  <li>{{this}}</li>
{{/each}}
```

#### encodeURIComponent

对字符串做 URL 组件编码，常用于拼接第三方请求参数（例如 favicon 的 `url=` 参数）：

```handlebars
{{encodeURIComponent url}}
```

#### add

数字加法，用于根据层级动态计算标题级别等场景：

```handlebars
<h{{add level 1}}>...</h{{add level 1}}>
```

### 核心函数

#### escapeHtml

转义 HTML 特殊字符：

```handlebars
{{escapeHtml content}}
```

#### safeHtml

安全输出 HTML（不转义）：

```handlebars
{{safeHtml htmlContent}}
```

## 扩展指南

### 添加新的助手函数

1. 选择适当的分类文件（`formatters.js`、`conditions.js` 或 `utils.js`）
2. 添加新的函数并导出
3. 函数会自动通过 `index.js` 中的 `registerAllHelpers` 注册

示例：添加一个新的格式化函数到 `formatters.js`：

```javascript
/**
 * 将数字格式化为带千位分隔符的字符串
 * @param {number} number 要格式化的数字
 * @returns {string} 格式化后的字符串
 * @example {{formatNumber 1000000}} -> 1,000,000
 */
function formatNumber(number) {
  if (typeof number !== 'number') return '';
  return number.toLocaleString();
}

// 在导出中添加新函数
module.exports = {
  formatDate,
  limit,
  toLowerCase,
  toUpperCase,
  json,
  formatNumber, // 添加新函数
};
```

### 添加新的分类

如果需要添加新的分类：

1. 在 `src/helpers/` 创建新的 JS 文件
2. 在 `index.js` 中导入并注册新的助手函数集

```javascript
const newHelpers = require('./new-helpers');

function registerAllHelpers(handlebars) {
  // 现有注册代码...

  // 注册新的助手函数
  Object.entries(newHelpers).forEach(([name, helper]) => {
    handlebars.registerHelper(name, helper);
  });
}
```

## 最佳实践

1. **文档化函数** - 使用 JSDoc 风格为所有函数添加文档注释
   - 描述函数功能
   - 列出参数和返回值
   - 提供使用示例

2. **参数校验** - 增加参数类型和有效性检查
   - 检查必要参数是否存在
   - 验证参数类型
   - 为无效输入提供默认值或空结果

3. **命名规范**
   - 使用描述性名称，清晰表达函数用途
   - 遵循现有命名风格（如 `kebab-case`）
   - 保持命名一致性（如条件判断函数以 `is` 或 `if` 开头）

4. **避免副作用** - 助手函数应为纯函数，不修改传入的数据

5. **保持简单** - 每个助手函数应只完成一个明确的任务
