Skip to content

Commit

Permalink
feat(okam-core): add vuex support and reafctor array observable defin…
Browse files Browse the repository at this point in the history
…ition
  • Loading branch information
wuhy committed Jan 20, 2019
1 parent 253b1bc commit e945934
Show file tree
Hide file tree
Showing 29 changed files with 1,663 additions and 355 deletions.
8 changes: 7 additions & 1 deletion packages/okam-core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/okam-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"nyc": "^13.0.1",
"promise-polyfill": "^8.0.0",
"redux": "^4.0.1",
"regenerator-runtime": "^0.12.1"
"regenerator-runtime": "^0.12.1",
"vuex": "^3.1.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

'use strict';

import {isPlainObject} from '../../../util/index';
import {isPlainObject} from '../../util/index';

function isShadowEqual(obj1, obj2) {
let keys = Object.keys(obj1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export default class ComputedObserver {
this.computed = this.initComputedProps(computedInfo);
this.watchComputed = {};

ctx.data || (ctx.data = {});

let watcher = this.handleDepChange.bind(this);
ctx.$dataListener.on('change', watcher);
this.computedCounter = 0;
Expand Down Expand Up @@ -331,9 +333,12 @@ export default class ComputedObserver {
value = ctx.data[k];
}

// maybe the computed prop is dependence on other computed props which
// has not collected deps yet, we need to call getter to collect deps
if (!this.deps[k]) {
let getter = this.computed[k].getter;
value = getter.call(ctx, ctx);
// watch computed props is not view data, so don't put it to data
watchInfo || (ctx.data[k] = value);
}

Expand Down
67 changes: 62 additions & 5 deletions packages/okam-core/src/extend/data/observable/Observer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,62 @@

'use strict';

import makeArrayObservable from './array';
import {addDep, getDataSelector, addSelectorPath} from './helper';

/**
* Update array item value
*
* @param {Observer} observer the observer
* @param {number} idx the index to update
* @param {*} value the value to set
*/
function updateArrayItem(observer, idx, value) {
observer.set(idx, value);
this[idx] = value;
}

/**
* Get the array item value
*
* @param {Observer} observer the observer
* @param {number} idx the index to get
* @return {*}
*/
function getArrayItem(observer, idx) {
return observer.get(idx);
}

/**
* Make array observable
*
* @param {Array} arr the array to observe
* @param {Observer} observer the observer
* @param {Object} proxyArrApis the array api to proxy
* @return {Array}
*/
function makeArrayObservable(arr, observer, proxyArrApis) {
// Here, not using __proto__ implementation, there are two import reasons:
// First, considering __proto__ will be deprecated and is not recommended to use
// Second, some plugins like weixin contact plugin will change array definition,
// the array instance __proto__ property does not contains any like `push`
// `pop` API, and these API only existed in the instance context.
// So, like the code as the following will not work correctly,
// a = []; // a.__proto__.push is not defined, a.push is defined
// a.__proto__.push = function () {};
// a.push(2); // always call the native push, not the override push method
// Therefor, using __proto__ to proxy the array method will not work

Object.keys(proxyArrApis).forEach(method => {
let rawMethod = arr[method];
arr[method] = proxyArrApis[method].bind(arr, observer, rawMethod);
});

arr.setItem = updateArrayItem.bind(arr, observer);
arr.getItem = getArrayItem.bind(arr, observer);

return arr;
}

/**
* Proxy the data object to observe
*
Expand Down Expand Up @@ -47,12 +100,12 @@ export function proxyObject(observer, data, root) {
*
* @param {Observer} observer the observer to observe array
* @param {Array} arr the array data to proxy
* @param {boolean} isPage whether is page component
* @param {Object} proxyArrApis the array api to proxy
* @return {Array}
*/
export function proxyArray(observer, arr, isPage) {
export function proxyArray(observer, arr, proxyArrApis) {
let newArr = [];
makeArrayObservable(newArr, observer, isPage);
makeArrayObservable(newArr, observer, proxyArrApis);

// XXX: copy array
// we cannot proxy array element visited by index, so we should not proxy array element by default
Expand Down Expand Up @@ -157,7 +210,11 @@ export default class Observer {
if (Array.isArray(value)) {
paths || (paths = this.getPaths(k));
let observer = new Observer(ctx, value, paths, this.isProps);
return (observeData[k] = proxyArray(observer, value, ctx.$isPage));
let proxyApis = ctx.__proxyArrayApis;
if (typeof proxyApis === 'function') {
proxyApis = ctx.__proxyArrayApis = proxyApis();
}
return (observeData[k] = proxyArray(observer, value, proxyApis));
}
else if (value && typeof value === 'object') {
paths || (paths = this.getPaths(k));
Expand Down
22 changes: 22 additions & 0 deletions packages/okam-core/src/extend/data/observable/ant/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ Object.keys(observableArray).forEach(k => {
};
});

Object.assign(observableArray, {
sort(observer, rawSort, ...args) {
let rawData = observer.rawData;
rawSort.apply(rawData, args);

let result = rawSort.apply(this, args);
observer.set(null, rawData);

return result;
},

reverse(observer, rawReverse) {
let rawData = observer.rawData;
rawData.reverse();

let result = rawReverse.call(this);
observer.set(null, rawData);

return result;
}
});

export {
observableArray as array,
componentApi as component
Expand Down
94 changes: 48 additions & 46 deletions packages/okam-core/src/extend/data/observable/ant/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,56 @@
'use strict';

import observable, {setObservableContext} from '../base';
import {observableArray, overrideArrayMethods} from '../array';
import {component as antApi, array as antArray} from './array';
import {component as antApi, array as proxyArrayApis} from './array';

setObservableContext('props', true);

let componentExtension = observable.component;
let rawCreated = componentExtension.created;
componentExtension.created = function () {
if (this.$rawComputed) {
// fix ant reference bug: `this.data.xx` operation is not allowed
// when page onload, otherwise it'll affect the init data state
// of the page when load next time.
// So, here create a shadow copy of data.
this.data = Object.assign({}, this.data);
}
rawCreated.call(this);
};

Object.assign(componentExtension.methods, antApi);

let arrApis = Object.assign({}, observableArray, antArray);
overrideArrayMethods(arrApis, true);
overrideArrayMethods(arrApis, false);

/**
* View update hook
*
* @private
* @param {Object} prevProps the previous property data before update
*/
observable.component.didUpdate = function (prevProps) {
let propObserver = this.__propsObserver;
if (!propObserver) {
return;
}

let currProps = this.props;
// update the cache props data, as for the prop data will be override
// when prop change, it leads to the cache props data will not refer to
// the new props data
propObserver.rawData = currProps;
Object.keys(prevProps).forEach(k => {
let newVal = currProps[k];
let oldVal = prevProps[k];
if (newVal !== oldVal) {
propObserver.firePropValueChange(k, newVal, oldVal);
const rawCreated = observable.created;
const observableAntComponent = Object.assign({}, observable, {
created() {
if (this.$rawComputed) {
// fix ant reference bug: `this.data.xx` operation is not allowed
// when page onload, otherwise it'll affect the init data state
// of the page when load next time.
// So, here create a shadow copy of data.
this.data = Object.assign({}, this.data);
}
rawCreated.call(this);
},

/**
* View update hook
*
* @private
* @param {Object} prevProps the previous property data before update
*/
didUpdate(prevProps) {
let propObserver = this.__propsObserver;
if (!propObserver) {
return;
}
});
};

export default observable;
let currProps = this.props;
// update the cache props data, as for the prop data will be override
// when prop change, it leads to the cached props data will not refer to
// the new props data
propObserver.rawData = currProps;
Object.keys(prevProps).forEach(k => {
let newVal = currProps[k];
let oldVal = prevProps[k];
if (newVal !== oldVal) {
propObserver.firePropValueChange(k, newVal, oldVal);
}
});
},

proxyArrayApis
});

observableAntComponent.methods = Object.assign(
{}, observableAntComponent.methods, antApi,
);

export default {
component: observableAntComponent
};
92 changes: 2 additions & 90 deletions packages/okam-core/src/extend/data/observable/array.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
/**
* @file Array data update proxy
* @file Array proxy apis
* @author [email protected]
*/

'use strict';

// const hasProto = '__proto__' in {};

/**
* The default override Array APIs to proxy array data update
*
* @type {Object}
*/
export const observableArray = {
export default {
push(observer, rawPush, ...items) {
let rawData = observer.rawData;
let idx = rawData.length;
Expand Down Expand Up @@ -94,89 +92,3 @@ export const observableArray = {
return result;
}
};

/**
* The Page Array APIs to override
*
* @inner
* @type {Object}
*/
let overridePageArrApis = observableArray;

/**
* The component Array APIs to override
*
* @inner
* @type {Object}
*/
let overrideComponentArrApis = observableArray;

/**
* Extend the array operation methods
*
* @param {Object} arrApis the array methods to override
* @param {boolean} isPage whether is page Array APIs to override
*/
export function overrideArrayMethods(arrApis, isPage) {
if (isPage) {
overridePageArrApis = arrApis;
}
else {
overrideComponentArrApis = arrApis;
}
}

/**
* Update array item value
*
* @param {Observer} observer the observer
* @param {number} idx the index to update
* @param {*} value the value to set
*/
function updateArrayItem(observer, idx, value) {
observer.set(idx, value);
this[idx] = value;
}

/**
* Get the array item value
*
* @param {Observer} observer the observer
* @param {number} idx the index to get
* @return {*}
*/
function getArrayItem(observer, idx) {
return observer.get(idx);
}

/**
* Make array observable
*
* @param {Array} arr the array to observe
* @param {Observer} observer the observer
* @param {boolean} isPage whether is page Array APIs to override
* @return {Array}
*/
export default function makeArrayObservable(arr, observer, isPage) {
// Here, not using __proto__ implementation, there are two import reasons:
// First, considering __proto__ will be deprecated and is not recommended to use
// Second, some plugins like weixin contact plugin will change array definition,
// the array instance __proto__ property does not contains any like `push`
// `pop` API, and these API only existed in the instance context.
// So, like the code as the following will not work correctly,
// a = []; // a.__proto__.push is not defined, a.push is defined
// a.__proto__.push = function () {};
// a.push(2); // always call the native push, not the override push method
// Therefor, using __proto__ to proxy the array method will not work

let overrideArrApis = isPage ? overridePageArrApis : overrideComponentArrApis;
Object.keys(overrideArrApis).forEach(method => {
let rawMethod = arr[method];
arr[method] = overrideArrApis[method].bind(arr, observer, rawMethod);
});

arr.setItem = updateArrayItem.bind(arr, observer);
arr.getItem = getArrayItem.bind(arr, observer);

return arr;
}
Loading

0 comments on commit e945934

Please sign in to comment.