Knockoutjs 響應式計算研究

reactive programming

https://en.wikipedia.org/wiki/Reactive_programminghtml

In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.[citation needed]react

git

 

Reactive programming has been proposed as a way to simplify the creation of interactive user interfaces and near-real-time system animation.[citation needed]github

For example, in a model–view–controller (MVC) architecture, reactive programming can facilitate changes in an underlying model that are reflected automatically in an associated view.[1]express

 

knockoutjs reactive

https://knockoutjs.com/documentation/observables.htmlthis

第一,定義 基礎的 可觀察對象:lua

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};

第二, 定義存在依賴其餘可觀察對象的 計算對象spa

function AppViewModel() {
    // ... leave firstName and lastName unchanged ...
 
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
}

第3、綁定到視圖prototype

The name is <span data-bind="text: fullName"></span>

 

若是基礎的 可觀察對象 改變, 則視圖會自動跟着改變。code

 

實現原理

https://github.com/fanqingsong/knockout-prototype

 

knockout模塊實現, 包括 observale 和 computed 接口實現, 以及內部依賴管理實現。

let ko = {}

ko.say = () => console.log("hello world")

ko.dependency = (() => {
    let callerstack = []
    let currentCaller

    return {
        currentCaller,
        callerstack
    }
})();


ko.observable = (initVal) => {
    // for record caller, ie observer
    let observerCache = [];

    // store current observable value
    let currentVal = "";
    if(initVal !== undefined){
        console.log("initVal 0=", initVal)
        currentVal = initVal;
    }

    let observable = (newVal) => {
        // for read, subscribe to caller
        if( newVal === undefined ) {
            if (ko.dependency.currentCaller) {
                observerCache.push(ko.dependency.currentCaller)
            }

            return currentVal;
        // for write
        } else {
            currentVal = newVal;

            console.log("===",observerCache.length)

            for (const index in observerCache) {
                console.log("-----------3-", observerCache[index]);
                observerCache[index].callEvalWithDeps();
            }
        }
    }

    return observable
}

ko.computed = (evalFunc) => {
    // store current observable value
    let currentVal = "";

    let unValuated = true;

    let computedObservable = () => {

        if (unValuated){
            computedObservable.callEvalWithDeps();
            unValuated = false;
        }

        return currentVal;
    }

    computedObservable.callEvalWithDeps = () => {
        if (ko.dependency.currentCaller) {
            ko.dependency.callerstack.push(ko.dependency.currentCaller)
        }

        ko.dependency.currentCaller = computedObservable;

        currentVal = evalFunc();

        let parent = ko.dependency.callerstack.pop();
        ko.dependency.currentCaller = parent;
    }

    return computedObservable
}

module.exports = ko

 

調用

import ko from "./knockout.js"
ko.say();

let aObservable = ko.observable("a");
console.log("aObservable=", aObservable());


let bComputed = ko.computed(() => {
  let result = aObservable() + "b";

  console.log("result=", result);

  return result;
})

// bind subscription to aObservable
let bVal = bComputed();
console.log("bVal=", bVal);

// trigger reactive effect
aObservable("c");

console.log("bComputed=", bComputed())

 

運行

 

 

參考:

https://knockoutjs.com/documentation/computed-dependency-tracking.html

https://www.reactivemanifesto.org/

相關文章
相關標籤/搜索