# 如何开发 Pagekit 主题
<p class="uk-article-lead">在本教程中,你将学习如何从我们的 Hello Theme 蓝图开始开发你自己的主题。你会了解到主题的结构并遵循必要的步骤来添加新的页面元素位置和主题选项。</p>
**Note** 本示例的[最终完成主题](https://github.com/pagekit/example-theme)可以在 Github 上找到。
## 起步
针对本教程,我们假设你已经有一个在本地服务器环境中运行的 Pagekit 站点。如果没有,请先[下载](https://pagekit.com/download)一个 Pagekit 安装包,并快速[安装](223143)它。然后登入到管理界面中商店里的主题这部分。
在 Pagekit 商店有一个 [Hello Theme](https://pagekit.com/marketplace/package/pagekit/theme-hello),它是用来开发主题的蓝图,包含代码示例和通用的基础代码来帮助你起步。
![](image/tutorial-theme-hello-plain.png)
Hello 主题没有提供任何样式,但给了你开发你自己的主题的一个起点。
首先,从 Pagekit 商店安装 Hello 主题并大致看一下它的文件结构。安装完成后,*Hello* 主题位于 `packages/pagekit/theme-hello`目录。如果你要开发自己的主题,建议你再安装一个简单的主题作为参考,比如默认的 [Theme One](https://pagekit.com/marketplace/package/pagekit/theme-one)。这样你可以比较结构元素和获得一些灵感。对于本教程,我们只需要 Hello 主题。
## 结构
我们来看一下在开发主题时要处理的主要文件和目录。
```txt
/css
theme.css // 带有预定义 class 的 CSS 骨架
/js
theme.js // 存储JS脚本的空文件
/views
template.php // 渲染主题的输出;默认情况下logo、菜单、主要内容、边栏和脚注位置都是可用的。
composer.json // 让Pagekit 识别主题的包定义文件
image.jpg // 预览截图
index.php // 主题的核心配置文件
CHANGELOG.md // 主题的修改记录
README.md // 包含基础的说明信息
```
### 主题重新命名
如果只是简单地修改了 Hello 主题的文件,这个主题在商店里任何更新都会覆盖掉你的修改。同时,你可能想为主题取一个名字。如果你决定将主题上传到商店,你需要为它取一个独一无二的名字。这些事,只需要简单的三步:
1. 从 `packages/pagekit/theme-hello` 将所有文件复制到 `packages/your-name/your-theme` (你得先创建这些目录).
2. 打开 `composer.json` ,并用`"name": "your-name/your-theme"`替换掉 `"name": "pagekit/theme-hello",`。并将 `"title": "Hello"` 改为 `"title": "Your theme"`。
3. 打开 `index.php` ,并用 `'name' => 'your-theme'` 替换 `'name' => 'theme-hello'`。
简单起见,本教程后续部分的例子里依然称之为 `theme-hello` 。
### CSS
Hello 主题没有包含任何样式。内含的 `css/theme.css` 文件列出了所有由 Pagekit 核心扩展程序渲染的不包含任何样式的 CSS 类名。你可以为这些 CSS 类名添加 CSS样式。
Pagekit 的管理界面和默认主题都是基于 [UIkit 前端框架](http://getuikit.com/)的。所以你也可以使用它来制作你的项目。在下面的例子中,我们就会使用 UIkit 框架来构建我们的主题。如果你使用的是其他框架或者干脆没有使用框架,其实CSS的基础方法都是类似的。
有许多方式来创建主题的文件结构。我们在这里推荐两种。其一是使用纯CSS的“经典”方式。其二是最初使用更复杂的设置,但拥有更大的灵活性。
#### 简单方式:引入纯 CSS 文件
前往 [UIkit 官网](http://getuikit.com/),下载最新版本并解压。UIkit 带有三款主题:默认、拟物和扁平。要引入默认的主题,从 UIkit 包中复制 `css/uikit.min.css` 文件,然后粘贴到你的主题的 `theme-hello/css/` 文件夹中。
确保文件已被 Pagekit 加载,需要打开主题中 `theme-hello/views/template.php` 这个主要布局文件。
在布局文件的 `<head>` 部分,我们可以看到一个 CSS 文件已被引入。
```
<?php $view->style('theme', 'theme:css/theme.css') ?>
```
现在,新增一行来添加 UIkit 的 CSS。确保它被添加在已有的那行**上面**,看起来如下:
```
<?php $view->style('custom-uikit', 'theme:css/uikit.min.css') ?>
<?php $view->style('theme', 'theme:css/theme.css') ?>
```
就这样,你的主题已经包含了 UIkit 的 CSS。要添加自己的 CSS,只需编辑 `theme-hello/css/theme.css`。
![](image/tutorial-theme-basic-css.png)
添加 UIkit 中的 CSS 会添加渲染标签的默认样式。要使其更漂亮,我们还需要给标签添加一些 class,自定义默认的 UIkit 样式,以及添加我们自己的 CSS 样式。
#### 进阶方法:使用 Gulp 和 LESS 来构建
在前面一节,我们了解了为主题添加纯 CSS 文件是多么的简单。如果你有构建网站的经验,你可能熟悉更灵活地为内容添加样式的方式。一个不错的例子就是使用类似 [LESS](http://lesscss.org/) 的CSS 预处理器。它允许你使用诸如变量这种让代码更易阅读和维护的东西。
在使用 UIkit 时,就有了更大的优势,你可以通过简单地修改变量来应用针对全局的修改,比如改变网站主色调。
要想更加舒适地使用 LESS 预处理器,你需要安装并启用一些工具,比如 *npm*, *gulp* 和 *bower*。如果你还没有安装他们,赶紧去谷歌一下,网上有很多教程。
有许多可能的文件结构设置。在下文中我们建议的结构与官方的 Pagekit 主题是一致的。或许第一次创建自己的主题时看起来有很多步骤,在我们的经验中,这样的设置是为良好的代码结构考虑的,轻松定制 UIkit 以及使用 Bower 舒心地保持 UIkit 与最新版本同步。
#### Step 1
在你的主题中,新建文件 `package.json`, `bower.json`, `.bowerrc`, `gulpfile.js` 和 `less/theme.less`。将以下内容粘贴到这些文件中。如果你为主题取了其他名字,用你的主题名字替换所有出现 `theme-hello` 的字符串。
`package.json`. 这个文件定义了运行 `npm install` 时要安装的所有 JavaScript 依赖,以及我们将要使用的几个 npm 包,比如在把 LESS 编译成为 CSS 时:
```
{
"name": "theme-hello",
"devDependencies": {
"bower": "*",
"gulp": "*",
"gulp-less": "*",
"gulp-rename": "*"
}
}
```
`bower.json` 告诉 bower 获取最新版本的 UIkit。这样我们就能通过运行 `bower install` 来获取 UIkit 中现有的 LESS 源文件了:
```js
{
"name": "theme-hello",
"dependencies": {
"uikit": "*"
},
"private": true
}
```
`.bowerrc` 包含 bower 的配置选项。默认地,bower 会将所有东西安装在主题文件夹内的 `/bower_components` 目录中。出于偏好,我们可以轻易地修改默认目录:
```js
{
"directory": "app/assets"
}
```
`gulpfile.js` 包含所有我们可以在 Gulp 上使用的任务指令。我们只需用到将 LESS 编译为 CSS 的指令。方便起见我们也添加一个 `watch` 指令,这样在任何文件有修改行为时,就能自动重新编译 LESS 了:
```js
var gulp = require('gulp'),
less = require('gulp-less'),
rename = require('gulp-rename');
gulp.task('default', function () {
return gulp.src('less/theme.less', {base: __dirname})
.pipe(less({compress: true}))
.pipe(rename(function (file) {
// the compiled file should be stored in the css/ folder instead of the less/ folder
file.dirname = file.dirname.replace('less', 'css');
}))
.pipe(gulp.dest(__dirname));
});
gulp.task('watch', function () {
gulp.watch('less/*.less', ['default']);
});
```
`less/theme.less` 这是存储主题样式的地方。注意,首先需要引入 UIkit 使其也能通过上面设定的 Gulp 指令进行编译。
```less
@import "uikit/uikit.less";
// 使用系统的图标字体
@icon-font-path: "../../../../app/assets/uikit/fonts";
// 你的主题样式放在这下面
```
`.gitignore` 是可选的文件,它可以在使用 Git 管理代码时为你提供帮助。在 Hello 主题中,已经有一个此文件的版本了。你可以像下面这样添加新条目。你大概不会提交从 bower 下载的包和已生成的 CSS。只需要在将主题上传到服务器或者提交到 Pagekit 商店时确保已经包含已生成的 CSS 就行了。
```txt
/app/bundle/*
/app/assets/*
/node_modules
/css
.DS_Store
.idea
*.zip
```
#### Step 2
创建以上文件后,前往 [UIkit Github 仓库](https://github.com/uikit/uikit),下载 Zip 压缩包并解压,找到 `themes/default` 文件夹(或者你喜欢的其他主题)。
![](image/tutorial-theme-download-uikit.png)
#### Step 3
在你的主题内创建 `/less` 文件夹,复制 `default` 主题的文件夹并粘贴到此处,将其重命名为 `/uikit`,这样它就被放在了主题文件夹中的 `less/uikit` 目录了。
#### Step 4
刚才复制的样式需要引入核心的 UIkit LESS 才能成功编译。你需要在主题的 `less/uikit/uikit.less` 文件中修改引入路径。将第四行修改为这个路径: `@import "../../app/assets/uikit/less/uikit.less";`
#### Step 5
在控制台标签页中打开你的主题(例如 `cd pagekit/packages/theme-hello`),并运行 `npm install`, `bower install` 和 `gulp`。
至此,还有一些步骤。确保你现在的文件结构和下面是一致的(加上之前额外的主题文件):
```
app/
assets/
jquery/ bower 安装的结果
uikit/ bower 安装的结果
css/
theme.css gulp 的结果
less/
uikit/
... uikit components
uikit.less
theme.less
node_modules/ npm 安装的结果
.bowerrc
bower.json
gulpfile.js
package.json
... 其它主题文件
```
对于这个文件设置,我们已经实现了以下几点:
- **分离**,主题文件和 UIkit 定制文件的分离。将自有的样式添加到 `less/theme.less`,在 `less/uikit/*` 中自定义 UIkit。
- **轻松定制 UIkit**: 每个 UIkit 组件的设置项目都在它自己的 `*.less` 文件中。例如,要修改正文的字体大小,只需要打开 `less/uikit/base.less` 并修改 `@base-body-font-size` 的值,然后运行 `gulp`。要使用任何[UIkit 附加组件](http://getuikit.com/docs/components.html),打开 `less/uikit.less` ,然后附加组件的 less 文件从 `app/assets` 目录中引入,例如幻灯片,只需要添加一行: `@import "../../app/assets/uikit/less/components/slideshow.less";` 然后再运行 `gulp.`
- **轻松更新 UIkit**: 运行 `bower install` 获取最新版本的 UIkit,然后运行 `gulp` 将 LESS 文件编译成 CSS。
## 添加 JavaScript
在 Hello 主题中,有一个空的 JavaScript 文件 `js/script.js`。可以将你的 javascript 代码放到这里。在 Hello 主题中,默认会加载这个文件,因为在 `template.php` 中已经引入了它:
```
<?php $view->script('theme', 'theme:js/theme.js') ?>
```
在引入脚本时,需要一个唯一标识符(`theme`) 和该脚本文件的路径(`theme:js/theme.js`)。如你所见,可以使用 `theme:` 作为简写的文件路径。要添加更多的 javascript 文件,只需以同样的方法添加几行就行。确保你已为这些文件指定了不同的标识符。如果所有文件都被命名为 `theme`,那么只有最后一个会被引入。
```
<?php $view->script('theme', 'theme:js/theme.js') ?>
<?php $view->script('plugins', 'theme:js/plugins.js') ?>
<?php $view->script('slideshow', 'theme:js/slideshow.js') ?>
```
添加多个 `script()` 调用是引入 javascript 的最简单方式,`script()` 助手函数允许以更强大的方式来引入相互依赖的 javascript 文件。在下一节我们将进行更深入的了解。
### 添加多个相互依赖的 javascript 文件
在前面的例子中,我们用到了 UIkit 的 CSS。如果还想要使用 UIkit 的 javascript 组件和效果,则需要添加 UIkit 的javascript 文件。注意,UIkit 需要先加载 jQuery,然后才能使用 UIkit 的 javascript 组件。
在 `views/template.php` 文件中 `script('theme', ...)` 的前面这样,替换为下面的三行。
```
<?php $view->script('theme-jquery', 'theme:app/assets/jquery/dist/jquery.min.js') ?>
<?php $view->script('theme-uikit', 'theme:app/assets/uikit/js/uikit.min.js', 'theme-jquery') ?>
<?php $view->script('theme', 'theme:js/theme.js', 'theme-uikit') ?>
```
这个例子假设你已使用了上面的高级设置,使用 bower 将 UIkit 和 jQuery 安装到了 `app/assets` 文件夹。如果你使用的是简单的设置,只需要下载并将 `jquery.min.js` 和 `uikit.min.js` 复制到主题中相应的位置并修改相关路径。
注意,我们现在要添加第三个参数了,第三个参数定义了我们加载的脚本的_依赖(dependencies)_ 。依赖是指需要更早地被加载的其他 javascript 文件。所以在例子中,Pagekit 将会清晰地确保三个文件是按这样的顺序加载的: jQuery, UIkit 然后才是自己的 `theme.js`。在这个特例中,这个机制好像不是很管用,因为脚本可能已经恰好按照我们在 `template.php` 中放置的顺序被加载了,是这样吗?确实是的,但是想象一下如果这些文件是位于主题中不同的文件和子模板的呢?定义依赖将确保 Pagekit 始终以正确的顺序加载这些文件。
如上面的例子,依赖是通过唯一的字符串标识符被引用的 (e.g. `theme-jquery`)。在我们的例子中,该标识符是赋予第一次使用 `script()` 方法被引入的脚本的。所以如你所见,这个方法有三个参数: `$view->script($identifier, $path_to_script, $dependencies)`。
要确保它已工作,打开 `views/template.php` 并添加下面这些代码(`data-uk-*` 是 UIkit javascript 组件的前缀).
```
<!-- 添加 id="up" 到 body -->
<body id="up">
<!-- 保留已有的内容 ... -->
...
<!-- 添加 to-top-scroller -->
<div class="uk-text-center">
<a href="#up" data-uk-smooth-scroll=""><i class="uk-icon-caret-up"></i></a>
</div>
<!-- 保留 footer 的渲染 -->
<?= $view->render('footer') ?>
</body>
```
刷新浏览器,你会看到一个用于平滑滚动到浏览器窗口顶部的小箭头。如果浏览器并不能平滑地滚动,而是一下子跳转了,你需要检查一些是不是各处都和例子中一样正确。
### 添加第三方脚本,比如 jQuery
你或许会问为什么我们将引入的 jQuery 称为 `theme-jquery` 而不是 `jquery`。通常,为自己的标识符添加前缀是有用的,可以避免与其他扩展发生冲突。然而,在这个特例中,标识符 `jquery` 和 `uikit` 都已被占用了,因为 Pagekit 自身就引入了 jQuery 和 UIkit。所以,使用前缀之后,所有主题和扩展都能使用独立版本的 jQuery (以及 UIkit,如果用了的话)来避免冲突。
```
<?php $view->script('theme', 'theme:js/theme.js', ['uikit', 'jquery']) ?>
```
如例子所示,`script()` 方法的第三个参数还可以获取一个包含多个依赖的列表。在前面的例子,我们只传递了单独的一个字符串 (例如 `theme-jquery`)。为单个依赖传递字符串,或者为多个依赖传递列表,都是可行的。
当前载入的 jQuery 和 UIkit 版本取决于当前的 Pagekit 版本。对于新版本的 Pagekit,这些库也会被更新。虽然这样至少能有一个当前的版本可以使用,但也有一个缺点,你需要确保你的代码总能在这些库的新版本中也是能正常工作的。
## 布局
主题布局的主文件就是 `views/template.php` 和 `index.php`。实际的渲染发生在 `template.php`中。
打开 `template.php` 时,你会看到为你开始起步而准备的基础设置。让我们来将主要内容包裹在一个容器内,使用网格分隔系统输出的信息和边栏。
在`views/template.php` 文件的**第30行**,渲染了边栏的位置和实际内容。
```
<!-- 渲染小工具位置 -->
<?php if ($view->position()->exists('sidebar')) : ?>
<?= $view->position('sidebar') ?>
<?php endif; ?>
<!-- 渲染内容 -->
<?= $view->render('content') ?>
```
使用 UIkit 的 [块布局(Block)](http://getuikit.com/docs/block.html) 和 [效果(Utility)](http://getuikit.com/docs/utility.html) 组件,创建一个定位块和一个流体宽度的容器。
为你自己的 class 添加前缀始终是一个好主意,它们不会与可能已经使用了的 CSS 冲突。例如,所有 UIkit 的 class 都是有 `uk-` 前缀的。要区分主题中的 class 或 ID,我们使用 `tm-` 前缀。因此,我们添加 class 和 ID `tm-main` 来识别这部分。
```
<div id="tm-main" class="tm-main uk-block">
<div class="uk-container uk-container-center">
<!-- Render widget position -->
<?php if ($view->position()->exists('sidebar')) : ?>
<?= $view->position('sidebar') ?>
<?php endif; ?>
<!-- Render content -->
<?= $view->render('content') ?>
</div>
</div>
```
现在,我们想要系统输出信息和边栏并排放置。[网格(Grid)](http://getuikit.com/docs/grid.html)能帮到我们。为了使布局代码更语义化,我们使用 `<main>` 和 `<aside>` 元素来定义容器。
```
<div id="tm-main" class="tm-main uk-block">
<div class="uk-container uk-container-center">
<div class="uk-grid" data-uk-grid-match data-uk-grid-margin>
<main class="<?= $view->position()->exists('sidebar') ? 'uk-width-medium-3-4' : 'uk-width-1-1'; ?>">
<!-- 渲染内容 -->
<?= $view->render('content') ?>
</main>
<?php if ($view->position()->exists('sidebar')) : ?>
<aside class="uk-width-medium-1-4">
<?= $view->position('sidebar') ?>
</aside>
<?php endif; ?>
</div>
</div>
</div>
```
### 主题元素
要创建更复杂的布局,可以添加小工具位置、菜单以及相关选项。常规的主题基本是由小工具、菜单和页面内容本身这三者组成的。
页面的*内容* 只不过是 Pagekit 的系统输出信息。这意味着你创建的任意页面的内容都会在此处渲染。
*小工具 Widgets* 是可以在网站各种位置渲染的内容块,所以它们也会显示在网站标签的特定位置上。
要浏览任意网站,首先需要设置一个*菜单*。为此,Pagekit 提供了多种菜单位置,允许用户在主题标签的各种位置上发布菜单。
![](image/tutorial-theme-elements.png)
典型的网站布局包括主导航、页面内容和一些小工具位置。
你的主题需要先注册所有的控件位置,在 `index.php` 文件中使用`menus` 和 `positions` 属性来操作。包括控件位置的名称和标签(label),都会显示在管理面板中。此文件也能用来添加其他的脚本等等东西。
## 导航栏
主导航栏是你想要在主题中首先呈现的事物之一。
![](image/tutorial-theme-menu-unstyled.png)
默认地,Hello 主题以非常简单的垂直方式呈现出菜单条目
#### Step 1
Hello 主题自带了预定义的*Main* 菜单位置。新增位置时,需要使用一个标识符(i.e. `main`)来定义它,并为用户添加一个标签(i.e. *Main*)。
```
'menu' => [
'main' => 'Main',
]
```
![](image/tutorial-theme-menu-position.png)
菜单可以发布在 Pagekit 站点树中已定义的位置上
#### Step 2
基于模块化的理念,Pagekit 以单独的文件来渲染每个位置布局。对于导航,创建一个包含以下代码的 `views/menu-navbar.php` 文件:
```
<?php if ($root->getDepth() === 0) : ?>
<ul class="uk-navbar-nav">
<?php endif ?>
<?php foreach ($root->getChildren() as $node) : ?>
<li class="<?= $node->hasChildren() ? 'uk-parent' : '' ?><?= $node->get('active') ? ' uk-active' : '' ?>" <?= ($root->getDepth() === 0 && $node->hasChildren()) ? 'data-uk-dropdown':'' ?>>
<a href="<?= $node->getUrl() ?>"><?= $node->title ?></a>
<?php if ($node->hasChildren()) : ?>
<?php if ($root->getDepth() === 0) : ?>
<div class="uk-dropdown uk-dropdown-navbar">
<?php endif ?>
<?php if ($root->getDepth() === 0) : ?>
<ul class="uk-nav uk-nav-navbar">
<?php elseif ($root->getDepth() === 1) : ?>
<ul class="uk-nav-sub">
<?php else : ?>
<ul>
<?php endif ?>
<?= $view->render('menu-navbar.php', ['root' => $node]) ?>
</ul>
<?php if ($root->getDepth() === 0) : ?>
</div>
<?php endif ?>
<?php endif ?>
</li>
<?php endforeach ?>
<?php if ($root->getDepth() === 0) : ?>
</ul>
<?php endif ?>
```
#### Step 3
要将实际的导航栏渲染到 `template.php` 文件中,创建一个 `<nav>` 元素并添加 class `.uk-navbar` 。像下面这样在元素内加载 `menu-navbar.php` 文件(可以在已经渲染了 `$view->menu('main')` 的地方移除现有的内容块)。
```
<nav class="uk-navbar">
<?php if ($view->menu()->exists('main')) : ?>
<div class="uk-navbar-flip">
<?= $view->menu('main', 'menu-navbar.php') ?>
</div>
<?php endif ?>
</nav>
```
现在,主菜单就能自动渲染到新建的 *Navbar* 位置了。
#### Step 4
你可以能还想在导航栏中显示 LOGO。那么,用 `<nav>` 元素包裹 logo 然后添加 `.uk-navbar-brand` class, 为其添加适当的间距。
```
<nav class="uk-navbar">
<!-- Render logo or title with site URL -->
<a class="uk-navbar-brand" href="<?= $view->url()->get() ?>">
<?php if ($logo = $params['logo']) : ?>
<img src="<?= $this->escape($logo) ?>" alt="">
<?php else : ?>
<?= $params['title'] ?>
<?php endif ?>
</a>
<?php if ($view->menu()->exists('main')) : ?>
<div class="uk-navbar-flip">
<?= $view->menu('main', 'menu-navbar.php') ?>
</div>
<?php endif ?>
</nav>
```
![](image/tutorial-theme-navbar.png)
基于我们的修改,菜单条目现在总算是以水平方式排列了。
### 添加主题选项
Pagekit 使用 [Vue.js](https://vuejs.org/) 构建它的管理界面。这里有一个关于在 Pagekit 中使用 Vue.js 的[视频教程](http://www.bilibili.com/video/av6824636/index_5.html) 。
导航栏频繁被请求的特性是为了在页面滚动时使其固定在窗口上方。下面的步骤里,我们将它作为一个选项添加到主题中。
#### Step 1
首先,我们需要创建 `app/components` 文件夹,再在它里面创建 `site-theme.vue` 文件。作用于整个网站的设置选项就存放在这个文件里,可以在站点树中 *Setting*标签页下的 *Theme* 中找到这些设置。这些设置无法用于特定的页面。
![](image/tutorial-theme-site-tree.png)
可以为管理界面添加各种各样的设置选项
在新建的 `site-theme.vue` 文件中,添加可以显示在 Pagekit 管理内的设置选项。
```
<template>
<div class="uk-margin uk-flex uk-flex-space-between uk-flex-wrap" data-uk-margin>
<div data-uk-margin>
<h2 class="uk-margin-remove">{{ 'Theme' | trans }}</h2>
</div>
<div data-uk-margin>
<button class="uk-button uk-button-primary" type="submit">{{ 'Save' | trans }}</button>
</div>
</div>
<div class="uk-form uk-form-horizontal">
<div class="uk-form-row">
<label for="form-navbar-mode" class="uk-form-label">{{ 'Navbar Mode' | trans }}</label>
<p class="uk-form-controls-condensed">
<label><input type="checkbox" v-model="config.navbar_sticky"> {{ 'Sticky Navigation' | trans }}</label>
</p>
</div>
</div>
</template>
```
#### Step 2
现在,我们需要使这些选项在站点树中可用。为此,我们通过在 `site-theme.vue` 文件中的选项下添加以下脚本来创建一个 *Theme* 标签页。
```
<script>
module.exports = {
section: {
label: 'Theme',
icon: 'pk-icon-large-brush',
priority: 15
},
data: function () {
return _.extend({config: {}}, window.$theme);
},
events: {
save: function() {
this.$http.post('admin/system/settings/config', {name: this.name, config: this.config}).catch(function (res) {
this.$notify(res.data, 'danger');
});
}
}
};
window.Site.components['site-theme'] = module.exports;
</script>
```
#### Step 3
要加载这端脚本并为站点树添加选项,你还需要添加以下代码到 `index.php` 文件中。
```
'events' => [
'view.system/site/admin/settings' => function ($event, $view) use ($app) {
$view->script('site-theme', 'theme:app/bundle/site-theme.js', 'site-settings');
$view->data('$theme', $this);
}
]
```
#### Step 4
为 `index.php` 文件中的导航栏模式添加默认设置
```
'config' => [
'navbar_sticky' => false
],
```
#### Step 5
Vue 组件需要使用 Webpack 来编译成 javascript。为此,需要在主题文件夹中创建一个 `webpack.config.js` 文件:
```
module.exports = [
{
entry: {
"site-theme": "./app/components/site-theme.vue"
},
output: {
filename: "./app/bundle/[name].js"
},
module: {
loaders: [
{ test: /\.vue$/, loader: "vue" }
]
}
}
];
```
#### Step 6
之后,在主题目录中运行指令 `webpack`,`site-theme.vue` 将被编译成为`/bundle/site-theme.js` ,模板标签则转换成行内字符串。
如果你还没这么用过,你可以[全局安装 webpack](http://webpack.github.io/docs/installation.html) ,然后在项目目录中运行以下指令,使本地的 webpack 和 Vue 编译器生效。
```
npm install webpack vue-loader vue-html-loader babel-core babel-loader babel-preset-es2015 babel-plugin-transform-runtime --save-dev
```
无论何时,只要你对 Vue 组件做了修改,都需要再次运行这个指令。或者,可以运行 `webpack --watch` 或 `webpack -w` 使 webpack 保持激活状态然后自动将 Vue 组件的修改再次编译。可以使用 *Ctrl+C* 来退出。要了解 Vue 和 Webpack 的更多信息,查看 [Vue.js 和 Webpack](224138)。
#### Step 7
最后,我们想要在 `views/template.php` 文件的头部加载必要的javascript依赖。我们需要使用 UIkit 的 [Sticky 组件](http://getuikit.com/docs/sticky.html)。它并不包含在 UIkit 核心框架中,需要单独加载它。可以在加载主题的 javascript 时,通过为 sticky 组件添加依赖的方式完成此事。
```
<?php $view->script('theme', 'theme:js/theme.js', ['uikit-sticky']) ?>
```
现在你所需要做的就是将这些选项渲染到 `template.php`内实际的导航栏中。
```
<nav class="uk-navbar uk-position-z-index" <?= $params['navbar_sticky'] ? ' data-uk-sticky' : '' ?>>
```
在管理界面,前往*Site > Settings > Theme* 然后启用 _Sticky Navigation_ 选项观察它在网站上的效果。
## 小工具
小工具位置允许用户在主题标签的各个位置发布小工具。它们显示在 Pagekit 管理面板的*小工具 Widgets* 区域,用户在设置小工具时可以选择它们。
#### Step 1
要渲染一个新的小工具位置,首先需要在 `index.php` 中注册。例如,如果我们想新建一个 *Top* 位置,需要使用 `positions` 属性来定义它。
```
'positions' => [
'sidebar' => 'Sidebar',
'top' => 'Top'
],
```
#### Step 2
现在,我们已经让 Pagekit 知晓了新的位置,我们还需要创建一个位置渲染器。可以跳过这步直接使用默认的渲染器,但所有的小工具都会简单地渲染在彼此下方。所以为了在网格中布局小工具,需要创建 `views/position-grid.php` 文件:
```
<?php foreach ($widgets as $widget) : ?>
<div class="uk-width-medium-1-<?= count($widgets) ?>">
<div class="uk-panel">
<h3 class="uk-panel-title"><?= $widget->title ?></h3>
<?= $widget->get('result') ?>
</div>
</div>
<?php endforeach ?>
```
#### Step 3
要在主题标签中渲染新的位置,需要将它添加到 `views/template.php` 文件中,`</nav>` 标签后面:
```
<?php if ($view->position()->exists('top')) : ?>
<div id="top" class="tm-top">
<div class="uk-container uk-container-center">
<section class="uk-grid uk-grid-match" data-uk-grid-margin>
<?= $view->position('top', 'position-grid.php') ?>
</section>
</div>
</div>
<?php endif ?>
```
现在就能为任意小工具选择*Top* ,将其渲染到刚刚创建的位置上了。
![](image/tutorial-theme-select-position.png)
创建小工具时选择新的位置
### 添加位置选项
在添加配置选项前,上面的例子会对整个网站生效。我们还可以扩展站点树的配置,使之只适用于特定的页面。现在,我们来为新的 Top 位置添加一个应用不同背景色彩的选项。
#### Step 1
首先,需要在 `app/components` 中创建文件 `node-theme.vue`。在此我们添加的选项会显示在 Pagekit 管理界面中。设置选项都存储在这个文件里,可以在站点树中输入相应的条目和点击 *Theme* 标签页来单独应用到每个页面。
```
<template>
<div class="uk-form-horizontal">
<div class="uk-form-row">
<label for="form-top-style" class="uk-form-label">Top {{ 'Position' | trans }}</label>
<div class="uk-form-controls">
<select id="form-top-style" class="uk-form-width-large" v-model="node.theme.top_style">
<option value="uk-block-default">{{ 'Default' | trans }}</option>
<option value="uk-block-muted">{{ 'Muted' | trans }}</option>
</select>
</div>
</div>
</div>
</template>
```
#### Step 2
现在,我们还必须让选项在站点树中可用。为此,我们可以通过添加以下代码到 `node-theme.vue` 文件中,在界面上创建一个*Theme* 标签页。
```
<script>
module.exports = {
section: {
label: 'Theme',
priority: 90
},
props: ['node']
};
window.Site.components['node-theme'] = module.exports;
</script>
```
#### Step 3
在关于主题选项这章,我们将事件监听器插入到 `index.php`文件以加载脚本并将选项添加到站点树。现在,我们需要做网站选项相同的事。完整的 `events` 这部分应该是这样子的:
```
'events' => [
'view.system/site/admin/settings' => function ($event, $view) use ($app) {
$view->script('site-theme', 'theme:app/bundle/site-theme.js', 'site-settings');
$view->data('$theme', $this);
},
'view.system/site/admin/edit' => function ($event, $view) {
$view->script('node-theme', 'theme:app/bundle/node-theme.js', 'site-edit');
}
]
```
#### Step 4
小工具位置的默认设置同样需要添加到 `index.php` 中
```
'node' => [
'top_style' => 'uk-block-muted'
],
```
#### Step 5
在关于主题选项这一章,我们创建了文件 `webpack.config.js`。我们的 `node-theme.vue` 文件还需要注册以便编译成为 JavaScript。
```
entry: {
"node-theme": "./app/components/node-theme.vue",
"site-theme": "./app/components/site-theme.vue"
},
```
#### Step 6
现在,在主题目录中运行 `webpack` 命令,`node-theme.vue` 将编译成为 `app/bundle/node-theme.js`。
#### Step 7
最后,要切实地将已选的设置渲染到小工具位置上,我们需要添加 `.uk-block` class 以及样式参数到 `template.php` 文件里的位置本身。
```
<div id="top" class="tm-top uk-block <?= $params['top_style'] ?>">
```
#### Step 8
在站点树中,你现在编辑页面时,能看到一个 _Theme_ 标签页。在此可以配置新选项。这个配置只会应用到特定的页面中。
![](image/tutorial-theme-node.png)
由主题添加的阶段选项
### 添加小工具选项
还可以为小工具本身添加特定的选项。在这种情况下,我们可能想要为每个小工具提供可选的面板样式选项。
![](image/tutorial-theme-widget-options.png)
主题可以为小工具编辑器添加各种各样的选项
#### Step 1
首先,我们需要在 `app/components` 文件夹中创建一个 `app/components/widget-theme.vue` 文件。它将渲染一个选择框,我们可以在选择框中为小工具选择样式。
```
<template>
<div class="uk-form-horizontal">
<div class="uk-form-row">
<label for="form-theme-panel" class="uk-form-label">{{ 'Panel Style' | trans }}</label>
<div class="uk-form-controls">
<select id="form-theme-panel" class="uk-form-width-large" v-model="widget.theme.panel">
<option value="">{{ 'None' | trans }}</option>
<option value="uk-panel-box">{{ 'Box' | trans }}</option>
<option value="uk-panel-box uk-panel-box-primary">{{ 'Box Primary' | trans }}</option>
<option value="uk-panel-box uk-panel-box-secondary">{{ 'Box Secondary' | trans }}</option>
<option value="uk-panel-header">{{ 'Header' | trans }}</option>
</select>
</div>
</div>
</div>
</template>
```
#### Step 2
我们还需要使此选项在小工具管理界面中可用。为此,添加以下代码到`widget-theme.vue` 文件中,在界面中创建一个 Theme* 标签页。
```
<script>
module.exports = {
section: {
label: 'Theme',
priority: 90
},
props: ['widget', 'config']
};
window.Widgets.components['theme'] = module.exports;
</script>
```
#### Step 3
要让选项在小工具管理界面中可用,还需要在 `index.php` 中添加以下代码来在界面上创建一个`Theme` 标签页。
```
'view.system/widget/edit' => function ($event, $view) {
$view->script('widget-theme', 'theme:app/bundle/widget-theme.js', 'widget-edit');
}
```
#### Step 4
小工具的默认设置也需要添加到 `index.php`中
```
'widget' => [
'panel' => ''
],
```
#### Step 5
将 `widget-theme` 实体添加到 `webpack.config.json` 文件,这样它就能被编译成为 javascript(别忘了在主题目录中运行 `webpack`指令)。
```
entry: {
"node-theme": "./app/components/node-theme.vue",
"site-theme": "./app/components/site-theme.vue",
"widget-theme": "./app/components/widget-theme.vue"
},
```
#### Step 6
现在需要在做的就是将已选的设置渲染到 `position-grid.php` 文件中的小工具上。
```
<div class="uk-panel <?= $widget->theme['panel'] ?>">
```
#### Step 7
在编辑小工具时,你会在编辑器上面看到一个 _Theme_ 标签,在这里可以访问刚刚添加的 _Panel Style_ 配置选项。
## 覆写系统视图
主题也能定制 Pagekit 渲染静态页面和博客文章的方式。可以很容易地通过覆写系统的视图文件来应用你自己的布局。为此,要在主题中创建相应的目录来模拟原始的 Pagekit 系统的结构,并且像这样放置模板文件:
|文件 | 原始的视图文件 | 描述|
|---------------------------- | ---------------------------------------- | ------------------------|
|`views/system/site/page.php` | `/app/system/site/views/page.php` | 默认的静态页面视图 |
|`views/blog/post.php` | `/packages/pagekit/blog/views/post.php` | 博客文章单页视图 |
|`views/blog/posts.php` | `/packages/pagekit/blog/views/posts.php` | 博客文章列表视图 |
要了解哪些变量在视图中可用,查阅原始视图文件的标签即可。
## 总结
在这个教程中,你已学习创建 Pagekit 主题的基础知识和相关工具的用法。现在,我们来总结一下。
**Note** [最后完成的主题](https://github.com/pagekit/example-theme) 可以在 Github 上找到哦。.
**Note** [最后完成的主题](https://github.com/pagekit/example-theme) 可以在 Github 上找到哦。.
- 现在你已熟悉了 Pagekit 主题的**文件结构**。配置和自定义代码的主入口是主题的 `index.php`,主模板文件位于主题内的 `views/template.php` 中。
- 我们提出了现代化的 **前端工作流程**和 LESS、Gulp以及 bower这些工具的相关方法。可以根据你的偏好来定制这些设置。
- 要在主题中**添加 javascript**,可以是在模板中添加自己的代码或者引入脚本文件。你还可以添加第三方库,比如 UIkit和 jQuery 来帮助提升网站的交互。
- **小工具和菜单** 是在 Pagekit 管理界面里管理的,可以渲染在主题中指定的位置。你已经学习了如何创建位置以及如何修改默认的小工具渲染。
- 要让你的主题可以在管理区域进行定制,你可以添加自己的**设置界面**。这些都可以添加到站点树中、网站设置中以及小工具编辑器中。
- 要**覆写系统视图**,你的主题可以包含自定义的视图文件来修改静态页面和博客文章的渲染方式。
有了创建 Pagekit 主题的这些技能,你已经可以为客户项目或者 Pagekit 商店创建主题啦!