微信小程序性能优化

性能优化是任何应用开发中的重要组成部分,尤其是在移动环境中。对于微信小程序而言,随着用户量的增加和应用功能的丰富,性能优化显得尤为关键。良好的性能不仅提升用户体验,还能增加用户留存率和应用的使用频率。我们将探讨如何在微信小程序中进行性能优化,涵盖从首屏加载、分包、网络请求到渲染性能等多个方面。

  • 网络请求:合并请求,减少HTTP请求次数;使用缓存策略减少重复请求。
  • 布局与样式:避免使用复杂的CSS选择器;合理使用Flex布局减少重排重绘。
  • 事件处理:移除不再需要的事件监听器,防止内存泄漏。
  • 组件懒加载、按需加载

一、优化首屏加载

1. 减少初始包的体积

每个小程序都有一个加载包,包含了代码及依赖。为了加快加载速度,我们应尽量减少包的体积。

  • 懒加载组件 :在小程序中使用 Component 时,可以选择懒加载。只有在组件实际需要时再加载。

    javascript 复制代码
    Component({
      properties: {
        show: {
          type: Boolean,
          value: false
        }
      },
      observers: {
        'show': function (newVal) {
          if (newVal) {
            // 只在需要展示时加载相关资源
            require('./path/to/your/component.js');
          }
        }
      }
    });
  • 拆分代码 :把代码拆分成多个小模块,按需加载。

    合理地分割代码模块,不仅可以使代码结构更加清晰,还能够提高加载效率。只加载用户当前需要的部分,避免一次性加载整个应用的所有代码。

    javascript 复制代码
    // 示例:按需加载
    const pages = {
      home: () => import('./pages/home'),
      about: () => import('./pages/about')
    };
    
    Page({
      onLoad: function() {
        // 根据需要动态加载页面
        pages.home().then(module => {
          module.default.loadContent();
        });
      }
    });
  • 资源懒加载:按需加载图片和其他资源,减少初始加载时间。

    javascript 复制代码
    <image src="" mode="widthFix" data-src="{{item.image}}" bindload="imageLoad"/>
    
    // JS部分
    imageLoad(e) {
      e.currentTarget.src = e.currentTarget.dataset.src;
    }
  • 资源复用:合理利用已有资源减少重复加载

    对于那些多次使用的资源,如图标、背景等,我们可以将其作为公共资源存储起来,避免在每次加载页面时重复下载。

    javascript 复制代码
    // 示例:将公共图标作为公共资源
    const icons = ['icon1.png', 'icon2.png'];
    
    function preloadIcons() {
      icons.forEach(icon => {
        const img = new Image();
        img.src = icon;
      });
    }
    
    preloadIcons();

2. 资源压缩与图片优化

  • 图片格式与大小:使用合适的格式,如 JPEG、PNG 或 WEBP。尽可能减小图片尺寸,以减少加载时间。借助工具压缩图片,减少图片大小,同时保持较高质量。

  • 使用云存储:将图片存储在云端,利用 CDN 加速加载。

  • 代码压缩和混淆
    利用构建工具(如webpack)进行代码压缩和混淆,减小包体积,提高加载速度。

    javascript 复制代码
    // webpack.config.js
    module.exports = {
      mode: 'production',
      optimization: {
        minimize: true,
      },
    };

3. 优化 CSS 和 JavaScript

  • 减少 CSS 选择器的复杂性:复杂的选择器会增加渲染时间,建议使用简短的类名和 ID。
  • 合并 CSS 文件:将多个 CSS 文件合并成一个,减少 HTTP 请求次数。
  • 使用 CSS3 动画:使用 CSS3 动画替代 JavaScript 动画,控制性能和流畅度。

二、网络请求优化

网络请求是小程序性能的另一个关键因素,优化网络请求可以显著提升用户体验。

1. 缓存机制

使用缓存机制可以减少数据重复请求,加速数据的获取。

  • 使用 wx.setStorageSync 和 wx.getStorageSync:在获取数据后将其存储到本地,以后请求前先检查本地是否已有缓存。
javascript 复制代码
const fetchData = async () => {
  const cachedData = wx.getStorageSync('myData');
  if (cachedData) {
    return cachedData; // 使用缓存
  }

  const response = await wx.request({/*...*/});
  wx.setStorageSync('myData', response.data);
  return response.data;
};

2. 限制请求数量

避免在短时间内发送过多请求,可以合并请求或使用防抖和节流技术。

  • 防抖

    javascript 复制代码
    let debounceTimer;
    function debounce(func, delay) {
      return function (...args) {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => func.apply(this, args), delay);
      };
    }
    
    // 使用示例
    const fetchDataDebounced = debounce(fetchData, 300);
  • 节流

    javascript 复制代码
    function throttle(func, limit) {
      let lastFunc;
      let lastRan;
      return function () {
        const context = this;
        const args = arguments;
        if (!lastRan) {
          func.apply(context, args);
          lastRan = Date.now();
        } else {
          clearTimeout(lastFunc);
          lastFunc = setTimeout(() => {
            if (Date.now() - lastRan >= limit) {
              func.apply(context, args);
              lastRan = Date.now();
            }
          }, limit - (Date.now() - lastRan));
        }
      };
    }

