Weex 在 JS Runtime 内的多实例管理

论坛 期权论坛     
选择匿名的用户   2021-5-22 15:19   17   0
<div class="content-detail markdown-body">
<p>Weex 的技术架构和传统的客户端渲染机制相比有一个显著的差别,就是引入了 JavaScript,通过 JS Runtime 完成一些动态性的运算,再把运算结果和外界进行通信,完成界面渲染等相关操作指令。而客户端面对多个甚至可能同时共存的 Weex 页面时,并没有为每个 Weex 页面提供各自独立的 JS Runtime,相反我们只有一个 JS Runtime,这意味着所有的 Weex 页面共享同一份 JS Runtime,共用全局环境、变量、内存、和外界通信的接口等等。这篇文章会循序渐进的介绍 Weex JS Runtime 这部分的内容,大概的章节设计是这样的:</p>
<ol><li>为什么需要多实例</li><li>多实例管理面临的挑战</li><li>解决问题的思路</li><li>几个特殊处理的地方</li><li>总结</li></ol>
<h2>为什么在 JS Runtime 内部手动管理多实例?</h2>
<p>如果只用一个词来回答,那就是“性能”</p>
<p>如果要用一段话来回答:手机上的资源是很宝贵的,包括CPU、内存、电量等等,而 Weex 团队从设计初期就决定以页面为单位对产品实现进行划分,一个完整的应用是多个相互独立解耦的页面通过一定的路由规则和链接跳转互联起来组合而成。所以为每个页面都单独提供一份 JS Runtime 代价还是比较昂贵的,这会引起大量的资源开销,手机发烫,反应迟钝,甚至应用或操作系统的崩溃。尤其是在国内一些中低端机型上面,反应尤其明显。</p>
<p>从另外一个角度讲,我们通过同一个 JS Runtime,可以更直接方便的做一些运行时的资源共享,比如 JS Framework 的初始化过程,只需要应用启动的时候执行一次就可以了,不必每个页面被打开的时候才进行。目前 JS Framework 的启动过程一般会在几百毫秒不等,相当于每个页面打开的时候,这几百毫秒都被节省下来了。</p>
<h2>多实例管理的 JS Runtime 需要额外关注哪些问题?</h2>
<p>首先不同的 Weex 页面肯定需要执行各自的 JavaScript 运算,完成各自的 native 指令收发。所以如何避免多个 Weex 页面在同一个 JS Runtime 里相互“打架”就变得至关重要。</p>
<p>这里的“打架”有以下几个细节:</p>
<ul><li>数据和状态的记录,能够正确的完成并且不会被其它页面的运算所干扰或截获</li><li>和 native 之间的收发指令或通信,能够准确的调度不同的 native 端页面</li><li>对系统资源的利用,遇到大运算量的页面时,其它页面有机会快速得到响应</li></ul>
<p>除了“打架”的问题之外,传统 HTML5 页面里,每个 JS Runtime 的生命周期是对应页面本身的生命周期的,相对是个短效的实例,而且一旦页面被关闭,对应这个页面的 JS Runtime 就可以大方的 kill 掉,没有任何后顾之忧;而 Weex 的 JS Runtime 需要在应用被开启之后至始至终存在并不间断工作,所以长期运转的内存管理也变成了一个不得不正视的问题。</p>
<h2>Weex 解决上述问题的过程</h2>
<ul><li>首先,我们会为每个新打开的 Weex 页面创建一个唯一的 instance id</li><li> <p>其次,JS Runtime 里所有的 native 通信接口,不管是发送还是接收,全部需要传递 instance id 作为第一个参数,这样 JS Runtime 和 native 端都可以快速准确的识别并分发给每个 Weex 页面,比如:</p>
   <ul><li> <code>createInstance(id, code, config, data)</code>:创建一个新的 Weex 页面,通过一整段 Weex JS Bundle 的代码,在 JS Runtime 开辟一块新的空间用来存储、记录和运算</li><li> <code>sendTasks(id, tasks)</code>:从 JS Runtime 发送指令到 native 端</li><li> <code>receiveTasks(id, tasks)</code>:从 native 端发送指令到 JS Runtime</li></ul></li><li>然后,我们根据不同的 instance id 在 JS Runtime 里进行独立的运算和数据、状态记录。这里我们通过 JavaScript 里的闭包原理把不同实例的运算和数据状态管理隔离在了不同的闭包里,达到相互不“打架”的目的。</li></ul>
<h3>初级形态</h3>
<p>形如:</p>
<pre class="blockcode"><code class="javascript">// old version of Weex JS Runtime

function createInstance(id, code) {
  const customComponents &#61; {}

  function define(name, definition) {
    // todo: register a weex component in this Weex instance
    ...
    customComponents[name] &#61; definition
    ...
  }
  function bootstrap(name) {
    // todo: start to render this Weex instance from a certain named component
    ...
    sendTasks(id, [...])
    ...
  }

  // run
  eval(code)
}</code></pre>
<p>我们在闭包中设置了这么几个东西,保障隔离效果:</p>
<ol><li> <code>define</code>: 用来自定义一个复合组件</li><li> <code>bootstrap</code>: 用来以某个复合组件为根结点渲染页面</li></ol>
<p>
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP