前言

这几周不加班,早早到家,闲来无事在 NAS 服务器上搭建了个 Jenkins,用了几天后觉得自带的主题有点单调。

Them Plguin

需要安装 Simple Theme Plugin 和 Login Theme Plugin。

怎么安装和使用就不说明了,参考官方最新的文档更为靠谱。

主要他们提供了加载 css 和 js 的配置。

本地 Web 服务

本地开发时为了方便查看效果,我用 NodeJS 的 Koa 在本地启动了一个 Web 服务,让 Theme Plugin 读取我本地的 css 和 js 文件。

这样修改完 css 或 js 文件后,刷新页面即可看到效果,而不需要每次把 css 和 js 的文件内容配置到 Theme Plugin 中。

1
2
3
4
5
6
7
8
const Koa = require("koa");
const serve = require("koa-static-server");

const port = 4600;
const app = websockify(new Koa());

app.use(serve({ rootDir: "dist", rootPath: "/" }));
app.listen(port);

引入 Less

写了几句 css 后,发现没嵌套功能写的真难受,于是引入 Less,方便编写 css。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const less = require("less");

const renderLess = async (lessFilePath) => {
let lessFileContent = fs.readFileSync(lessFilePath, { encoding: "utf-8" });
let lessRenderRes = await less.render(lessFileContent);

let targetPath = path.join(
__dirname,
"../dist",
lessFilePath.substring("src/".length, lessFilePath.length)
);
let targetDir = path.dirname(targetPath);
let targetName = path.basename(targetPath, ".less") + ".css";
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
targetPath = path.join(targetDir, targetName);
fs.writeFileSync(targetPath, lessRenderRes.css, { encoding: "utf-8" });
return { path: targetPath.replace(path.join(__dirname, "../dist"), "") };
};

监听文件变化

既然都引入了 Less,那么肯定要引入 自动编译,同时为了后续热更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const glob = require("glob");
const chokidar = require("chokidar");

const watchAndRenderLess = () => {
glob("src/**/*.less", {}, function (er, files) {
files.forEach(renderLess);
});
const watcher = chokidar.watch("src/**/*.less");
watcher.on("all", async (e, filePath, stats) => {
if (e == "add" || e == "change" || e == "addDir") {
let result = await renderLess(filePath);
console.log(`${url}${result.path}`);
}
});
};

watchAndRenderLess();

现在就可以愉快的使用 Less 了。

热更新

每次编写样式后,都需要手动刷新页面才能看到效果,能不能自动热更新页面,不需要手动刷新页面呢?

使用三方库虽然方便,但自己尝试写一下也是不错的选择。

主要就是监听样式文件变化,然后通过 webScoket 通知页面,页面再加载新的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const websockify = require("koa-websocket");

const port = 4600;
const url = `http://localhost:${port}`;
const clients = new Set();

app.ws.use(function (ctx, next) {
clients.add(ctx.websocket);
ctx.websocket.onclose = () => {
clients.delete(ctx.websocket);
};
return next(ctx);
});

const watchAndRenderLess = () => {
// ...
const watcher = chokidar.watch("src/**/*.less");
watcher.on("all", async (e, filePath, stats) => {
if (e == "add" || e == "change" || e == "addDir") {
// ...
refresh(result.path);
}
});
};

const renderClientJs = () => {
let content = fs.readFileSync(path.join(__dirname, "client.js"), {
encoding: "utf-8",
});
content = content.replace(/\$\[port\]/g, port);
fs.writeFileSync(path.join(__dirname, "../dist", "client.js"), content, {
encoding: "utf-8",
});
};

const refresh = (filePath) => {
clients.forEach((client) => {
let data = { type: "refresh", path: filePath };
client.send(JSON.stringify(data));
});
};

renderClientJs();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// client.js
(function () {
// 此文件用于修改主题样式文件时,热更新样式用的。

let host = "localhost:$[port]";
let files = new Map();

let findTargetCss = () => {
let links = [...document.querySelectorAll('link[rel="stylesheet"]')];
for (const link of links) {
let url = new URL(link.href);
if (url.host === host) {
files.set(url.pathname, link);
}
}
};

findTargetCss();

let ws = new WebSocket(`ws://${host}/`);
ws.onmessage = (e) => {
console.log("message", e);
let data = JSON.parse(e.data);
if (data.type == "refresh") {
if (files.has(data.path)) {
let node = files.get(data.path);
let url = new URL(node.href);
let qs = new URLSearchParams(url.search);
qs.set("t", new Date().getTime());
url.search = qs.toString();
node.href = url.toString();
}
}
};
ws.onopen = (e) => {
console.log("open", e);
ws.send("hello world");
};
})();

通过 Theme Plugin 再把这个 client.js 引入,就可以实现热更新了。

上面的代码仅是 demo,为了快速解决问题随手写的。

主题效果

背景使用了 Bing 的今日壁纸,每天都会看到不一样的背景,整体是半透明的风格,整体颜色风格会受到背景颜色的影响,这样每天都可以感受到不一样的效果了。

下面图片是经过压缩的,背景效果有点模糊,没真实的看着丝滑。