用nodejs的原生fs文件模块,去实现html文件,引入公共文件处理工具。assets资源文件夹存放公共文件,pages存放页面文件,页面文件里面会引用公共文件。common公共文件夹里面存放公共文件。实现了监听页面文件的监听,当页面文件发生变化时,会重新编译页面文件。资源文件改变更新时,重新拷贝资源包到dist目录。
源码下载:https://gitee.com/leolee18/html-template/
目录结构
src是源码目录,包含了pages目录,其下是所有的页面文件,里面可能会引用公共文件。common是公共文件目录,包含了header.html和footer.html等文件。 dist是资源目录,用来存放页面include标签被公共文件替换后的实际使用的页面文件资源。
dist
include.js
src
common
footer.html
header.html
assets
bg.jpg
pages
index.html
index.html文件内容
<!DOCTYPE HTML>
<html>
<head>
<title>nodejs 制作html静态公共文件包含工具</title>
<meta charset="utf8" />
<style>
html,body,div{padding:0;border:0;margin:0;box-sizing:border-box;}
html,body{height:100%;}
body{display:flex;flex-direction:column;}
.center-middle{display:flex;justify-content:center;align-items:center;}
.header{flex:0 0 50px;border-bottom:1px solid #ddd;background-color:#95cfd4;}
.bodyer{flex:1 1 100%;background-color:#ececec;}
.footer{flex:0 0 50px;border-top:1px solid #ddd;background-color:#7def40;}
</style>
</head>
<body>
<include src="./header.html"></include>
<div class="bodyer center-middle">bodyer</div>
<include src="./footer.html"></include>
</body>
</html>
header.html文件内容
<div class="header center-middle">header</div>
footer.html文件内容
<div class="footer center-middle">footer</div>
include.js文件代码
- 用nodejs的fs模块的readdirSync同步文件读取函数读取pages目录下的所有文件。
- 遍历每个文件,正则匹配语句,读取包含于其中的src属性指定的文件路径,并用此文件内容替换掉当前所匹配的include标签。
- 将替换掉include标签后的新的文件内容,写入到dist资源目录,文件名跟当前pages下的文件名同名。
const $Fs = require('fs'); // 引入文件模块
let files = $Fs.readdirSync('./src/pages'); // 读取pages页面文件目录下的html文件
files.forEach((file,index) => { // 遍历获得的所有页面文件
let old_data = $Fs.readFileSync('./src/pages/' + file,'utf8'); // 读取当前文件的内容
let new_data = old_data.replace(/\<include.*src="(.*)">.*\<\/include\>/gi,(match,p1,offset,string) => {//查找html文件中include标签
let inc_data = $Fs.readFileSync('./src/common/' + p1,'utf8'); // p1是第一个括号匹配到的值,也就是include包含的文件路径,读取此公共文件
return inc_data; // 此回调函数返回的值,将替换正则匹配到的字符串
});
$Fs.writeFileSync('./dist/' + file,new_data); //将include标签被替换后新的html内容,写入到dist资源目录的同名file文件中(不存在会自动新建)。
});
console.log('执行成功');
node执行include.js文件
node include.js
监控文件系统变动
npm install chokidar
const chokidar = require('chokidar');
// 监听指定目录下的所有文件和子目录
const watcher = chokidar.watch('/path/to/directory', {
ignored: /(^|[\/\\])\../, // 忽略以 . 开头的文件和目录
persistent: true // 长期运行
});
watcher.on('add', path => console.log(`File ${path} has been added`));
watcher.on('change', path => console.log(`File ${path} has been changed`));
watcher.on('unlink', path => console.log(`File ${path} has been removed`));
node 移动静态文件夹
const fs = require('fs');
const path = require('path');
// 源文件夹路径
const sourceDir = path.join(__dirname, 'static');
// 目标文件夹路径
const targetDir = path.join(__dirname, 'public', 'static');
// 移动文件夹的函数
function moveFolder(source, destination) {
fs.rename(source, destination, (err) => {
if (err) throw err;
console.log('文件夹移动成功!');
});
}
// 调用函数移动文件夹
moveFolder(sourceDir, targetDir);
静态html页面服务器 http-server
npm i -g http-server
// http-server [path] [options]
//path默认指向工程路径下的./public,如果不存在那么使用./。
//options就是常见的配置,比如端口、代理地址等,常用配置
http-server ./ -o --port 8085 --proxy http://192.168.11.120:8888/
hs ./ -o -p 8085 -P http://192.168.11.120:8888/
"scripts": {
"dev": "http-server ./ -o --port 8089 --proxy http://192.168.11.120:8888/"
}
live-server
live-server服务器(与http-server一样,但多了代码热更新功能,只要修改了代码,不需要刷新浏览器,浏览器会自动热更新)
npm i live-server -g
// 使用方法与http-server一致
live-server --port=80
package.json
{
"name": "html",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "http-server ./dist -o --port 8085 | node index.js",
"build": "node index.js",
"live": "live-server ./dist --port=8080 | node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chokidar": "^3.6.0",
"http-server": "^14.1.1"
}
}
index.js
const chokidar = require('chokidar');
const fs = require('fs'); // 引入文件模块
const path = require('path');
const mPath = './src/pages/';
const mCom = './src/common/';
const mDist = './dist/';
const mAsstes = './src/assets';
// 监听页面目录
const watcher = chokidar.watch(mPath, {
ignored: /(^|[\/\\])\../, // 忽略以 . 开头的文件和目录
persistent: true // 长期运行
});
// 监听 资源目录
const watcherAss = chokidar.watch(mAsstes, {
ignored: /(^|[\/\\])\../, // 忽略以 . 开头的文件和目录
persistent: true // 长期运行
});
// 更新页面文件
function updatePage(params) {
const fileName = path.basename(params);
const pagePath = path.join(mPath, fileName);
let old_data = fs.readFileSync(pagePath, 'utf8');
let new_data = old_data.replace(/\<include.*src="(.*)">.*\<\/include\>/gi, (match, p1, offset, string) => {
const comPath = path.join(mCom, p1);
let inc_data = fs.readFileSync(comPath, 'utf8');
return inc_data;
});
const disPath = path.join(mDist, fileName);
fs.writeFileSync(disPath, new_data);
}
//移动资源文件夹
function moveFolder() {
const mAsName = path.basename(mAsstes);
fs.cp(mAsstes, (mDist + mAsName), { recursive: true }, (err) => {
if (err) {
// console.error(err);
}
});
}
//清空dist文件夹
function deleteFolderRecursive(mPath) {
const folderPath = path.join(mPath);
if (fs.existsSync(folderPath)) {
// 递归删除文件夹
fs.rmdirSync(folderPath, { recursive: true }, (error) => {
if (error) {
// return console.error(`无法删除文件夹: ${error}`);
}
});
}
}
// 删除文件
function deleteFile(params) {
const fileName = path.basename(params);
const pagePath = path.join(mDist, fileName);
if (fs.existsSync(pagePath)) {
fs.unlinkSync(pagePath);
}
}
// 创建目录
function mkdir(filePath) {
const arr = filePath.split('/');
let dir = arr[0];
for (let i = 1; i < arr.length; i++) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
dir = dir + '/' + arr[i];
}
}
// 编译所有页面
function updatePageAll() {
// 删除文件夹下的所有文件和文件夹
deleteFolderRecursive(mDist);
if (!fs.existsSync(mDist)) {
mkdir(mDist);
}
let files = fs.readdirSync(mPath); // 读取pages页面文件目录下的html文件
files.forEach((file, index) => { // 遍历获得的所有页面文件
let old_data = fs.readFileSync(mPath + file, 'utf8'); // 读取当前文件的内容
let new_data = old_data.replace(/\<include.*src="(.*)">.*\<\/include\>/gi, (match, p1, offset, string) => {//查找html文件中include标签
let inc_data = fs.readFileSync(mCom + p1, 'utf8'); // p1是第一个括号匹配到的值,也就是include包含的文件路径,读取此公共文件
return inc_data; // 此回调函数返回的值,将替换正则匹配到的字符串
});
fs.writeFileSync(mDist + file, new_data); //将include标签被替换后新的html内容,写入到dist资源目录的同名file文件中(不存在会自动新建)。
});
// 移动资源文件夹
moveFolder();
}
// 防抖函数
function debounce(fn, t, path) {
let timer;
return function () {
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
fn(path);
}, t);
}
}
// 监听页面改变
const mCatch = {};
let mAllFile;
watcher.on('add', path => {
if (!mAllFile) {
mAllFile = debounce(updatePageAll, 1000, path);
}
mAllFile();
// console.log(`File ${path} has been added`)
});
watcher.on('change', path => {
if (!mCatch[path]) {
mCatch[path] = debounce(updatePage, 1000, path);
}
mCatch[path]();
// console.log(`File ${path} has been changed`)
});
watcher.on('unlink', path => {
deleteFile(path);
// console.log(`File ${path} has been removed`)
});
//监听资源改变
let assFile;
watcherAss.on('change', path => {
if (!assFile) {
assFile = debounce(moveFolder, 1000, path);
}
assFile();
});
watcherAss.on('unlink', path => {
if (!assFile) {
assFile = debounce(moveFolder, 1000, path);
}
assFile();
});
扩展
Node.js + gulp 合并静态页模版,文件更新自动热重载。浏览器可预览目录下的页面。使用的是ejs的语法.其实你用什么文件后缀都可以,都是按ejs来解析
// 项目地址:https://gitcode.com/Liaozhenting/template/overview