uniapp小程序包瘦身
·
1. 分包内静态文件
"subPackages": [{
"root": "pages2",
"pages": [{
"path": "index/index"
}]
}]
注意
- 主包只放置tabbar页面,其他页面放入子包
- 多个包公用的组件,可以放在主包components中
- 为了方便快捷使用,也可以参照easycom配置公共组件
2. 运行—运行到小程序模拟器—运行时是否压缩代码(打开)
3. 压缩图片
常用网站:https://tinypng.com/ 或者将图片放置在服务器上或云上,需要衡量下图片大小和请求数量。另外渐变背景等尽量不用图片用css实现。图标图片可引入iconfont字体文件,替代图片。
4. 开启分包优化
manifest.json—源码视图—mp-weixin对象下添加"optimization": {“subPackages”: true}
关于分包优化的说明:
- 在对应平台的配置下添加"optimization":{“subPackages”:true}开启分包优化
- 目前只支持mp-weixin、mp-qq、mp-baidu、mp-toutiao、mp-kuaishou的分包优化
- 分包优化具体逻辑:
- 静态文件:分包下支持 static 等静态资源拷贝,即分包目录内放置的静态资源不会被打包到主包中,也不可在主包中使用
- js文件:当某个 js 仅被一个分包引用时,该 js 会被打包到该分包内,否则仍打到主包(即被主包引用,或被超过 1 个分包引用)
- 自定义组件:若某个自定义组件仅被一个分包引用时,且未放入到分包内,编译时会输出提示信息
5. 分包异步化:将主要代码放在子包,主包以组件方式引入使用
自微信小程序2.11.2版本后小程序支持分包异步化。
在uniapp中的pages.json文件中,某一个页面要引入其他页面组件 需要添加usingComponents、componentPlaceholder
"pages": [{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
"usingComponents": { // 组件引入
"test-list": "/pagesO/components/test-a"
},
"componentPlaceholder": { // 配置组件占位符,在组件包没加载时,暂时使用占位符组件
"test-list": "view"
}
}
}],
"subPackages": [{
"root": "pagesO",
"pages": [{
"path": "index/index",
"style": {
"navigationBarTitleText": "列表"
}
}]
}],
// 以下可以配置
"preloadRule": {
"pagesO/index/index": {
"network": "all",
"packages": ["pagesO"]
}
},
在页面上引入:
import TestA from '/pagesO/components/test-a'
template里面引入:注意组件名要和usingComponents和componentPlaceholder中保持一致
<test-a />
额外插件:
但有些情况下(没找到原因)uniapp编译的小程序代码中会缺失componentPlaceholder,导致组件引入失败, 可以使用插件手动插入:
// vue.config.js
const UniappComponentPlaceholderPlugin = require('./uniapp-component-placeholder-plugin');
module.exports = {
configureWebpack: {
plugins: [
new UniappComponentPlaceholderPlugin({
pagesJsonPath: 'pages.json', // uniapp页面配置文件位置
sourceDir: '', // 路径前缀配置
outputDir: 'unpackage/dist/dev/mp-weixin', // 编译后的小程序文件位置
debug: true, // 启用调试模式
logLevel: 'debug' // 设置日志级别
})
]
}
}
// uniapp-component-placeholder-plugin.js
const fs = require('fs');
const path = require('path');
class UniappComponentPlaceholderPlugin {
constructor(options = {}) {
this.options = {
pagesJsonPath: 'pages.json', // pages.json路径
sourceDir: '', // 源码目录
outputDir: 'dist/dev/mp-weixin', // 编译输出目录
debug: true, // 默认启用调试模式
...options
};
// 日志级别:debug, info, warn, error
this.logLevel = this.options.logLevel || 'info';
}
// 日志输出函数
log(level, ...args) {
const logLevels = ['debug', 'info', 'warn', 'error'];
const levelIndex = logLevels.indexOf(level);
const currentLevelIndex = logLevels.indexOf(this.logLevel);
if (levelIndex >= currentLevelIndex) {
const prefix = `[UniappComponentPlaceholderPlugin][${level.toUpperCase()}]`;
console.log(`${prefix} ${args.join(',')}`);
}
}
apply(compiler) {
this.log('info', '插件已启动,等待编译完成...');
// 获取当前插件文件所在目录的绝对路径
const pluginDir = path.dirname(__filename);
// 在编译完成后执行
compiler.hooks.afterEmit.tapAsync('UniappComponentPlaceholderPlugin', async (compilation, callback) => {
try {
this.log('info', '开始处理componentPlaceholder配置...');
// 等待关键文件生成---小程序文件编译完成
await this.waitForFilesToExist([
path.join(pluginDir, this.options.outputDir, 'app.json')
]);
// 处理componentPlaceholder配置
this.processComponentPlaceholders();
this.log('info', '配置处理完成!');
callback();
} catch (error) {
this.log('error', `处理失败: ${error.message}`);
callback(error);
}
});
}
// 等待文件生成
waitForFilesToExist(filePaths) {
this.log('debug', `等待文件生成: ${filePaths.join(', ')}`);
return new Promise((resolve, reject) => {
const startTime = Date.now();
const checkInterval = 1000; // 检查间隔(ms)
const timeout = 0; // 超时时间(ms)
const checkFiles = () => {
const allExist = filePaths.every(filePath => {
try {
return fs.existsSync(filePath);
} catch (error) {
return false;
}
});
if (allExist) {
this.log('debug', `所有文件已生成,耗时: ${Date.now() - startTime}ms`);
resolve();
return;
}
if (timeout > 0 && Date.now() - startTime > timeout) {
reject(new Error(`等待文件超时: ${filePaths.join(', ')}`));
return;
}
this.log('debug', `文件尚未全部生成,继续等待... (已等待: ${Date.now() - startTime}ms)`);
setTimeout(checkFiles, checkInterval);
};
checkFiles();
});
}
// 处理componentPlaceholder配置
processComponentPlaceholders() {
this.log('info', '开始解析pages.json..., 请去掉json中的注释');
// 获取当前插件文件所在目录的绝对路径
const pluginDir = path.dirname(__filename);
// 读取pages.json
const pagesJsonPath = path.join(pluginDir, this.options.sourceDir, this.options.pagesJsonPath);
if (!fs.existsSync(pagesJsonPath)) {
this.log('error', `找不到pages.json: ${pagesJsonPath}`);
return;
}
try {
const fileObj = fs.readFileSync(pagesJsonPath, 'utf8');
const pagesJson = JSON.parse(fileObj);
this.log('info', '成功解析pages.json');
let processedPages = 0;
// 处理主包页面配置
if (Array.isArray(pagesJson.pages)) {
this.log('info', `发现主包页面: ${pagesJson.pages.length}个`);
pagesJson.pages.forEach(page => {
if (page.style && page.style.componentPlaceholder) {
this.log('debug', `处理主包页面: ${page.path}`);
this.updatePageJson('main', page.path, page.style.componentPlaceholder);
processedPages++;
}
});
}
// 处理分包页面配置
if (Array.isArray(pagesJson.subPackages)) {
this.log('info', `发现分包: ${pagesJson.subPackages.length}个`);
pagesJson.subPackages.forEach(subPkg => {
const subPkgRoot = subPkg.root;
this.log('info', `处理分包: ${subPkgRoot}`);
// 处理分包级别的componentPlaceholder
if (subPkg.componentPlaceholder) {
if (Array.isArray(subPkg.pages)) {
this.log('debug', `分包${subPkgRoot}配置了componentPlaceholder,应用到${subPkg.pages.length}个页面`);
subPkg.pages.forEach(page => {
// 合并分包级别和页面级别的配置
const combinedConfig = {
...subPkg.componentPlaceholder,
...(page.style && page.style.componentPlaceholder || {})
};
if (Object.keys(combinedConfig).length > 0) {
this.log('debug', `处理分包${subPkgRoot}页面: ${page.path}`);
this.updatePageJson(subPkgRoot, page.path, combinedConfig);
processedPages++;
}
});
}
} else if (Array.isArray(subPkg.pages)) {
// 只处理页面级别的componentPlaceholder
subPkg.pages.forEach(page => {
if (page.style && page.style.componentPlaceholder) {
this.log('debug', `处理分包${subPkgRoot}页面: ${page.path}`);
this.updatePageJson(subPkgRoot, page.path, page.style.componentPlaceholder);
processedPages++;
}
});
}
});
}
this.log('info', `处理完成,共更新${processedPages}个页面的配置`);
} catch (error) {
this.log('error', `解析pages.json失败: ${error.message}`);
}
}
// 更新页面JSON文件
updatePageJson(packageName, pagePath, componentPlaceholder) {
// 获取当前插件文件所在目录的绝对路径
const pluginDir = path.dirname(__filename);
// 计算编译后的JSON文件路径
const sourcePagePath = packageName === 'main'
? path.join(this.options.sourceDir, pagePath)
: path.join(this.options.sourceDir, packageName, pagePath);
this.log('debug', `查找源码JSON文件: ${sourcePagePath}`)
const outputJsonPath = path.join(
pluginDir,
this.options.outputDir,
path.relative(this.options.sourceDir, sourcePagePath) + '.json'
);
this.log('debug', `查找编译后的JSON文件: ${outputJsonPath}`);
if (!fs.existsSync(outputJsonPath)) {
this.log('warn', `找不到JSON文件: ${outputJsonPath}`);
return;
}
try {
this.log('debug', `读取JSON文件: ${outputJsonPath}`);
// 读取并更新JSON文件
const jsonContent = fs.readFileSync(outputJsonPath, 'utf8');
const jsonData = JSON.parse(jsonContent);
// 记录原始配置
const originalConfig = { ...jsonData.componentPlaceholder };
// 应用新配置
jsonData.componentPlaceholder = {
...originalConfig,
...componentPlaceholder
};
// 计算变更
const newComponents = Object.keys(componentPlaceholder);
const addedComponents = newComponents.filter(name => !originalConfig.hasOwnProperty(name));
const modifiedComponents = newComponents.filter(name =>
originalConfig.hasOwnProperty(name) && originalConfig[name] !== componentPlaceholder[name]
);
// 写回文件
fs.writeFileSync(outputJsonPath, JSON.stringify(jsonData, null, 2), 'utf8');
// 输出变更日志
if (addedComponents.length > 0) {
this.log('info', `已添加${addedComponents.length}个组件占位符: ${addedComponents.join(', ')}`);
}
if (modifiedComponents.length > 0) {
this.log('info', `已修改${modifiedComponents.length}个组件占位符: ${modifiedComponents.join(', ')}`);
}
this.log('info', `成功更新: ${outputJsonPath}`);
} catch (error) {
this.log('error', `更新失败: ${outputJsonPath} - ${error.message}`);
}
}
// 规范化路径
normalizePaths(config) {
const normalized = Object.fromEntries(
Object.entries(config).map(([name, path]) => {
// 确保路径以/开头
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
return [name, normalizedPath];
})
);
if (this.options.debug && Object.keys(normalized).length > 0) {
this.log('debug', '规范化路径:');
Object.entries(normalized).forEach(([name, path]) => {
this.log('debug', ` ${name}: ${path}`);
});
}
return normalized;
}
}
module.exports = UniappComponentPlaceholderPlugin;
更多推荐
所有评论(0)