欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

發(fā)表時間:2021-1-4

發(fā)布人:葵宇科技

瀏覽次數(shù):57

在幾天前開源的華為 HarmonyOS (鴻蒙)中,提供了一種“微信小程序”式的跨平臺開發(fā)框架,通過 Toolkit 將應(yīng)用代碼編譯打包成 JS Bundle,解析并生成原生 UI 組件。

按照入門文檔,很容易就能跑通 demo,唯一需要注意的是彈出網(wǎng)頁登錄時用 chrome 瀏覽器可能無法成功:

JS 應(yīng)用框架部分的代碼主要在 ace_lite_jsfwk 倉庫 中,其模塊組成如下圖所示:

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

其中為了實(shí)現(xiàn)聲明式 API 開發(fā)中的單向數(shù)據(jù)綁定機(jī)制,在 ace_lite_jsfwk 代碼倉庫的 packages/runtime-core/src 目錄中實(shí)現(xiàn)了一個 ViewModel 類來完成數(shù)據(jù)劫持。

這部分的代碼總體上并不復(fù)雜,在國內(nèi)開發(fā)社區(qū)已經(jīng)很習(xí)慣 Vue.js 和微信小程序開發(fā)的情況下,雖有不得已而為之的倉促,但也算水到渠成的用一套清晰的開源方案實(shí)現(xiàn)了類似的開發(fā)體驗(yàn),也為更廣泛的開發(fā)者快速入場豐富 HarmonyOS 生態(tài)開了個好頭。

本文范圍局限在 ace_lite_jsfwk 代碼倉庫中,且主要談?wù)?JS 部分。為敘述方便,對私有方法/作用域內(nèi)部函數(shù)等名詞不做嚴(yán)格區(qū)分。

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

ViewModel 類

packages/runtime-core/src/core/index.js
復(fù)制代碼

構(gòu)造函數(shù)

