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}

关于分包优化的说明:

  1. 在对应平台的配置下添加"optimization":{“subPackages”:true}开启分包优化
  2. 目前只支持mp-weixin、mp-qq、mp-baidu、mp-toutiao、mp-kuaishou的分包优化
  3. 分包优化具体逻辑:
  • 静态文件:分包下支持 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;
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