总体来说 Typecho做到了简洁实用,有些部分虽然有些简单或是有些不足,但是自行修改是十分便捷的,这非常适合用来维持一个单纯的博客系统。

因为近期实在是感受到了记忆力下滑、接触面过广的时候对于笔记、知识点记忆搜索的需求很强烈,不得不重新拾起曾经一度丢弃的个人博客。 区别在于以前更多的是设计作品展示、图片展示。现在更重要的是做技术博客。

在很久之前我一直以来习惯的就是使用WordPress来搭建博客和网站,曾经在做设计师的时候折腾过蛮多次WordPress来制作个人网站、在线教育网站,包括给别人搭建电子商务网站。 2017年 基本主义在线商城: 基本主义

但是由于一些原因、我不打算再尝试WordPress这个较重、同时风险也比较高的CMS 其中最主要原因还是因为折腾成本太高、想要去自定义的时候需要很了解这个框架的设计、想要深度定制的难度很大、修改别人的主题、模板的时候由于代码的结构化很差、所以想要做修改的时间成本异常的高。

(也正是因为2017年痛的领悟,所以自行开发了AppSite全栈框架) 现在自己再去写一个博客又感觉挺浪费时间的。

简书、lofter这类自由度不够、cnblogs、CSDN等技术博客又太过不能入眼, 所以决定还是找一个极简的cms来做,这时候正好发现了Typecho。

有兴趣的可以去GitHub看一下他们的源码、还是很轻量级的,作为比较纯的博客程序又完全足够,如果有自己的想法,可以自己快速的修改,所以就在服务器上马上安装了一个。 Typecho

以下是具体搭建过程,分两大部分


I. 部署

服务器和环境搭建 服务器: 阿里云ECS 1核2GB 40G数据盘 1MBs带宽 折扣购买阿里云ECS 系统: CentOS 7.2 x64 环境: Nginx 1.14, PHP 7.2, MySQL 5.6

阿里云安装 Nginx,Php,MySql 环境教程

Typecho软件安装 安装完环境后下载最新版的Typecho到web目录中,域名和目录要先绑定好

打开 yourdomain/install.php 默认没有安装的时候打开域名会自动跳转安装页面(安装完毕之后建议从www目录删除这个文件) 推荐在服务端开启rewrite功能,这样我们的目录可以看起来比较简洁 不同环境下的rewrite设置-Typecho Wiki

我们设置好域名、网站名称、数据等 就可以开始使用了!


II. 优化

网站加速

在服务端推荐开启opcache来加速访问

markdown链接跳转

在前台的时候markdown文本的链接会被自动添加为原页面跳转、这对于我们网站的体验和留存有一定影响。我们要将链接改为在新窗口中打开,或者是在编辑的时候可以选择新窗口打开链接或本窗口直接跳转。 这个部分对应代码经过简单排查定位在 /var/HyperDown.php 很快在300,377行找到了对应的正则表达式 我们修改上面的就可以

// link
$text = preg_replace_callback(
    "/<(https?:\/\/.+)>/i",
    function ($matches) use ($self) {
        $url = $self->cleanUrl($matches[1]);
        $link = $self->call('parseLink', $matches[1]);

        return $self->makeHolder(
            "<a target='_blank' href=\"{$url}\">{$link}</a>"
        );
    },
    $text
);

如果你不会正则最简单的就是在html上直接做改动 "<a href=" > "<a target=_blank href=" 这样就全部是新窗口

代码高亮

Typecho默认的代码展示部分即网页的<code>标签内容,是非常简单的,只是灰色底,灰色字。作为一个程序开发者,我们应该要有一个代码高亮,这里推荐使用 Highlight.js DEMO 具体修改首先打开 HightLightJS Download 注意这里从demo选择自己喜欢的样式时,记住样式的名称,后面引入链接的时候要使用小写的样式名称,比如我有iOS的开发习惯,选用的是xcode样式,后面就将 default.min.css改为 xcode.min.css。 本站使用的是CDN方式引入。 复制好链接 在博客后台依次打开

控制台 -> 外观 -> 编辑当前外观 -> 右侧点击 header.php

