mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
feat(performance): 优化性能并添加离线支持
- 新增 .babelrc 配置文件,使用 Babel 进行代码转换和压缩 - 添加 PERFORMANCE.md 文件,详细说明性能优化措施 - 在 README.md 中增加性能优化相关说明 - 更新 index.html,添加 Service Worker 注册和性能优化脚本
This commit is contained in:
159
scripts/compress.js
Normal file
159
scripts/compress.js
Normal file
@@ -0,0 +1,159 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const gzip = promisify(zlib.gzip);
|
||||
const brotliCompress = promisify(zlib.brotliCompress);
|
||||
|
||||
// 压缩配置
|
||||
const compressionOptions = {
|
||||
gzip: {
|
||||
level: 9,
|
||||
memLevel: 9
|
||||
},
|
||||
brotli: {
|
||||
params: {
|
||||
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
|
||||
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_GENERIC,
|
||||
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 需要压缩的文件类型
|
||||
const compressibleExtensions = ['.js', '.css', '.html', '.json', '.xml', '.svg'];
|
||||
|
||||
// 压缩单个文件
|
||||
async function compressFile(filePath) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath);
|
||||
const ext = path.extname(filePath);
|
||||
|
||||
// 只压缩特定类型的文件
|
||||
if (!compressibleExtensions.includes(ext)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileName = path.basename(filePath);
|
||||
const dir = path.dirname(filePath);
|
||||
|
||||
// Gzip压缩
|
||||
const gzipped = await gzip(content, compressionOptions.gzip);
|
||||
const gzipPath = path.join(dir, `${fileName}.gz`);
|
||||
fs.writeFileSync(gzipPath, gzipped);
|
||||
|
||||
// Brotli压缩(如果支持)
|
||||
try {
|
||||
const brotlied = await brotliCompress(content, compressionOptions.brotli);
|
||||
const brotliPath = path.join(dir, `${fileName}.br`);
|
||||
fs.writeFileSync(brotliPath, brotlied);
|
||||
} catch (error) {
|
||||
console.log(`Brotli压缩失败 ${fileName}:`, error.message);
|
||||
}
|
||||
|
||||
const originalSize = content.length;
|
||||
const gzipSize = gzipped.length;
|
||||
const compressionRatio = ((originalSize - gzipSize) / originalSize * 100).toFixed(1);
|
||||
|
||||
return {
|
||||
file: fileName,
|
||||
original: originalSize,
|
||||
gzip: gzipSize,
|
||||
ratio: compressionRatio
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`压缩文件失败 ${filePath}:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归压缩目录
|
||||
async function compressDirectory(dirPath) {
|
||||
const results = [];
|
||||
|
||||
async function scanDirectory(dir) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
await scanDirectory(fullPath);
|
||||
} else if (stat.isFile()) {
|
||||
const result = await compressFile(fullPath);
|
||||
if (result) {
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await scanDirectory(dirPath);
|
||||
return results;
|
||||
}
|
||||
|
||||
// 主压缩函数
|
||||
async function compressBuild() {
|
||||
const distDir = path.join(__dirname, '../dist');
|
||||
|
||||
if (!fs.existsSync(distDir)) {
|
||||
console.error('dist目录不存在,请先运行构建命令');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('开始压缩构建文件...');
|
||||
|
||||
try {
|
||||
const results = await compressDirectory(distDir);
|
||||
|
||||
if (results.length === 0) {
|
||||
console.log('没有找到需要压缩的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示压缩结果
|
||||
console.log('\n压缩结果:');
|
||||
console.log('文件名'.padEnd(30) + '原始大小'.padEnd(12) + 'Gzip大小'.padEnd(12) + '压缩率');
|
||||
console.log('-'.repeat(70));
|
||||
|
||||
let totalOriginal = 0;
|
||||
let totalGzip = 0;
|
||||
|
||||
results.forEach(result => {
|
||||
totalOriginal += result.original;
|
||||
totalGzip += result.gzip;
|
||||
|
||||
const originalKB = (result.original / 1024).toFixed(1);
|
||||
const gzipKB = (result.gzip / 1024).toFixed(1);
|
||||
|
||||
console.log(
|
||||
result.file.padEnd(30) +
|
||||
`${originalKB}KB`.padEnd(12) +
|
||||
`${gzipKB}KB`.padEnd(12) +
|
||||
`${result.ratio}%`
|
||||
);
|
||||
});
|
||||
|
||||
const totalRatio = ((totalOriginal - totalGzip) / totalOriginal * 100).toFixed(1);
|
||||
console.log('-'.repeat(70));
|
||||
console.log(
|
||||
'总计'.padEnd(30) +
|
||||
`${(totalOriginal / 1024).toFixed(1)}KB`.padEnd(12) +
|
||||
`${(totalGzip / 1024).toFixed(1)}KB`.padEnd(12) +
|
||||
`${totalRatio}%`
|
||||
);
|
||||
|
||||
console.log('\n压缩完成!');
|
||||
} catch (error) {
|
||||
console.error('压缩失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
compressBuild();
|
||||
}
|
||||
|
||||
module.exports = { compressBuild, compressFile };
|
||||
122
scripts/optimize-images.js
Normal file
122
scripts/optimize-images.js
Normal file
@@ -0,0 +1,122 @@
|
||||
const imagemin = require('imagemin');
|
||||
const imageminWebp = require('imagemin-webp');
|
||||
const imageminMozjpeg = require('imagemin-mozjpeg');
|
||||
const imageminPngquant = require('imagemin-pngquant');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 图片优化配置
|
||||
const webpOptions = {
|
||||
quality: 85,
|
||||
method: 6,
|
||||
autoFilter: true,
|
||||
filter: 0.8
|
||||
};
|
||||
|
||||
const jpegOptions = {
|
||||
quality: 85,
|
||||
progressive: true,
|
||||
smooth: 1
|
||||
};
|
||||
|
||||
const pngOptions = {
|
||||
quality: [0.6, 0.8],
|
||||
speed: 4
|
||||
};
|
||||
|
||||
// 优化函数
|
||||
async function optimizeImages() {
|
||||
const srcDir = path.join(__dirname, '../src/images');
|
||||
const distDir = path.join(__dirname, '../dist/images');
|
||||
|
||||
// 确保目标目录存在
|
||||
if (!fs.existsSync(distDir)) {
|
||||
fs.mkdirSync(distDir, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('开始优化图片...');
|
||||
|
||||
// 优化为WebP格式
|
||||
const webpFiles = await imagemin([`${srcDir}/*.{jpg,jpeg,png}`], {
|
||||
destination: distDir,
|
||||
plugins: [
|
||||
imageminWebp(webpOptions)
|
||||
]
|
||||
});
|
||||
|
||||
console.log(`WebP优化完成: ${webpFiles.length} 个文件`);
|
||||
|
||||
// 优化JPEG文件
|
||||
const jpegFiles = await imagemin([`${srcDir}/*.{jpg,jpeg}`], {
|
||||
destination: distDir,
|
||||
plugins: [
|
||||
imageminMozjpeg(jpegOptions)
|
||||
]
|
||||
});
|
||||
|
||||
console.log(`JPEG优化完成: ${jpegFiles.length} 个文件`);
|
||||
|
||||
// 优化PNG文件
|
||||
const pngFiles = await imagemin([`${srcDir}/*.png`], {
|
||||
destination: distDir,
|
||||
plugins: [
|
||||
imageminPngquant(pngOptions)
|
||||
]
|
||||
});
|
||||
|
||||
console.log(`PNG优化完成: ${pngFiles.length} 个文件`);
|
||||
|
||||
// 生成图片清单
|
||||
generateImageManifest(distDir);
|
||||
|
||||
console.log('图片优化完成!');
|
||||
} catch (error) {
|
||||
console.error('图片优化失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成图片清单
|
||||
function generateImageManifest(distDir) {
|
||||
const manifest = {
|
||||
images: [],
|
||||
generated: new Date().toISOString()
|
||||
};
|
||||
|
||||
function scanDirectory(dir, relativePath = '') {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
files.forEach(file => {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
scanDirectory(fullPath, path.join(relativePath, file));
|
||||
} else if (stat.isFile()) {
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
if (['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
|
||||
manifest.images.push({
|
||||
name: file,
|
||||
path: path.join(relativePath, file),
|
||||
size: stat.size,
|
||||
type: ext.substring(1)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scanDirectory(distDir);
|
||||
|
||||
const manifestPath = path.join(distDir, 'manifest.json');
|
||||
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
||||
|
||||
console.log(`图片清单已生成: ${manifestPath}`);
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
optimizeImages();
|
||||
}
|
||||
|
||||
module.exports = { optimizeImages };
|
||||
Reference in New Issue
Block a user