小程序動(dòng)態(tài)化

動(dòng)態(tài)化

Web 應(yīng)用具有天然的動(dòng)態(tài)化特性,即在應(yīng)用上線后,可通過配置數(shù)據(jù)接口,實(shí)時(shí)更改頁面布局及交互:

  • HTML 可以通過 Ajax 獲取后,使用 DOM API 變更 DOM 實(shí)時(shí)生效;
  • CSS 可以通過動(dòng)態(tài)添加 style 標(biāo)簽實(shí)時(shí)更新;
  • JS 既可以通過添加 script 標(biāo)簽拉取執(zhí)行,也可以通過接口獲取 JS 文本,再使用 eval/Function 等 API 執(zhí)行。

小程序動(dòng)態(tài)化存在的問題:

  • Runtime 接口缺失(沒有開放 eval/Function 功能),導(dǎo)致無法直接執(zhí)行交互腳本。
  • 橋的缺失,導(dǎo)致無法直接操作更新視圖元素。

為了解決這兩個(gè)問題,需要開發(fā):

  • JSVM —— 在小程序端能夠執(zhí) JS AST 的 runtime;
  • 渲染模板引擎 —— 能夠?qū)魅氲哪0宀季峙渲眠€原成小程序頁面。

架構(gòu)圖

image.png

處理過程解析

1. 動(dòng)態(tài)組件
(1)動(dòng)態(tài)組件設(shè)置case屬性后,通過接口獲取云端代碼(在遠(yuǎn)端編寫的組件代碼),dom即wxml-ast, jscode即js-ast;
(2) 使用內(nèi)置jsvm 解析 js-ast:
(3) 解析wxml-ast,將傳入的模板布局配置還原成小程序頁面。參照上圖需要解決兩個(gè)問題:遞歸渲染vnode,綁定交互事件。

2.如何遞歸渲染,綁定交互事件
動(dòng)態(tài)組件其內(nèi)部有一個(gè)模板,把所有標(biāo)簽動(dòng)寫成一個(gè)template。例如:<template is="button">.當(dāng)拿到遠(yuǎn)端的dom的ast后,遞歸渲染每一個(gè)標(biāo)簽。樣式是通過style添加到每個(gè)標(biāo)簽上的。

3.內(nèi)置jsvm
抽象語法樹是由編譯器(complier)在語法分析階段產(chǎn)生(由 parser 解析生成)。參考ref2。
ESTree:js-ast 使用的AST標(biāo)準(zhǔn);
@babel/parser : 將js 轉(zhuǎn)為AST;
jsvm:解釋執(zhí)行AST;

主要代碼流程:

dynamic組件設(shè)置case屬性后,會(huì)觸發(fā)observer,調(diào)用init方法:

init {

  const vm = getVm(); // 獲取虛擬機(jī)對(duì)象

  const vdom = new VDOM(); // 初始化虛擬dom對(duì)象

  _init: getData( 獲取云端代碼) => bindScope( 綁定js執(zhí)行上下文) => evalData( 解釋執(zhí)行js) => setData(執(zhí)行diff, 更新視圖層)

  reload: getData => evalData => setData

  bindScope {

    scope = this[SCOPE] = getScope(context); // 生成一個(gè)新作用域,包含context中的所有對(duì)象;

  }

  evalData {

    comp[TPL] = dom; // 緩存云端wxml-ast

    vm.runInScope(_scope, jscode.type ? jscode : (data.jscode = inflate(jscode))); //  在提供的Scope下解釋執(zhí)行AST代碼~

  }

  setData {

    const setData = (obj ? : Partial < DynamicContext > , callback ? : any) => {
      Object.assign(context.data || (context.data = {}), obj); // 更新當(dāng)前的context
      injectIntoScope(scope, obj); // 更新當(dāng)前模板的作用域
      ?
      callback && cachedActions.push(callback); // 緩存callback隊(duì)列
      flush(); // flush setData
      comp[V_DOM] = vdom; // 緩存本次vdom
    };
    flush {

      vdom.calc(comp[TPL], scope); //  依據(jù)模板和當(dāng)前作用域,數(shù)據(jù)內(nèi)聯(lián)后生成虛擬dom

      const diffNode = diff(vdom.vnodes, comp.data.config, 'config'); // diff算法:comp.data.config是上一次的vdom,生成diffnode

      comp.setData(diffNode, callBackFun); //  調(diào)用微信的setData方法,diffnode會(huì)在小程序渲染層解析最后更新視圖,這部分代碼參考core.wxml

    }
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容