>[danger] **弃用提醒:**
> *由于看云对于免费用户的限制愈发严苛,本文档已经迁移至语雀。本文档将不做维护。*
> **语雀地址**:[https://www.yuque.com/a632079/nodebb](https://www.yuque.com/a632079/nodebb)
*****
# 主题制作
## 导言
![](https://img.kancloud.cn/6b/eb/6beb209f07be655ca1e51eee68dd2946_2534x810.png)
NodeBB 作为一个比较成熟的 BBS 开源程序,当然具备了自定义主题的功能。NodeBB 采用 **Express** 作为其 Web 服务底层,模板引擎则采用自写的 **benchpressjs** (还单独写了 rust 实现的编译加速版),性能十分出色。
和 DiscuzX 类似, NodeBB 采用了一种替换式的方案实现了主题的更换。换句话说, 你只需要在自己的主题目录中重写与渲染路径对应的模板文件即可使渲染结果生效。
**在开始之前,你需要知道**: NodeBB 初版采用 Bootstrap 作为其前端 UI 基础,这也是当今 Persona 主题的前身。而对于 Bootstrap, 我们都应了解: 这是一个非常优秀的前端框架,但是其传染性也十分的强。通常,这会限制我们在主题中的自由发挥。虽然 NodeBB 支持主题使用其他的框架,但是我们**必须**实现 bootstrap 3 的栅格引擎以适配第三方插件的自带模板。
[TOC]
## 了解 NodeBB 主题的内在原理
NodeBB 主题其实就是一种特殊的插件。它具备插件的所有特性(有关插件的特性,请参考前一章节)。与插件最大的不同, 它具备了模板替换的特性。
NodeBB 将主题划分为两种: **原生主题** 和 **子主题**。
所谓**原生主题**就是如 **persona** 这种,完整实现了 NodeBB 路由所需的所有模板的主题。
而**子主题**必须是基于**原生主题**构建的,子主题可以只修改特定的模板文件,缺省其他文件,使 NodeBB 自动调用回滚机制,在渲染时调用原生主题的模板文件。我们将在稍后详细讲解子主题。
>[info] **子主题** 和 **原生主题** 之间的关系和子类和母类的关系很像。子主题继承了原生主题的所有,包括 scripts,静态资源等等。
### NodeBB 中主题识别的方式
和插件一样,NodeBB 为了鼓励主题的流通,使用 npm 作为其主题的存储媒介。这意味着大部分主题可以很简单得通过如下方式安装:
```
$ yarn add nodebb-theme-name
```
NodeBB 要求主题必须以 `nodebb-theme-`作为前缀,并且至少以下两个文件:
* `theme.json`
* `theme.less`
否则, 你的主题将不会被 NodeBB 识别。
`theme.less` 是你主题的入口样式。它是主题重要的样式文件。它通常由 less 编写, 你可以在这里使用任何的 less 语法。并且子主题可以修改父主题的 less 变量。
>[warning] 建议将`theme.less` 当成单纯的入口文件。将样式按模块拆分,再使用`@import`引入。而不是一股脑的全塞这儿。
### 配置 `theme.json`
和 `plugin.json` 类似, `theme.json`中包含主题的配置信息。以下是一个例子:
```
{
"id": "nodebb-theme-persona",
"name": "Persona",
"description": "The default theme for NodeBB. Uses a standard approach to forum design.",
"url": "https://github.com/psychobunny/nodebb-theme-persona",
"screenshot": "screenshot.png"
}
```
* `id` 主题的标识。通常建议以 `nodebb-theme-name` 的形式命名
* `name` 主题的名称。例如:“仿 MiUI 社区主题”
* `description` 主题的描述。简单讲解主题的作用,功能。
* `screenshot` 主题预览图片。值应是一个和`theme.json`同目录的文件名。图像最好比例为 1.48:1
* `url` 可以链接到主题的主页,介绍页。
* `templates` 该项可选,主题文件夹名。值应是一个相对路径的文件夹。默认为:“templates”
* `baseTheme` 该项可选,父主题标识。如果未定义,将以 `nodebb-theme-persona` 或者当前选择的主题作为父主题。更多细节请参考 子主题 一节。
## 子主题
### CSS / LESS
子主题可以直接合并原生主题的 CSS/LESS 资源。
当你指定主题 `nodebb-theme-persona` 作为子主题的父主题时,像 `topic.less` 这些 `persona` 中存在的 CSS/LESS 资源将自动合并到你的子主题中。
### 模板
和前文所述一样, 子主题将自动引入父主题的所有模板文件。所以,你无需定义所有的模板文件,只需要创建必要的主题文件。
>[success] 需要注意的是: `nodebb-theme-persona` 默认是所有主题的父主题。因此,你也可以在“原生主题”中享受自动继承的便利。
## 有关 **皮肤** 的说明
![](https://img.kancloud.cn/ec/1b/ec1b9edf8ed7f3c54b2b9f93cccb4002_1152x468.png)
NodeBB 在线丢人。 主题目前还不支持自定义皮肤哦!
## 模板引擎
### 原理
每个页面都有相对应的 API 接口,主题文件以及 i18n 文件。
例如,当你访问 `/topic/351/nodebb-wiki` 时,NodeBB会返回三个资源。API 返回 `/api/topic/351/nodebb-wiki` 相对应的模板文件(本例中为:`topic.tpl`),以及相对应的语言文件(本例中为:`topic.json`)
> 页面名称是和模板相对应的。 例如: `/topic/xyz` 对应 `topic.tpl`
你可以简单的再路由前加上 `/api` 前缀以查阅其 JSON 返回。这些数据便是模板处理的关键数据。
### 基础
如前节所说, NodeBB 渲染模板的实质是对 API 返回数据的渲染。因此,你可以在模板中使用任何能在 API 中得到的数据。
以 `/api/topic` 为例,你可以在模板中这样打印值:
```
{topic_name}
```
访问对象中的值:
```
{privileges.read}
```
并且你可以像这样遍历数组:
```
<!-- BEGIN posts -->
{posts.content}
<!-- END posts -->
```
这会基于数组 posts ,生成 X 个块。
### 流程控制
NodeBB 的模板系统实现了一些基本逻辑。继续我们刚才的例子,你可以这样控制流程:
```
<!-- IF unreplied -->
This thread is unreplied!
<!-- ENDIF unreplied -->
```
或者这样:
```
<!-- IF !disableSocialButtons -->
<button>Share on Facebook</button>
<!-- ELSE -->
Sharing has been disabled.
<!-- ENDIF !disableSocialButtons -->
```
我们可以像这样,检测数组的长度:
```
<!-- IF posts.length -->
There be some posts
<!-- ENDIF posts.length -->
```
当遍历数组时,我们可以使用 `@first` 和 `@last` 确定引索:
```
<!-- BEGIN posts -->
<!-- IF @first -->
<h1>Main Author: {posts.username}</h1>
<!-- ENDIF @first -->
{posts.content}
<!-- IF @last -->
End of posts. Click here to scroll to the top.
<!-- ENDIF @last -->
<!-- END posts -->
```
想要更深入得了解模板引擎?[https://github.com/benchpressjs/benchpressjs](https://github.com/benchpressjs/benchpressjs)
### 向客户端 JS 传递模板参数
除了 Socket.IO,我们还拥有两种方法能向客户端 JS 传递参数。
#### 通过 jQuery.get
如果我们需要获得其他页面的数据, 我们可以使用 `$.get` 请求 API 接口获得数据。例如,我们可以像这样获得一个用户的数据:
~~~
$.get(RELATIVE_PATH + '/api/user/psychobunny', {}, function(user) {
console.log(user)
});
~~~
API 返回的数据大致为这样:[https://community.nodebb.org/api/user/psychobunny](https://community.nodebb.org/api/user/psychobunny)
#### 通过模板参数
在 `topic.tpl` 中, 我们像这样添加了一个不可见(hidden)的输入框(input):
```
<input type="hidden" template-variable="pageCount" value="{pageCount}" />
```
而在 JS 中,我们只需这样,即可获得值:
```
ajaxify.variables.get('pageCount');
```
这是让 JS 知道模板中重要变量的理想方式。
### 国际化
模板引擎被设计为和国际化系统相耦合,变量可以嵌入语言字符串中。让我们以[该 API 请求](https://community.nodebb.org/api/register) 和 [语言文件](https://community.nodebb.org/assets/language/zh-CN/register.json) 为例:
```
[[register:help.username_restrictions, {minimumUsernameLength}, {maximumUsernameLength}]]
```
他将会翻译文本:
```
A unique username between %1 and %2 characters
```
成
```
全局唯一的用户名,长度 2 到 12 个字。
```
### 进阶
#### 从客户端动态获取并渲染模板
模板引擎根据需要延迟加载模板并缓存它们。如果你的代码需要按需加载模板(或是部分按需),可以这样加载:
```
ajaxify.loadTemplate('myTemplate', function(myTemplate) { // 获得模板内容
const html = templates.parse(myTemplate, myData) // 渲染成 HTML
})
```
你还可以访问模板中独立的块。这在更新`<ul>`块中的`<li>`部分时,十分有用。
```html
Some stuff here...
<!-- BEGIN posts -->
We just want to pull this block only.
<!-- END posts -->
... some stuff here
```
```javascript
ajaxify.loadTemplate('myTemplate', function(myTemplate) {
const block = templates.getBlock(myTemplate, 'posts')
const html = templates.parse(block, myData)
})
```
#### 在服务端渲染模板
与大多数模板引擎一样,服务端中模板引擎与 Express 深度耦合。 只需要使用 `app.render` 或 `res.render` 即可渲染模板。
```
res.render('myTemplate', myData)
app.render('myTemplate', myData, function(err, parsedTemplate) {
console.log(parsedTemplate)
});
```
>[info] `res.render` 会将渲染的结果直接发送给浏览器。而 `app.render` 则是渲染后将结果传递给回调方法。
## 使用工具包快速开始
NodeBB 官方提供了一个[主题工具包](https://github.com/nodebb/nodebb-theme-quickstart)方便快速开始一个主题。这个工具包由于是基于ES5,回调风格编写的,所以我们不推荐使用。
我们在保持与官方工具包风格大致一致的情况下,与插件一样,我们也提供了一个主题工具包:[https://github.com/nodebb-china/nodebb-theme-quickstart](https://github.com/nodebb-china/nodebb-theme-quickstart)。
接下来的涉及工具包的内容,我们将使用我们自己的工具包作为例子讲解。
嗯,为了方便开发,和官方工具包一样,我们的主题工具包是一个基于 **主题:Persona** 的子主题,它会自动从 **主题:Persona** 补全所缺省的模板文件。
当然,我们也可以完整实现一个 NodeBB 主题。只需要简单克隆 [Persona 仓库](https://github.com/nodebb/nodebb-theme-persona
) ,修改相关信息, 对照结构完成主题就好了。是不是很简单呢?
更多相关工具包的使用说明,我们将在稍后讲解。
>[info] 编写: a632079
维护: PA Team
审核: PA Team
最后更新: 2019.12.09
- 序
- 赞助
- 导言
- 安装
- 通过操作系统
- Windows + Mongodb/Redis
- Ubuntu/Debian + Redis/Mongodb
- CentOS + Redis
- CentOS + Mongodb
- FreeBSD/OpenBSD + Redis
- Arch Linux + Redis
- OSX + Redis
- 通过云服务
- 通过主机面板安装
- AppNode
- CPanel
- 宝塔
- 使用
- FAQ
- 高级
- 运行 NodeBB
- 配置 Config.json
- 配置 Nginx
- 配置 MongoDB
- 更新 NodeBB
- 设置 Widgets
- 安装 Yarn
- 更新 MongoDB
- 数据库备份与恢复
- 重置管理员密码
- 让 NodeBB 支持搜索
- 优化
- 优化配置,提升NodeBB处理能力
- Google字体库 -> 360公共前端库
- Google字体库 -> 中科大镜像
- 海外VPS提升NodeBB访问速度
- 通过 NodeBB API 自动发帖
- 开发
- 准备
- 常用方法 & 变量
- 插件制作
- 使用工具包编写一个插件
- 主题制作
- 使用工具包编写一个主题
- 部件制作
- 国际化
- 钩子(hook)使用说明