主要工作就是依次解析唯一參數(shù) options 中的屬性字段:

  • 對于 options.render,賦值給 vm.$render 后,在運(yùn)行時交與“JS 應(yīng)用框架”層的 C++ 代碼生成的原生 UI 組件,并由其渲染方法調(diào)用:

    // src/core/context/js_app_context.cpp

    jerry_value_t JsAppContext::Render(jerry_value_t viewModel) const { // ATTR_RENDER 即 vm.$render 方法 jerry_value_t renderFunction = jerryx_get_property_str(viewModel, ATTR_RENDER); jerry_value_t nativeElement = CallJSFunction(renderFunction, viewModel, nullptr, 0); return nativeElement; }

  • 對于 options.styleSheet,也是直接把樣式丟給由 src/core/stylemgr/app_style_manager.cpp 定義的 C++ 類 AppStyleManager 去處理

  • 對于 options 中其他的自定義方法,直接綁定到 vm 上

    else if (typeof value =http://www.wxapp-union.com/=='function') { vm[key] = value.bind(vm); }

options.data
同樣在構(gòu)造函數(shù)中,對于最主要的 options.data,做了兩項(xiàng)處理:

  • 首先,遍歷 data 中的屬性字段,通過 Object.defineProperty 代理 vm 上對應(yīng)的每個屬性, 使得對 vm.foo = 123 這樣的操作實(shí)際上是背后 options.data.foo 的代理:

    /**

    • proxy data
    • @param {ViewModel} target - 即 vm 實(shí)例
    • @param {Object} source - 即 data
    • @param {String} key - data 中的 key

    */ function proxy(target, source, key) { Object.defineProperty(target, key, { enumerable: false, configurable: true, get() { return source[key]; }, set(value) { source[key] = value; } }); }

  • 其次,通過 Subject.of(data) 將 data 注冊為被觀察的對象,具體邏輯后面會解釋。
    組件的 $watch 方法

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

作為文檔中唯一提及的組件“事件方法”,和 render()及組件生命周期等方法一樣,也是直接由C++實(shí)現(xiàn)。除了可以在組件實(shí)例中顯式調(diào)用this.render() 及組件生命周期等方法一樣,也是直接由 C++ 實(shí)現(xiàn)。除了可以在組件實(shí)例中顯式調(diào)用 this.watch,組件渲染過程中也會自動觸發(fā),比如處理屬性時的調(diào)用順序:

  1. Component::Render()

  2. Component::ParseOptions()

  3. 在 Component::ParseAttrs(attrs) 中求出 newAttrValue = http://www.wxapp-union.com/ParseExpression(attrKey, attrValue)

  4. ParseExpression 的實(shí)現(xiàn)為:

    // src/core/components/component.cpp 
        
    /**
     * check if the pass-in attrValue is an Expression, if it is, calculate it and bind watcher instance.
     * if it's not, just return the passed-in attrValue itself.
     */
    jerry_value_t Component::ParseExpression(jerry_value_t attrKey, jerry_value_t attrValue)
    {
        jerry_value_t options = jerry_create_object();
        JerrySetNamedProperty(options, ARG_WATCH_EL, nativeElement_);
        JerrySetNamedProperty(options, ARG_WATCH_ATTR, attrKey);
        jerry_value_t watcher = CallJSWatcher(attrValue, WatcherCallbackFunc, options);
        jerry_value_t propValue = http://www.wxapp-union.com/UNDEFINED;
        if (IS_UNDEFINED(watcher) || jerry_value_is_error(watcher)) {
            HILOG_ERROR(HILOG_MODULE_ACE,"Failed to create Watcher instance.");
        } else {
            InsertWatcherCommon(watchersHead_, watcher);
            propValue = http://www.wxapp-union.com/jerryx_get_property_str(watcher,"_lastValue");
        }
        jerry_release_value(options);
        return propValue;
    }?
    復(fù)制代碼

在上面的代碼中,通過 InsertWatcherCommon 間接實(shí)例化一個 Watcher: Watcher *node = new Watcher()

// src/core/base/js_fwk_common.h

struct Watcher : public MemoryHeap {
    ACE_DISALLOW_COPY_AND_MOVE(Watcher);
    Watcher() : watcher(jerry_create_undefined()), next(nullptr) {}
    jerry_value_t watcher;
    struct Watcher *next;
};

// src/core/base/memory_heap.cpp

void *MemoryHeap::operator new(size_t size)
{
    return ace_malloc(size);
}
復(fù)制代碼

通過 ParseExpression 中的 propValue = http://www.wxapp-union.com/jerryx_get_property_str(watcher,"_lastValue") 一句,結(jié)合 JS 部分 ViewModel 類的源碼可知,C++ 部分的 watcher 概念對應(yīng)的正是 JS 中的 observer:

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

// packages/runtime-core/src/core/index.js

ViewModel.prototype.$watch = function(getter, callback, meta) {
  return new Observer(this, getter, callback, meta);
};
復(fù)制代碼

下面就來看看 Observer 的實(shí)現(xiàn)。

Observer 觀察者類

packages/runtime-core/src/observer/observer.js
復(fù)制代碼

構(gòu)造函數(shù)和 update()
主要工作就是將構(gòu)造函數(shù)的幾個參數(shù)存儲為實(shí)例私有變量,其中

  • _ctx 上下文變量對應(yīng)的就是一個要觀察的 ViewModel 實(shí)例,參考上面的 $watch 部分代碼

  • 同樣,_getter、_fn、_meta 也對應(yīng)著 $watch 的幾個參數(shù)

  • 構(gòu)造函數(shù)的最后一句是 this._lastValue = http://www.wxapp-union.com/this._get(),這就涉及到了 _lastValue 私有變量、_get() 私有方法,并引出了與之相關(guān)的 update() 實(shí)例方法等幾個東西。

  • 顯然,對 _lastValue 的首次賦值是在構(gòu)造函數(shù)中通過 _get() 的返回值完成的:

    Observer.prototype._get = function() { try { ObserverStack.push(this); return this._getter.call(this._ctx); } finally { ObserverStack.pop(); } };

稍微解釋一下這段乍看有些恍惚的代碼 -- 按照 ECMAScript Language 官方文檔中的規(guī)則,簡單來說就是會按照 “執(zhí)行 try 中 return 之前的代碼” --> “執(zhí)行并緩存 try 中 return 的代碼” --> “執(zhí)行 finally 中的代碼” --> “返回緩存的 try 中 return 的代碼” 的順序執(zhí)行:

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解比如有如下代碼:

let _str = '';

function Abc() {}
Abc.prototype.hello = function() {
  try {
    _str += 'try';
    return _str + 'return';
  } catch (ex) {
    console.log(ex);
  } finally {
    _str += 'finally';
  }
};

const abc = new Abc();
const result = abc.hello();
console.log('[result]', result, _str);
復(fù)制代碼

輸出結(jié)果為:

[result] tryreturn tryfinally
復(fù)制代碼

了解這個概念就好了,后面我們會在運(yùn)行測試用例時看到更具體的效果。

  • 其后,_lastValue 再次被賦值就是在 update() 中完成的了:

    Observer.prototype.update = function() {
      const lastValue = http://www.wxapp-union.com/this._lastValue;
      const nextValue = this._get();
      const context = this._ctx;
      const meta = this._meta;
        
      if (nextValue !== lastValue || canObserve(nextValue)) {
        this._fn.call(context, nextValue, lastValue, meta);
        this._lastValue = nextValue;
      }
    };?
    復(fù)制代碼

    // packages/runtime-core/src/observer/utils.js

    export const canObserve = target => typeof target === 'object' && target !== null;

邏輯簡單清晰,對新舊值做比較,并取出 context/meta 等一并給組件中傳入等 callback 調(diào)用。

新舊值的比較就是用很典型的辦法,也就是經(jīng)過判斷后可被觀察的 Object 類型對象,直接用 !== 嚴(yán)格相等性比較,同樣,這由 JS 本身按照 ECMAScript Language 官方文檔中的相關(guān)計(jì)算方法執(zhí)行就好了:

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

# 7.2.13 SameValueNonNumeric ( x, y )

...

8. If x and y are the same Object value, return true. Otherwise, return false.
復(fù)制代碼

另外我們可以了解到,該 update() 方法只有 Subject 實(shí)例會調(diào)用,這個同樣放到后面再看。

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

訂閱/取消訂閱

Observer.prototype.subscribe = function(subject, key) {
  const detach = subject.attach(key, this);
  if (typeof detach !== 'function') {
    return void 0;
  }
  if (!this._detaches) {
    this._detaches = [];
  }
  this._detaches.push(detach);
};
復(fù)制代碼
  • 通過 subject.attach(key, this) 記錄當(dāng)前 observer 實(shí)例

  • 上述調(diào)用返回一個函數(shù)并暫存在 observer 實(shí)例本身的 _detaches 數(shù)組中,用以在將來取消訂閱

    Observer.prototype.unsubscribe = function() {
      const detaches = this._detaches;
      if (!detaches) {
        return void 0;
      }
      while (detaches.length) {
        detaches.pop()(); // 注意此處的立即執(zhí)行
      }
    };?
    復(fù)制代碼

unsubscribe 的邏輯就很自然了,執(zhí)行動作的同時,也會影響到 observer/subject 中各自的私有數(shù)組。

順便查詢一下可知,只有 Subject 類里面的一處調(diào)用了訂閱方法:

鴻蒙 “JS 小程序” 數(shù)據(jù)綁定原理詳解

經(jīng)過了上面這些分析,Subject 類的邏輯也呼之欲出。

Subject 被觀察主體類

packages/runtime-core/src/observer/subject.js

作者:HarmonyOS技術(shù)社區(qū)
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

相關(guān)案例查看更多