3. 分批加载:避免一次性加载大量数据

当需要加载大量数据时,可以采用分批加载的方式,每次只加载一部分数据,待这部分数据处理完毕后再加载下一批。

javascript 复制代码
// 示例:分批加载数据
function fetchBatchData(page) {
  wx.request({
    url: `https://api.wwww.com/data?page=${page}`,
    success: function(res) {
      const newData = res.data;
      // 处理数据
      this.setData({
        items: this.data.items.concat(newData)
      });
    }
  });
}

fetchBatchData(1);

4. 智能请求:根据网络状况调整请求策略

根据用户的网络环境动态调整请求策略,可以进一步提高数据加载的速度和成功率。

javascript 复制代码
// 示例:根据网络状况调整请求策略
wx.getNetworkType({
  success: function(res) {
    const networkType = res.networkType;
    if (networkType === '2g') {
      // 在2G网络环境下减少请求次数
      fetchBatchData(1, 5); // 一次性请求5页数据
    } else {
      // 其他网络环境下正常请求
      fetchBatchData(1);
    }
  }
});

三、优化页面渲染性能

页面渲染效率直接影响用户体验,优化渲染性能是非常必要的。

1. 启动速度优化

  • 减少全局数据初始化
  • 使用分包加载,将不常用页面分割到子包中

2. 使用 wx:if 和 wx:for

适当使用 wx:if 和 wx:for(使用wx:key优化列表渲染) 避免过多的视图层重绘。尽量使用 wx:show 替代 wx:if,因为 wx:if 会在条件变化时销毁和重新创建节点。

3. 合理使用数据绑定

在数据大规模更新时,避免一次性改变全部数据,使用 this.setData() 时要尽量精简,减少触发视图的重复渲染。

javascript 复制代码
this.setData({
  'todos[0].completed': true // 只更新部分数据
});

4. 使用组件化

将页面拆分成多个小组件,每个组件负责各自的状态和渲染,这样可以有效地减少失去控制的渲染过程,增加性能。同时也提高了代码的可维护性。

5. 避免长任务

对长时间运行的任务进行拆分,避免在主线程中阻塞 UI 更新。可以使用 Promise 和 setTimeout 进行分步执行,确保 UI 不被冻结。

6. 内存管理

  • 监听页面生命周期,及时释放不再使用的资源
  • 使用WeakRef避免循环引用问题
javascript 复制代码
onUnload() {
  // 清理定时器、监听器等
  this.timer && clearInterval(this.timer);
}

7. 削减冗余:识别与移除无用代码

删除冗余代码就像是给程序减肥,让它变得更加苗条。那些不再使用的函数、未引用的变量或者废弃的样式都应该被清理掉,这样才能保证小程序运行得更快。

javascript 复制代码
// 示例:删除未使用的变量
let unusedVar = 'I will be removed'; // 不再使用此变量

function cleanUpCode() {
  // 删除未使用的变量
  delete unusedVar; // 实际开发中应使用工具或手动检查
}

cleanUpCode();

8. 异步处理:合理使用异步任务不阻塞主线程

合理使用异步任务可以避免阻塞主线程,从而提高程序的执行效率。

javascript 复制代码
// 示例:使用Promise异步加载数据
function fetchDataAsync() {
  return new Promise((resolve, reject) => {
    wx.request({
      url: 'https://api.wwww.com/data',
      success: resolve,
      fail: reject
    });
  });
}

fetchDataAsync().then(res => {
  this.setData({
    items: res.data
  });
});

9. 动画优化:流畅过渡增强交互感

通过优化动画效果,可以使用户界面更加流畅,增强用户的交互体验。

javascript 复制代码
// 示例:优化动画效果
Page({
  data: {
    animate: false
  },
  startAnimation: function() {
    this.setData({
      animate: true
    });
    setTimeout(() => {
      this.setData({
        animate: false
      });
    }, 1000);
  }
});

四、减少小程序的启动时间

1. 初始页面优化

在小程序启动时载入的页面要尽量简化,减少初始页面的内容,延迟加载不必要的内容。

2. 使用 wx.navigateTo 与 wx.redirectTo

合理使用页面跳转方式,避免使用 wx.redirectTo 后缀跳转过多的页面,而导致栈溢出,减慢响应速度。

五、工具与监控

1. 性能监控工具

使用微信开发者工具中的性能分析工具来监控小程序的性能,查看每个调用的时间,找出性能瓶颈。

2. 代码性能分析

使用一些 JavaScript 的代码静态分析工具(如 ESLint, Prettier)来确保代码的高效性,及时优化掉低效代码。

六、优化微信小程序超包问题

1. 分包

分包部分规则

1、子包最多100个

2、tabBar页面只能在主包中

3、子包内不能再分包

4、子包内的资源不能互相引用(可使用分包异步化解决),但可使用主包内的资源

