使用Typecho搭建一个极简又好用的技术博客
总体来说 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
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即可。
上传文件:
替换解析语句:
<?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 目录,然后将的下载好的文件全部先上传到这个目录中,如图。
本地打开 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.php
和 write-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搭建一个极简又好用的技术博客 - Sprite keep learning
可以问一下吗这个里面的“mdEditor/editor.php”editor.php文件怎么写啊,文中没有说到
Hi 朋友,你说的editor.php有贴出完整的代码哦。
"给editor.md添加自定义按钮" 文字上方的一段就是完整代码。
我为啥修改了编辑器不能使用了
这需要你自己查验一下是否有编写错误,建议你完整的参照一下md编辑器这部分的全部过程,循序渐进的修改。 不要简单的复制某一段代码,看一下是不是缺了文件,路径是否正确,从开发者工具中看一下是否有前台报错,打开PHP的错误输出。
你好,博主。。是否可以把编辑器这块的修改做一个压缩包以供下载了?
可以的,不过没有时间写插件。需要自己放到对应目录里。
我回看了一下,好像没办法直接一个压缩包解决问题,涉及到需要修改的文件是比较多的,分布在不同部分。
编辑器的文件包: https://shezw.com/usr/uploads/2020/07/editor.zip
需要替换admin/目录下的 write-post.php, mdEditor.php ,如果你没有改过的话直接替换,自己改过的对比之后再换哦。 窗口大小需要的话,按照上面写的自己改一下。
特别需要注意以下 ../admin/mdEditor/examples/php/upload.php这个文件,里面第25行,改成你自己的网站目录。
收到。谢谢博主
Hi Sprite
你上文中有如下内容: 代码高亮 II 替换后台的 Markdown 编辑器 (20190819 更新) 因为是技术博客,所以不断的会贴入新的 code,慢慢的发现有的时候 代码高亮不能正确工作,主要是不能正确匹配编程语言。 较新的 Markdown 解析实际上是可以指定语言的,很幸运的是,经过我的测试,上面的 Parsedown 就可以完成指定语言解析的任务。
我现在遇到了这样的问题,我常常会需要使用DAX语言,Prism.js是有这个预言的,并在Prism的网站上测试,是可以正确显示语法高亮的。但是如同你上文中所说,我想你是对的,我的Blog(Typecho搭建)中按照你的教程替换了解析器为Parsedown,依然无法实现DAX语言的语法高亮。
而你在文中提到,Parsedown可以完成指定语言解析任务,所以我想请问,具体应该如何实现,从而让Prism能正确显示的语言,在Typecho上也可以正确显示语法高亮。 希望得到你的回复。
Hi Rocky 代码高亮是前端的功能,你用的Prism.js 和我这里用的Hightlight.js是一样的。
所以我推测你是在前端部署Prism.js时出现了问题。
建议你检查一下,在浏览器开发者模式下,你的代码高亮部分是否具有 class="language-dax" 这样的样式,如果已经有了,那证明后台的MD解析器已经正常工作,那么你需要去正确部署Prism.js。
Hi Sprite 按照你说的方式,我发现问题并不是Dax无法高亮,而是当我Login之后,代码高亮有效,当我Logout之后,代码高亮就无效了。
不知道针对这样Logout,代码高亮失效,是因为代码放错位置么? 我尝试了放在其他页面,似乎也都无法解决这个问题。 不知道你这边是否了解,具体应该吧js放在哪个位置。
当我Logout之后,发现代码部分的网页源码变成下面这样了,似乎Logout之后,转为调用EnlighterJSRAW。我不了解这是什么情况。 ————————————
FODOfCustomer = CALCULATE ( MIN ( 'ECDatas SSDTotal'[BillingDate] ), ALLEXCEPT( 'ECDatas SSDTotal', 'ECDatas SSDTotal'[Customer] ) ) ————————————而Login状态,代码部分的页面源代码部分如下:
FODOfCustomer = CALCULATE ( MIN ( 'ECDatas SSDTotal'[BillingDate] ), ALLEXCEPT( 'ECDatas SSDTotal', 'ECDatas SSDTotal'[Customer] ) )SNVOfNewCustomer = CALCULATE ( [SumNetValue],FILTER('ECDatas SSDTotal' , YEAR( [FODOfCustomer] ) = 2019 ) )
Logout
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" ………… ————————
Login
< pre class=" language-dax">FODOfCustomer =按你所说的应该是在后台写文章的时候是有效的,但是前台看文章的时候就无效了吗? 一般来说 登录是不影响这个功能的。 方便的化把你的链接发给我看一下吧。
https://www.bilibili.com/video/BV14a4y1H7oY/ 我刚上传了一个视频说明怎么添加代码高亮,应该过一会能过审,你可以参考一下。
https://fyxsky.xyz/allexcept-%E8%A7%A3%E9%87%8A.html 哈哈,谢谢你,不过我昨天改了 JS 添加的页面,现在已经好了。 之前我添加在footer.php,昨天我改为添加在 header.php 的 之前了。 我不确定是不是模板的原因。
Hi Rocky, 我看了一下你的网站,恭喜你可以正常使用代码高亮了。 不过我看到你把 网页的<head></head> 和 网页主体内容中的 <header></header> 混淆了,虽然他们是挺容易混淆的。 head 是网页的不可见内容, header 是可见内容,和 div,p,span之类的标签一样,没什么特别。 一般来说建议是把 样式表放在 head部分,js理论上可以放在任何位置,不过越靠前,他越会阻塞浏览器渲染,所以一般都放到body之后(也就是约定俗称的footer,而不是标签)
当然这些修改不是必须的