为了让大家更好的理解小程序的一些限制和做一些优化,下面从小程序的基础架构讲起,如有不对的地方,望指正,请轻喷 :smile:
首先,我们看看下图,小程序的架构如下:
我们可以看到,一个页面使用一个 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 中运行的。限制如下:
还是从一张图说起:
不同于页面的渲染,所有的脚本逻辑都是跑在同一个 JsCode 线程里面,类似于路由中改变 Hash 值。 因此也会引起下面一些常见的坑
像 canvas , video ,input ,map ,picker …… 组件,官方直接使用原生组件,渲染方式如下图:
从上图很容易就可以看出,Navtive 组件的层级是最高的,那么仅仅去改变 z-index 也无法让其他组件覆盖原生组件。还好,官方给出了一个解决方案:
如果以往在移动端, 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 都是一次线程通信。
线程通信成本很高,非常耗时间,因此官方明确的给出了建议:
data: { array: { changeData: '我改变了', noChangeData: '我没有改变' }, }, this.setData({ 'array.changeData':'changed data' }) |
data: { view: '与界面相关的数据' }, noRelaView: '与界面无关' 复制代码 |