5、整个小程序所有分包大小不超过 20M,单个分包/主包大小不能超过 2M

小程序开发者工具的代码依赖分析可以查看包体积情况

分包规则

javascript 复制代码
// 在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示,此时终端界面会有等待提示。
{
	// pages 主包(默认启动页面及 TabBar 页面)
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			// 页面上访问路径 /pages/index/index
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "首页",
				// custom即取消默认的原生导航栏
				"navigationStyle": "custom",
				"navigationBarTextStyle": "white"
				// "enablePullDownRefresh":false, //是否允许底部下拉刷新,可在页面配合 onPullDownRefresh生命周期一起使用
			}
		},
		{
			"path": "pages/my/my",
			"style": {
				"navigationBarTitleText": "我的",
				"navigationStyle": "custom",
				"navigationBarTextStyle": "white"
			}
		},
		{
			"path": "pages/cart/cart",
			"style": {
				"navigationBarTitleText": "购物车"
			}
		},
		
		{
			"path": "pages/404/404",
			"style": {
				"navigationBarTitleText": "页面未找到",
				"enablePullDownRefresh": false
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	// 组件自动引入规则
	// easycom方式引入组件不是全局引入,而是局部引入。例如在H5端只有加载相应页面才会加载使用的组件
	"easycom": {
		// 是否开启自动扫描
		"autoscan": true,
		"custom": {
			// uni-ui 规则如下配置  uni扩展组件
			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
			//以 Xtx 开头的组件,匹配components目录内的vue文件(需要重启服务器)
			"^Xtx(.*)": "@/components/Xtx$1.vue"
			// 匹配node_modules内的vue文件,如匹配 uview-ui 包的组件
			// "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
		}
	},
	// 底部
	"tabBar": {
		"color": "#333",
		"selectedColor": "#27ba9b",
		"backgroundColor": "#fff",
		"borderStyle": "white",
		"list": [{
				"text": "首页",
				// pagePath要与上面的pages一致
				"pagePath": "pages/index/index",
				"iconPath": "static/tabs/home_default.png",
				"selectedIconPath": "static/tabs/home_selected.png"
			},
			{
				"text": "购物车",
				"pagePath": "pages/cart/cart",
				"iconPath": "static/tabs/cart_default.png",
				"selectedIconPath": "static/tabs/cart_selected.png"
			},
			{
				"text": "我的",
				"pagePath": "pages/my/my",
				"iconPath": "static/tabs/user_default.png",
				"selectedIconPath": "static/tabs/user_selected.png"
			}
			// {
			// 	"text": "SKU",
			// 	"pagePath": "pages/sku/sku"
			// }
		]
	},
	// 分包页面(需手动创建subPackages文件夹),按需加载
	"subPackages": [
		{
			// 子包的根目录
			"root": "pagesMember",
			// 页面路径和窗口表现,和上面 pages 一样
			// 访问的完整路径   如:/pagesMember/settings/settings
			"pages": [{
					"path": "settings/settings",
					"style": {
						"navigationBarTitleText": "设置"
					}
				},
				{
					"path": "profile/profile",
					"style": {
						"navigationBarTitleText": "个人信息",
						// 取消默认的导航栏
						"navigationStyle": "custom",
						"navigationBarTextStyle": "white"
					}
				},
				{
					"path": "address/address",
					"style": {
						"navigationBarTitleText": "地址管理"
					}
				},
				{
					"path": "address-form/address-form",
					"style": {
						"navigationBarTitleText": "新建地址"
					}
				}
			]
		},
		{
			"root": "pagesOrder",
			"pages": [{
					"path": "create/create",
					"style": {
						"navigationBarTitleText": "结算"
					}
				},
				{
					"path": "detail/detail",
					"style": {
						"navigationBarTitleText": "订单详情",
						"navigationStyle": "custom"
					}
				},
				{
					"path": "payment/payment",
					"style": {
						"navigationBarTitleText": "支付结果"
					}
				},
				{
					"path": "list/list",
					"style": {
						"navigationBarTitleText": "订单列表"
					}
				}
			]
		}
	],
	// 预下载分包,分包预下载规则
	"preloadRule": {
		// 进入 我的 页面,预下载分包
		"pages/my/my": {
			"network": "all",
			// 分包 root的值
			"packages": [
				"pagesMember"
			]
		},
		"pages/cart/cart": {
			"network": "all",
			"packages": [
				"pagesOrder"
			]
		}
	},
	"condition": { //模式配置,仅开发期间生效
		"current": 0, //当前激活的模式(list 的索引项)
		"list": [{
			"name": "", //模式名称
			"path": "", //启动页面,必选
			"query": "" //启动参数,在页面的onLoad函数里面得到
		}]
	}
}

2. 如果分包后主包还是超了可以把图片放在云服务上,把主包文件夹中一些大的js或者json等的文件移到子包中

3. 在hbuilderx中的运行 -> 运行到小程序模拟器 -> 运行时是否压缩代码,勾选

4. 微信小程序模拟器中的本地设置把压缩的都勾选