我们在header结束前引入对应的代码高亮css和js,体积比较小 所以不必担心加载速度。 如果怕cdn不稳定的 可以下载到自己的服务器,通过内链的形式引入

    <link rel="stylesheet"
      href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/styles/xcode.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/highlight.min.js"></script>
    <!-- 通过自有函数输出HTML头部信息 -->
    <?php $this->header(); ?>
</head>

保存,回到博客文章页面刷新,代码看起来很舒服。


后台Markdown解析器的更换(2019-08-19更新)

随着使用的增多,发现有很多比较多的markdown标记没有被typecho支持,最简单的比如表格。 这里我经过GitHub简单搜索发现了一个比较好的解析器,下载ZIP包后即可看到后面要用到的文件。 GITHUB主页:Parsedown{:target="_blank"}

替换的工作也比较简单,Typecho的Markdown解析主要是由 var/Markdown.php var/Hyperdown.php 来完成 其中Hyperdown为独立的支持模块,我们只需要将Parsedown.php下载并上传到同一个目录下,再到Markdown.php中引入文件、替换解析function即可。

上传文件:

WX20190819-151739.png

替换解析语句:

<?php
    if (!defined('__TYPECHO_ROOT_DIR__')) exit;

    include "Parsedown.php"; // 引入包!

    /**
     * Markdown解析
     *
     * @package Markdown
     * @copyright Copyright (c) 2014 Typecho team (http://www.typecho.org)
     * @license GNU General Public License 2.0
     */
    class Markdown
    {
        /**
         * convert 
         * 
         * @param string $text 
         * @return string
         */
        public static function convert($text)
        {
            static $parser;

            if (empty($parser)) {
                // $parser = new HyperDown();          
                // $parser->hook('afterParseCode', function ($html) {
                //     return preg_replace("/<code class=\"([_a-z0-9-]+)\">/i", "<code class=\"lang-\\1\">", $html);
                // });

                // $parser->hook('beforeParseInline', function ($html) use ($parser) {
                //     return preg_replace_callback("/^\s*<!\-\-\s*more\s*\-\->\s*$/s", function ($matches) use ($parser) {
                //         return $parser->makeHolder('<!--more-->');
                //     }, $html);
                // });

                // $parser->enableHtml(true);
                // $parser->_commonWhiteList .= '|img|cite|embed|iframe';
                // $parser->_specialWhiteList = array_merge($parser->_specialWhiteList, array(
                //     'ol'            =>  'ol|li',
                //     'ul'            =>  'ul|li',
                //     'blockquote'    =>  'blockquote',
                //     'pre'           =>  'pre|code'
                // ));
                $parser = new Parsedown(); // 使用Parsedown实例化
            }

            // return str_replace('<p><!--more--></p>', '<!--more-->', $parser->makeHtml($text));// 注释掉原有解析
            return str_replace('<p><!--more--></p>', '<!--more-->', $parser->text($text));
        }

PS:同样建议修改一下新窗口链接跳转。Parsedown.php更加简单,在1454行后添加一个属性即可

    $Element['attributes']['target'] = '_blank';

代码高亮II 替换后台的Markdown编辑器 (20190819更新)

因为是技术博客,所以不断的会贴入新的code,慢慢的发现有的时候 代码高亮不能正确工作,主要是不能正确匹配编程语言。 较新的Markdown解析实际上是可以指定语言的,很幸运的是,经过我的测试,上面的Parsedown就可以完成指定语言解析的任务。 Markdown的写法是

```php
    $Element['attributes']['target'] = '_blank';
```

而现在比较遗憾的是Typecho后台的md编辑器并不支持类似的相对丰富的功能。 于是一不做二不休,我们直接将这个编辑器替换成一个能够实时预览的高级md编辑器.

如果要使用一个富媒体编辑器,我们要做的工作会相对比较多,例如对接上传功能(服务器/云存储),音视频解析,图像排版等很多细节,暂时不想做的太复杂. 这些功能虽然我在自己的框架中已经有完整的实现,但是纯博客系统,我认为Markdown已经很好用了.

首先我在Github上找到了 Editor.md

因为要使用实时预览,所以同时会占用2倍的编辑器空间。 这里我选择将后台编辑器的可视空间扩大,否则就无法正常的进行编辑了。

打开 admin/css/gird.css 在最后添加

@media(min-width:1600px) {
    .container {
        max-width: 1440px;
    }
}

@media(min-width:1900px) {
    .container {
        max-width: 1600px;
    }
}

接下来开始替换编辑器,首先经过简单排查,定位编辑器是在 admin/write-post.php 42行的位置,可以看到这里只是用textarea将md内容简单输出.

这里虽然看到了一个 editorSize() 但是后台却没有提供对应的修改入口,我决定把编辑器高度留到js的部分去调整。

实际的编辑器调用是在 write-post.php 最后的部分:

<?php
include 'copyright.php';
include 'common-js.php'; // 通用js
include 'form-js.php'; // 表单
include 'write-js.php'; // 日期等功能

Typecho_Plugin::factory('admin/write-post.php')->trigger($plugged)->richEditor($post);
if (!$plugged) {
    include 'editor-js.php'; // Markdown编辑器
}

include 'file-upload-js.php'; // 右侧文件上传功能
include 'custom-fields-js.php'; // 下面自定义字段功能
Typecho_Plugin::factory('admin/write-post.php')->bottom($post);
include 'footer.php';
?>

由于文件包有很多demo文件,这个时候为了方便维护管理,我选择在admin目录下新建一个 mdEditor 目录,然后将的下载好的文件全部先上传到这个目录中,如图。 mdeditor.png

本地打开 examples/index.html 就可以看到官方给出的demo 我们可以选择一个自己喜欢的, 这里我直接选择Full完整版本

参考 examples/full.html 就可以发现,实际上使用非常简单,我们需要的是

1.header引入editormd.css样式 # 任意地方引入也没问题,但是css会导致dom重新渲染,所以严格来说 放在header里效率比较高(Typecho博客其实差别不大) 2.引入jquery和editor.md.js 3.配置并注册编辑器

这里我写一个 mdEditor/editor.php 来替换我们上面所提到的 editor-js.php (为了省事儿 我把全部引入都写在一个文件中)

write-post.phpwrite-page.php中 都修改默认的编辑器

if (!$plugged) {
    // include 'editor-js.php';
    include 'mdEditor/editor.php';
}

完成后效果: 可以全屏编辑,效果还是不错的


!最后 如果不解决图片的编辑器上传问题,那么就不完美,所以现在来解决图片上传按钮。

先使用examples的php目录下的upload来完成上传,我们更改编辑器的上传url为:

../admin/mdEditor/examples/php/upload.php

需要注意的是,为了防止上传接口被恶意利用,我们必须将示例代码中的跨域请求禁止掉。 然后最好是将上传文件设置一个 年/月 这样的文件夹,方便后期管理维护 所以我们对upload.php做简单修改,完整代码如下:

<?php

    /*
     * PHP upload demo for Editor.md
     *
     * @FileName: upload.php
     * @Auther: Pandao
     * @E-mail: pandao@vip.qq.com
     * @CreateTime: 2015-02-13 23:20:04
     * @UpdateTime: 2015-02-14 14:52:50
     * Copyright@2015 Editor.md all right reserved.
     */

    //header("Content-Type:application/json; charset=utf-8"); // Unsupport IE
    header("Content-Type:text/html; charset=utf-8");
    // header("Access-Control-Allow-Origin: *"); // 禁用跨域调用

    require("editormd.uploader.class.php");

    error_reporting(E_ALL & ~E_NOTICE);

    $path     = dirname(dirname(dirname(dirname(__DIR__))));
    $url      = dirname($_SERVER['PHP_SELF']) . '/';
    $savePath = $path.'/usr/uploads/';            // 我们遵循Typecho所制定的上传路径
    $saveURL  = 'https://shezw.com/usr/uploads/'; // 使用主域名以备后续生成图片链接

    // 推荐按年份/月份/ 组织文件上传
    $savePath.= date("Y/m/");
    $saveURL .= date("Y/m/");

    $formats  = array(
        'image' => array('gif', 'jpg', 'jpeg', 'png', 'bmp')
    );

    $name = 'editormd-image-file';

    if (isset($_FILES[$name]))
    {        
        $imageUploader = new EditorMdUploader($savePath, $saveURL, $formats['image']);  // Ymdhis表示按日期生成文件名,利用date()函数

        $imageUploader->config(array(
            'maxSize' => 2048,        // 允许上传的最大文件大小,以KB为单位,默认值为1024 可以适当增大
            'cover'   => true         // 是否覆盖同名文件,默认为true
        ));

        if ($imageUploader->upload($name))
        {
            $imageUploader->message('success!', 1);
          exit;
        }
        else
        {
            $imageUploader->message('failed!', 0);
          exit;
        }
    }
?>

完成后效果:


我们如果愿意的话,多花一些时间也可以将mdEditor和Typecho自带的后台上传结合到一起.

经过抓包分析(Chrome,Safari自带开发者工具即可),我们可以看到Typecho自带的上传接口是

https://shezw.com/action/upload?cid=8&_=07cea8aff364759...

cid是文章ID 我们可以通过php获得,然后写入到HTML的js变量中 后面一串防止接口被外部恶意使用而加的防盗串,要去附件上传的模块去看一下~ 在上传模块确实有一个安全函数用来生成一个安全码的方法。我们直接从该文件中复制出来并粘贴到上传URL的部分。

imageUploadURL : "<?php $security->index('/action/upload'. (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>"

如果我们不使用安全码调用的话,会被重定向,无法完成上传哦。

使用有效地址后,我们遇到的问题是,mdEditor的回调程序使用的json结构和Typecho的输出不一致的问题。 我们可以选择修改Typecho的服务端输出 或者 修改上传完成的回调js部分.

我选择修改上传完成回调JS. 经过代码分析,对应的模块位于 mdEditor/plugins/image-dialog/image-dialog.js

修改 submitHandler 中读取json的部分即可(根据Type的输出格式来改,格式在上一张图中)

  if(!settings.crossDomainUpload)
  {
    // if (json.success === 1)
    if(json[1] && json[1].cid)
    {
        // dialog.find("[data-url]").val(json.url);
        dialog.find("[data-url]").val(json[0]); 
    }
    else
    {
        // alert(json.message);
        alert("上传失败.");
    }
  }

保存后,清除缓存,完成!

最后贴一张 使用优化后的上传图片功能和完整的 editor.php

<?php
if (isset($post) && $post instanceof Typecho_Widget && $post->have()) {
    $fileParentContent = $post;
} else if (isset($page) && $page instanceof Typecho_Widget && $page->have()) {
    $fileParentContent = $page;
}

?>

<link rel="stylesheet" href="mdEditor/css/editormd.css" />   
<script src="mdEditor/editormd.js"></script>

    <script type="text/javascript">
        var testEditor;

        $(function() {

            testEditor = editormd("mdEditor", { // Typecho的编辑器ID是 text 我们改成自定义的
                width: "100%",
                height: 540,
                path : '../admin/mdEditor/lib/', // 注意修改editor所在的路径 ! 否则可能找不到其他需要自动引入的文件
                theme : "light",                 // 工具栏主题 推荐白色 light/dark
                previewTheme : "light",          // 实时预览主题 light/dark
                editorTheme : "default", // 编辑框主题 default / pastel-on-dark
                markdown : $("#mdEditor textarea").val(),
                codeFold : true,
                //syncScrolling : false,
                saveHTMLToTextarea : false,    // 保存 HTML 到 Textarea
                // 保持存储在数据库中的仍然是Markdown格式
                searchReplace : true,
                watch : true,                  // 开启实时预览 // 电脑性能一般的或者 追求完美顺畅的建议关闭
                htmlDecode : "style,script,iframe|on*",            // 开启 HTML 标签解析,为了安全性,默认不开启    
                //toolbar  : false,             //关闭工具栏
                //previewCodeHighlight : false, // 关闭预览 HTML 的代码块高亮,默认开启
                emoji : true,
                taskList : true,
                tocm            : true,         // Using [TOCM]
                tex : true,                   // 开启科学公式TeX语言支持,默认关闭
                flowChart : true,             // 开启流程图支持,默认关闭
                sequenceDiagram : true,       // 开启时序/序列图支持,默认关闭,
                //dialogLockScreen : false,   // 设置弹出层对话框不锁屏,全局通用,默认为true
                //dialogShowMask : false,     // 设置弹出层对话框显示透明遮罩层,全局通用,默认为true
                //dialogDraggable : false,    // 设置弹出层对话框不可拖动,全局通用,默认为true
                //dialogMaskOpacity : 0.4,    // 设置透明遮罩层的透明度,全局通用,默认值为0.1
                //dialogMaskBgColor : "#000", // 设置透明遮罩层的背景颜色,全局通用,默认为#fff
                imageUpload : true,
                imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
//              imageUploadURL : "../admin/mdEditor/examples/php/upload.php", // 使用emEditor的上传范例
                imageUploadURL : "<?php $security->index('/action/upload'. (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>",// 使用Typecho自带上传功能 !!推荐
                onload : function() { 
                // 下面是接口范例
                    console.log('onload', this);
                    //this.fullscreen();
                    //this.unwatch();
                    //this.watch().fullscreen();

                    //this.setMarkdown("#PHP");
                    //this.width("100%");
                    //this.height(480);
                    //this.resize("100%", 640);
                }
            });
        });
    </script>

给editor.md添加自定义按钮 ( 分隔符 ) 20190822更新

突然发现没有分割符的按钮了,虽然手动写<!--more--> 还是比较简单的,不过按钮缺失是不完美的,所以手动添加一下。 基本上要涉及到修改editor.md的源码了,找到editormd.js

我选择在 hr 后面添加 more, 后面还要为他添加一个按钮. 由于md是基于fontawesome来绘制图标,所以我们要去 图标在线查询 选择一个合适的。我选择剪刀来表示裁切。

在editormd.css中我们找到了对应的版本信息,所以我们在不升级的情况下只能选择4.3或以前版本的图标。

@font-face {
  font-family: 'FontAwesome';
  src: url("../fonts/fontawesome-webfont.eot?v=4.3.0");
  src: url("../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0") format("embedded-opentype"), url("../fonts/fontawesome-webfont.woff2?v=4.3.0") format("woff2"), url("../fonts/fontawesome-webfont.woff?v=4.3.0") format("woff"), url("../fonts/fontawesome-webfont.ttf?v=4.3.0") format("truetype"), url("../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular") format("svg");
  font-weight: normal;
  font-style: normal;
}

添加好 按钮索引、图标、中文描述

    editormd.toolbarModes = {
        full : [
            "list-ul", "list-ol", "hr","more", "|",
        ],
        simple : [
            "list-ul", "list-ol", "hr","more", "|",
        ],
    };
    ...
    toolbarIconsClass    : {
            "list-ol"        : "fa-list-ol",
            hr               : "fa-minus",
            more             : "fa-scissors",
        },

        lang : {
            name        : "zh-cn",
            toolbar     : {
                "list-ol"        : "有序列表",
                hr               : "横线",
                more             : "分隔符",
            },

editormd.js中添加对应的分隔符函数(由于插入分隔符和插入hr很类似 所以直接复制修改就行了)


        hr : function() {
            var cm        = this.cm;
            var cursor    = cm.getCursor();
            var selection = cm.getSelection();

            cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n");
        },

        more : function(){
            var cm        = this.cm;
            var cursor    = cm.getCursor();
            var selection = cm.getSelection();
            cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "\n\n<!--more-->\n\n");
        },

完成,清缓存刷新。


视觉美化

这个部分能做的事情就比较多了,主要可以在header,footer等页面进行自定义的添加。如果你的编程能力不错的话,可以引入各种你写的逻辑和文件。 目前还没有太多的兴致做视觉方面的优化,先简单的对网站标题文字做了一个古风的美化。 可以选择的方式有 1字蛛 2有字库 我选用了 有字库,主要是使用在线cdn比较方便,可以轻松的换字体 感兴趣的可以点击进入官方自行了解。

标签: typecho, 技术博客, cms美化, 极简博客, 代码高亮, markdown链接跳转, 阿里云ecs

添加新评论