为了让大家更好的理解小程序的一些限制和做一些优化, 下面从小程序的基础架构讲起, 如有不对的地方, 望指正, 请轻喷
一. 页面栈限制最多 10 层
首先, 我们看看下图, 小程序的架构如下:
我们可以看到, 一个页面使用一个 webView 线程进行渲染. 如果于页面栈有 10 层, 则会开启 10 个 WebView 线程, 占多一点内存, 所以对页面栈进行了限制.
那如果在 10 层页面栈的限制内, 由于页面的内容过于复杂, 内存爆了怎么办? 小程序内部有一个回收机制, 如果内存紧张时, 会回收掉一部分 WebView .
很多人可能会觉得, 10 层页面栈基本已经够用了, 无须关注这方面的限制了.
但是如果出现循环引用的话, 用户反复点击, 则很容易出现爆栈的情况. 如下图:
所以, 在开发之前, 提前梳理好页面之间的跳转, 合理使用 navigator ,redirectTo, navigateBack ...... 是非常重要的.
当然, 作为一个程序员, 我并不想在跳转的时候去时时刻刻的关注我有没有正确引用, 有没有超出 10 层页面栈. 完全可以对小程序的跳转做一个封装.
因为只有 wx.navigateTo 才会使页面栈 + 1 , 那我们只要对这个方法做一层兜底处理即可. 如下代码:
- const PAGE_LIMIT = 10
- const pages = getCurrentPages()
- if(pages.length>= PAGE_LIMIT) {
- // 使用 wx.redirectTo 方法
- }
这样就可以随心所欲的跳来跳去了.
二. JsCore
小程序的逻辑层是在 JsCore 中运行的. 限制如下:
没有 Windows,Document 对象, 也就无法使用 基于 DOM 的一些库. 暂无解决方法.
还是从一张图说起:
不同于页面的渲染, 所有的脚本逻辑都是跑在同一个 JsCode 线程里面, 类似于路由中改变 Hash 值. 因此也会引起下面一些常见的坑
需要自己清理页面的 setTimeout 或者 setInterval 定时器
如果要求页面每次出现都是最新的数据, 则要把拉取数据的逻辑放在 onShow 中执行, 别放在 onload 中.
若追求 "极致化体验", 在用户切到下一个页面的时候, 可以在 onHide 中 abort 掉没有请求成功的接口, 把即将处理的 setData 去掉, 可以减轻最新页面的逻辑, 让最新页面最快渲染展示.
三. 原生组件层级最高
像 canvas , video ,input ,map ,picker ...... 组件, 官方直接使用原生组件, 渲染方式如下图:
从上图很容易就可以看出, Navtive 组件的层级是最高的, 那么仅仅去改变 z-index 也无法让其他组件覆盖原生组件. 还好, 官方给出了一个解决方案:
使用 cover-view cover-image 将希望层级变高的组件也包裹成 "原生组件"
四. 独特的网络请求, 本地存储
独特的 Storage 存储
以用户纬度隔离, 同一个设备, A 无法访问 B 用户的数据.
持久缓存, 只有在用户关掉小程序才会删除, 如果空间不足, 会进行 LRU , 也就是不经常使用的小程序的数据缓存区域会被全部清空.
在体验版, 开发版, 和线上版都共用一套, 并不会隔离.
没有 Cookie
Request uploadFile downloadFile 并发最多 10 个请求
只支持 HTTPS WSS
如果以往在移动端, PC 的接口会使用 Cookie 进行一些处理, 那在小程序中使用该接口就比较尴尬了.
因此, 我们可以在小程序中也模拟出 Cookie , 如下图:
在 Storage 中隔离一个字段, 用来做 Cookie , 下面用了一个小技巧, 把 Cookie 的内容存放于内存中, 而非每次都从 storage 中读取.
- let cookie = (function(){
- return wx.getStorageSync('cookies');
- }())
- const Cooke = {
- getCookie(){}, // 从内存中获取 cookie
- setCookie(){}, // 设置 cookie
- setCookieInHeader(){}, // 根据 response 的 Header 设置 cookie
- removeCookie() {}, // 删除 cookie
- isExpired() {} // 判断是否过期
- }
然后, 我们在每次 Request 成功后, 解析 Header 中 SET-COOKIE 属性设置 Cookie , 在每一次请求的时候, 手动在 Header 中设置 Cookie . 这样就完美地模拟浏览器的 Cookie 概念了.
并发请求处理
设置一个队列, 如果请求处理完毕, 则移出队列, 如果当前数组超过 10 个请求, 则进入等待状态. 大概代码如下:
- class Request {
- constructor() {
- this.maxLimit = 10;
- this.requestQueue = []; // 请求队列
- this.requestIng = 0; // 当前并发数
- }
- request () {
- // 判断是否超出并发数
- if(this.requestIng>= this.maxLimit) {
- // 推入请求队列
- }else {
- this.requestIng ++;
- // 执行成功后 - 1 , 从 requestQueue 中取出重新请求
- }
- }
- }
五. setData
好吧, 还是从一张图说起, 显然, 我们可以看出, 每一次 setData 都是一次线程通信.
线程通信成本很高, 非常耗时间, 因此官方明确的给出了建议:
将多次 setData 合并成一次进行调用
只 setData 对象改变的某个值
- data: {
- array: {
- changeData: '我改变了',
- noChangeData: '我没有改变'
- },
- },
- this.setData({
- 'array.changeData':'changed data'
- })
与界面渲染无关的字段不要放在 data 中
- data: {
- view: '与界面相关的数据'
- },
- noRelaView: '与界面无关'
@Author:beidan
来源: https://juejin.im/post/5bfbc03ef265da615c58f0a5