Hexo 本身支持注入功能,可以实现无侵入式调整博客布局。Fluid 支持更优化的注入功能,本文记录 Fluid 代码注入的使用方法,并将背景视频功能转为注入实现。
简介
什么是代码注入
- 在项目之外将需要修改的代码动态插入到项目中的技术手段
为什么需要代码注入
- 是的,直接修改源码是完全可以达到目的的,但是源码修改会破坏仓库的代码完整性,问题主要出现在需要对仓库进行更新的时候
- 修改过的仓库很容易在更新时引入冲突,那时候很可能需要面对自己都不记得为什么改的代码和完全不懂的项目代码做出取舍,实在是很危险、痛苦而且不优雅的
- 也就是说,我们又要调整项目代码功能,又要保持项目足够“干净”,以便享受将来的更新,此时代码注入的价值便显现出来了
注入代码
Hexo 注入
- Hexo 注入器 是 Hexo 5 版本自身加入的一项新功能,所以在所有 Hexo 主题都是支持这个功能的。
- 注入器可以将 HTML 片段注入生成页面的
<head>
和<body>
节点中。 - 编写注入代码,需要在博客的根目录下创建
scripts
文件夹,然后在里面任意命名创建一个 js 文件即可。
代码语言:javascript复制例如创建一个
/blog/scripts/example.js
,内容为:
hexo.extend.injector.register('body_end', '<script src="/jquery.js"></script>', 'default');
上述代码会在生成的页面
</body>
注入加载jquery.js
的代码。
register
函数可接受三个参数,第一个参数是代码片段注入的位置,接受以下值:
参数 | 含义 |
---|---|
head_begin | 注入在 <head> 之后(默认) |
head_end | 注入在 </head> 之前 |
body_begin | 注入在 <body> 之后 |
body_end | 注入在 </body> 之前 |
- 第二个参数是注入的片段,可以是字符串,也可以是一个返回值为字符串的函数。
- 第三个参数是注入的页面类型,接受以下值:
参数 | 含义 |
---|---|
default | 注入到每个页面(默认值) |
home | 只注入到主页(is_home() 为 true 的页面) |
post | 只注入到文章页面(is_post() 为 true 的页面) |
page | 只注入到独立页面(is_page() 为 true 的页面) |
archive | 只注入到归档页面(is_archive() 为 true 的页面) |
category | 只注入到分类页面(is_category() 为 true 的页面) |
tag | 只注入到标签页面(is_tag() 为 true 的页面) |
或是其他自定义 layout 名称,例如在Fluid 主题中
about
对应关于页、links
对应友联页
Fluid 注入
- Fluid 主题也提供了一套注入代码功能,相较于 Hexo 注入功能更细致更丰富,并且支持注入
ejs
代码。 - 如果你想充分修改主题,又不想直接修改源码影响日后更新,本主题提供了代码注入功能,可以将代码无侵入式加入到主题里。
- 你可以直接注入 HTML 片段,不过建议你了解一下 EJS 模板引擎,这样你就可以像主题里的
ejs
文件一样编写自己的组件再注入进去。 - 进入博客目录下
scripts
文件夹(如不存在则创建),在里面创建任意名称的 js 文件,在文件中写入如下内容:
1234 | hexo.extend.filter.register('theme_inject', function(injects) { injects.header.file('default', 'source/_inject/test1.ejs', { key: 'value' }, -1); injects.footer.raw('default', '<script async src="https://xxxxxx" crossorigin="anonymous"></script>');}); |
---|
header
和footer
是注入点的名称,表示代码注入到页面的什么位置;file
方法表示注入的是文件,第一个参数下面介绍,第二个参数则是文件的路径,第三个参数是传入文件的参数(可省略),第四个参数是顺序(可省略);raw
方法表示注入的是原生代码,第一个参数下面介绍,第二个参数则是一句原生的 HTML 语句;default
表示注入的键名,可以使用任意键名,同一个注入点下的相同键名会使注入的内容覆盖,而不同键名则会让内容依次排列(默认按执行先后顺序,可通过file
第四个参数指定),这里 default 为主题默认键名,通常会替换掉主题默认的组件;- 主题目前提供的注入点如下:
注入点名称 | 注入范围 | 存在 default 键 |
---|---|---|
head | <head> 标签中的结尾 | 无 |
header | <header> 标签中所有内容 | 有 |
bodyBegin | <body> 标签中的开始 | 无 |
bodyEnd | <body> 标签中的结尾 | 无 |
footer | <footer> 标签中所有内容 | 有 |
postMetaTop | 文章页 <header> 标签中 meta 部分内容 | 有 |
postMetaBottom | 文章页底部 meta 部分内容 | 有 |
postMarkdownBegin | <div class="markdown-body"> 标签中的开始 | 无 |
postMarkdownEnd | <div class="markdown-body"> 标签中的结尾 | 无 |
postLeft | 文章页左侧边栏 | 有 |
postRight | 文章页右侧边栏 | 有 |
postCopyright | 文章页版权信息 | 有 |
postRight | 文章页右侧边栏 | 无 |
postComments | 文章页评论 | 有 |
pageComments | 自定义页评论 | 有 |
linksComments | 友链页评论 | 有 |
动态视频背景注入实现
背景
- 为了实现动视频态博客背景,之前记录过经验 Fluid -2- 随机视频背景切换 , Fluid -11- 封面视频背景顺滑加载 , Fluid -13- 视频背景 fixed 成功实现对 fluid 主题的侵入式修改
- 但在升级 fluid 1.9 的时候侵入修改的代价来了,由于主题升级了大版本,代码冲突太多,已经无从维护,现依赖代码注入功能重新实现上述功能
思路
- fluid 的注入更加灵活,此处选择 fluid 的注入方式
- 按照流程创建注入环境
- 创建注入代码文件
- 将文件嵌入到
header
和bodybegin
- 相应修改主题配置
实现
- 在博客根目录创建
scripts
文件夹,在其中创建page.js
文件,内容为:
hexo.extend.filter.register('theme_inject', function(injects) {
injects.header.file('default', 'source/_inject/test1.ejs', { key: 'value' }, -1);
injects.footer.raw('default', '<script async src="https://xxxxxx" crossorigin="anonymous"></script>');
});
将两个文件嵌入到指定位置,由于 bodybegin 是空的,这里采用覆盖 default 的方式 现在创建注入的两个文件,创建
source/_inject
文件夹,在其中创建文件header.ejs
和bodyBegin.ejs
文件
bodyBegin.ejs
<div>
<div class='real_mask' style="
background-color: rgba(0,0,0,0.3);
width: 100%;
height: 100%;
position: fixed;
z-index: -777;
"></div>
<div id="banner_video_insert">
</div>
<div id='vvd_banner_img'>
</div>
</div>
<div id="banner"></div>
header.js
<%
var banner_video = theme.index.banner_video
var banner_img = page.banner_img || theme.index.banner_img
var banner_img_height = page.banner_img_height || theme.index.banner_img_height
var banner_mask_alpha = page.banner_mask_alpha || theme.index.banner_mask_alpha
%>
<script type="text/javascript" src="/vvd_js/jquery.js"></script>
<div class="banner" id='banner' >
<div class="full-bg-img" >
<% if(banner_video){ %>
<script>
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OSs([d_] )/),
isIphone = !ipad && ua.match(/(iPhonesOS)s([d_] )/),
isAndroid = ua.match(/(Android)s ([d.] )/),
isMobile = isIphone || isAndroid;
function set_video_attr(id){
var height = document.body.clientHeight
var width = document.body.clientWidth
var video_item = document.getElementById(id);
if (height / width < 0.56){
video_item.setAttribute('width', '100%');
video_item.setAttribute('height', 'auto');
} else {
video_item.setAttribute('height', '100%');
video_item.setAttribute('width', 'auto');
}
}
$.getJSON('/vvd_js/video_url.json', function(data){
if (true){
var video_list_length = data.length
var seed = Math.random()
index = Math.floor(seed * video_list_length)
video_url = data[index][0]
pre_show_image_url = data[index][1]
banner_obj = document.getElementById("banner")
banner_obj.style.cssText = "background: url('" pre_show_image_url "') no-repeat; background-size: cover;"
vvd_banner_obj = document.getElementById("vvd_banner_img")
vvd_banner_content = "<img id='banner_img_item' src='" pre_show_image_url "' style='height: 100%; position: fixed; z-index: -999'>"
vvd_banner_obj.innerHTML = vvd_banner_content
set_video_attr('banner_img_item')
if (!isMobile) {
video_html_res = "<video id='video_item' style='position: fixed; z-index: -888;' muted='muted' src=" video_url " autoplay='autoplay' loop='loop'></video>"
document.getElementById("banner_video_insert").innerHTML = video_html_res;
set_video_attr('video_item')
}
}
});
if (!isMobile){
window.onresize = function(){
set_video_attr('video_item')
}
}
</script>
<% } %>
</div>
</div>
</div>
- 修改主题配置文件
_config.fluid.yml
1. 覆盖默认 banner 图为纯透明的 png 图像将所有的
代码语言:javascript复制#---------------------------
# 首页
# Home Page
#---------------------------
index:
# 首页 Banner 头图,可以是相对路径或绝对路径,以下相同
# Path of Banner image, can be a relative path or an absolute path, the same on other pages
banner_img: https://img.yuanmabao.com/zijie/pic/2022/08/06/e4b2f1zpkxt.png
# 首页 Banner 使用随机视频
# true 开启 false 关闭
banner_video: true
代码语言:txt复制1. 将所有 `banner_mask_alpha` 设置为 0其余的如
参考资料
- https://hexo.fluid-dev.com/docs/advance/