Comparing version 0.3.0 to 0.3.1
{ | ||
"name": "okam-core", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "The extension for small program framework", | ||
@@ -33,3 +33,3 @@ "main": "index.js", | ||
}, | ||
"gitHead": "6ed61daccfe296b8679452d8b5a2b1af287344c3" | ||
"gitHead": "faa8448e03eec3a2c079c9b596f14876a14e88e1" | ||
} |
@@ -9,2 +9,11 @@ /** | ||
import App from '../App'; | ||
import extendApi from './api'; | ||
import {definePropertyValue} from '../util/index'; | ||
import base from '../base/base'; | ||
const baseApi = base.$api; | ||
Object.keys(extendApi).forEach(k => { | ||
definePropertyValue(baseApi, k, extendApi[k]); | ||
}); | ||
export default App; |
@@ -10,18 +10,3 @@ /** | ||
/** | ||
* Emit custom component event | ||
* | ||
* @param {...any} args the event arguments | ||
*/ | ||
componentBase.methods.$emit = function (...args) { | ||
this.__beforeEmit && this.__beforeEmit(args); | ||
this.$listener.emit.apply(this.$listener, args); | ||
let eventProp = args[0]; | ||
eventProp = 'on' + eventProp.charAt(0).toUpperCase() + eventProp.substr(1); | ||
let eventHandler = this.props[eventProp]; | ||
eventHandler.call(this, args[1]); | ||
}; | ||
export default Object.assign(componentBase, { | ||
const antComponent = Object.assign({}, componentBase, { | ||
didMount() { | ||
@@ -34,3 +19,2 @@ this.created(); | ||
didUpdate() { | ||
this.beforeUpdate && this.beforeUpdate(); | ||
this.updated && this.updated(); | ||
@@ -43,1 +27,21 @@ }, | ||
}); | ||
antComponent.methods = Object.assign({}, antComponent.methods, { | ||
/** | ||
* Emit custom component event | ||
* | ||
* @param {...any} args the event arguments | ||
*/ | ||
$emit(...args) { | ||
this.__beforeEmit && this.__beforeEmit(args); | ||
this.$listener.emit.apply(this.$listener, args); | ||
let eventProp = args[0]; | ||
eventProp = 'on' + eventProp.charAt(0).toUpperCase() + eventProp.substr(1); | ||
let eventHandler = this.props[eventProp]; | ||
eventHandler.call(this, args[1]); | ||
} | ||
}); | ||
export default antComponent; |
@@ -10,3 +10,2 @@ /** | ||
import {promisifyApis, interceptApis} from '../na/api'; | ||
import {getApis, setApis} from '../na/api'; | ||
@@ -29,12 +28,2 @@ /** | ||
let apis = getApis(); | ||
if ((promiseApis && promiseApis.length) | ||
|| (interceptAPis && Object.keys(interceptAPis).length) | ||
) { | ||
// create new apis to ami to override existed apis | ||
apis = Object.assign({}, apis); | ||
setApis(apis); | ||
} | ||
this.$api = apis; | ||
promisifyApis(promiseApis, this); | ||
@@ -41,0 +30,0 @@ interceptApis(interceptAPis, '$api', this); |
@@ -21,2 +21,7 @@ /** | ||
/** | ||
* The native API | ||
*/ | ||
$api: Object.create(na.env), | ||
/** | ||
* The global object | ||
@@ -23,0 +28,0 @@ * |
@@ -10,3 +10,2 @@ /** | ||
import EventListener from '../util/EventListener'; | ||
import {getApis} from '../na/api'; | ||
import base from './base'; | ||
@@ -25,3 +24,2 @@ | ||
this.$app = getCurrApp(); | ||
this.$api = getApis(); | ||
@@ -28,0 +26,0 @@ this.$listener = new EventListener(); |
@@ -19,2 +19,3 @@ /** | ||
onLoad(query) { | ||
this.$isPage = true; | ||
this.$query = query || {}; | ||
@@ -21,0 +22,0 @@ |
@@ -8,6 +8,40 @@ /** | ||
import observable, {setPropDataKey} from '../index'; | ||
import observable, {setObservableContext} from '../index'; | ||
import {observableArray, overrideArrayMethods} from '../array'; | ||
import {component as antApi, array as antArray} from './array'; | ||
setPropDataKey('props'); | ||
setObservableContext('props', true); | ||
Object.assign(observable.component.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); | ||
} | ||
}); | ||
}; | ||
export default observable; |
@@ -8,2 +8,9 @@ /** | ||
const hasProto = '__proto__' in {}; | ||
/** | ||
* The default override Array APIs to proxy array data update | ||
* | ||
* @type {Object} | ||
*/ | ||
export const observableArray = { | ||
@@ -85,2 +92,33 @@ push(observer, rawPush, ...items) { | ||
/** | ||
* 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 | ||
@@ -108,39 +146,32 @@ * | ||
let overrideArrApis = observableArray; | ||
/** | ||
* Extend the array operation methods | ||
* Make array observable | ||
* | ||
* @param {Object} extension the array method extension | ||
* @param {boolean} isOverride whether override default array extend apis, | ||
* by default false, for test purpose | ||
* @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 function extendArrayMethods(extension, isOverride) { | ||
export default function makeArrayObservable(arr, observer, isPage) { | ||
let arrayMethods; | ||
/* istanbul ignore next */ | ||
if (isOverride) { | ||
overrideArrApis = extension; | ||
if (hasProto) { | ||
arrayMethods = Object.create(Array.prototype); | ||
/* eslint-disable no-proto */ | ||
arr.__proto__ = arrayMethods; | ||
} | ||
else { | ||
/* istanbul ignore next */ | ||
Object.assign(overrideArrApis, extension); | ||
arrayMethods = arr; | ||
} | ||
} | ||
/** | ||
* Make array observable | ||
* | ||
* @param {Array} arr the array to observe | ||
* @param {Observer} observer the observer | ||
* @return {Array} | ||
*/ | ||
export default function makeArrayObservable(arr, observer) { | ||
let overrideArrApis = isPage ? overridePageArrApis : overrideComponentArrApis; | ||
Object.keys(overrideArrApis).forEach(method => { | ||
let rawMethod = arr[method]; | ||
arr[method] = overrideArrApis[method].bind(arr, observer, rawMethod); | ||
let rawMethod = arrayMethods[method]; | ||
arrayMethods[method] = overrideArrApis[method].bind(arr, observer, rawMethod); | ||
}); | ||
arr.setItem = updateArrayItem.bind(arr, observer); | ||
arr.getItem = getArrayItem.bind(arr, observer); | ||
arrayMethods.setItem = updateArrayItem.bind(arr, observer); | ||
arrayMethods.getItem = getArrayItem.bind(arr, observer); | ||
return arr; | ||
} |
@@ -24,2 +24,9 @@ /** | ||
/** | ||
* Whether skip the `updated` hook | ||
* | ||
* @type {boolean} | ||
*/ | ||
let shouldSkipUpdateHook = false; | ||
/** | ||
* Make computed props observable | ||
@@ -67,3 +74,8 @@ * | ||
let observer = new Observer(ctx, ctx[propDataKey] || {}, null, true); | ||
let observer = new Observer( | ||
ctx, | ||
ctx[propDataKey] || /* istanbul ignore next */ {}, | ||
null, | ||
true | ||
); | ||
let propsObj = {}; | ||
@@ -108,38 +120,10 @@ | ||
/** | ||
* Initialize the props to add observer to the prop to listen the prop change. | ||
* Set observable context setting | ||
* | ||
* @inner | ||
* @param {Object} ctx the component definition context | ||
* @param {boolean} isPage whether is page component | ||
*/ | ||
function initProps(ctx, isPage) { | ||
// cache the raw props information because the mini program will merge data | ||
// and props later on. | ||
let props = ctx.props; | ||
if (!props) { | ||
return; | ||
} | ||
let rawProps = Object.assign({}, props); | ||
ctx.rawProps = rawProps; | ||
normalizeExtendProp(ctx, 'rawProps', '$rawProps', isPage); | ||
Object.keys(props).forEach(p => { | ||
let value = props[p]; | ||
let rawObserver = value.observer; | ||
value.observer = function (newVal, oldVal, changePath) { | ||
rawObserver && rawObserver.call(this, newVal, oldVal, changePath); | ||
let propObserver = this.__propsObserver; | ||
propObserver && propObserver.firePropValueChange(p, newVal, oldVal); | ||
}; | ||
}); | ||
} | ||
/** | ||
* Set the component property data key | ||
* | ||
* @param {string} key the prop data key | ||
* @param {boolean} ignoreUpdateHook whether skip update hook | ||
*/ | ||
export function setPropDataKey(key) { | ||
key && (propDataKey = key); | ||
export function setObservableContext(key, ignoreUpdateHook) { | ||
propDataKey = key; | ||
shouldSkipUpdateHook = !!ignoreUpdateHook; | ||
} | ||
@@ -151,12 +135,22 @@ | ||
/** | ||
* The instance initialization before the instance is normalized and created. | ||
* Initialize the props to add observer to the prop to listen the prop change. | ||
* | ||
* @param {boolean} isPage whether is page component | ||
* @private | ||
*/ | ||
$init(isPage) { | ||
initProps(this, isPage); | ||
// normalize extend computed property | ||
normalizeExtendProp(this, 'computed', '$rawComputed', isPage); | ||
// cache the raw props information because the mini program will merge data | ||
// and props later on. | ||
let props = this.props; | ||
if (!props) { | ||
return; | ||
} | ||
let rawProps = Object.assign({}, props); | ||
this._rawProps = rawProps; | ||
normalizeExtendProp(this, '_rawProps', '$rawProps', isPage); | ||
this.__initProps && this.__initProps(); | ||
}, | ||
@@ -194,3 +188,3 @@ | ||
this.afterObserverInit && this.afterObserverInit(); | ||
this.__afterObserverInit && this.__afterObserverInit(); | ||
}, | ||
@@ -253,3 +247,3 @@ | ||
// call lifecycle updated hook | ||
this.updated && this.updated(); | ||
shouldSkipUpdateHook || (this.updated && this.updated()); | ||
}, | ||
@@ -256,0 +250,0 @@ |
@@ -50,7 +50,8 @@ /** | ||
* @param {Array} arr the array data to proxy | ||
* @param {boolean} isPage whether is page component | ||
* @return {Array} | ||
*/ | ||
export function proxyArray(observer, arr) { | ||
export function proxyArray(observer, arr, isPage) { | ||
let newArr = []; | ||
makeArrayObservable(newArr, observer); | ||
makeArrayObservable(newArr, observer, isPage); | ||
@@ -90,2 +91,37 @@ // XXX: copy array | ||
/** | ||
* Set context data | ||
* | ||
* @private | ||
* @param {*} value the value to set | ||
*/ | ||
setContextData(value) { | ||
let paths = this.paths; | ||
let result = this.ctx.data; | ||
let lastIdx = paths.length - 1; | ||
for (let i = 0; i < lastIdx; i++) { | ||
let p = paths[i]; | ||
result = result[p]; | ||
} | ||
/* istanbul ignore next */ | ||
if (lastIdx >= 0) { | ||
result[paths[lastIdx]] = value; | ||
} | ||
} | ||
/** | ||
* Get context data | ||
* | ||
* @return {*} | ||
*/ | ||
getContextData() { | ||
let paths = this.paths; | ||
let result = this.ctx.data; | ||
for (let i = 0, len = paths.length; i < len; i++) { | ||
result = result[paths[i]]; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Add dependence to current target computed property | ||
@@ -126,3 +162,3 @@ * | ||
let observer = new Observer(ctx, value, paths, this.isProps); | ||
return (observeData[k] = proxyArray(observer, value)); | ||
return (observeData[k] = proxyArray(observer, value, ctx.$isPage)); | ||
} | ||
@@ -195,2 +231,5 @@ else if (value && typeof value === 'object') { | ||
} | ||
else { | ||
this.setContextData(this.rawData); | ||
} | ||
@@ -197,0 +236,0 @@ this.ctx.$setData({[selector]: val}); |
@@ -11,24 +11,21 @@ /** | ||
const componentApi = { | ||
$pushData(path, value) { | ||
__pushData(path, value) { | ||
this.pushData(path, value, this.__nextTickCallback); | ||
}, | ||
$popData(path) { | ||
__popData(path) { | ||
this.popData(path, this.__nextTickCallback); | ||
}, | ||
$unshiftData(path, value) { | ||
__unshiftData(path, value) { | ||
// FIXME: native bug | ||
this.unshiftData(path, value, this.__nextTickCallback); | ||
}, | ||
$shiftData(path) { | ||
__shiftData(path) { | ||
this.shiftData(path, this.__nextTickCallback); | ||
}, | ||
// $removeAtData(path, value) { | ||
// // TODO usage of removeAtData | ||
// this.removeAtData(path, value, this.__nextTickCallback); | ||
// }, | ||
$spliceData(path, spliceArgs) { | ||
__spliceData(path, spliceArgs) { | ||
// FIXME: native bug | ||
this.spliceData(path, spliceArgs, this.__nextTickCallback); | ||
@@ -41,3 +38,7 @@ } | ||
let {ctx, selector} = observer; | ||
items.forEach(data => ctx.$pushData(selector, data)); | ||
items.forEach(data => ctx.__pushData(selector, data)); | ||
// update the cache raw data | ||
observer.rawData = observer.getContextData(); | ||
return rawPush.apply(this, items); | ||
@@ -52,4 +53,7 @@ }, | ||
ctx.$shiftData(selector); | ||
ctx.__shiftData(selector); | ||
// update the cache raw data | ||
observer.rawData = observer.getContextData(); | ||
return rawShift.call(this); | ||
@@ -60,4 +64,7 @@ }, | ||
let {ctx, selector} = observer; | ||
ctx.$unshiftData(selector, items); | ||
ctx.__unshiftData(selector, items); | ||
// update the cache raw data | ||
observer.rawData = observer.getContextData(); | ||
return rawUnshift.apply(this, items); | ||
@@ -71,4 +78,7 @@ }, | ||
} | ||
ctx.$popData(selector); | ||
ctx.__popData(selector); | ||
// update the cache raw data | ||
observer.rawData = observer.getContextData(); | ||
return rawPop.call(this); | ||
@@ -79,4 +89,7 @@ }, | ||
let {ctx, selector} = observer; | ||
ctx.$spliceData(selector, args); | ||
ctx.__spliceData(selector, args); | ||
// update the cache raw data | ||
observer.rawData = observer.getContextData(); | ||
return rawSplice.apply(this, args); | ||
@@ -83,0 +96,0 @@ } |
@@ -9,8 +9,18 @@ /** | ||
import observable from '../index'; | ||
import {extendArrayMethods} from '../array'; | ||
import {component as swanApi, array as swanArray} from './array'; | ||
// import {observableArray, overrideArrayMethods} from '../array'; | ||
// import {component as swanApi, array as swanArray} from './array'; | ||
import initProps from '../initProps'; | ||
Object.assign(observable.component.methods, swanApi); | ||
extendArrayMethods(swanArray); | ||
// observable.page = { | ||
// methods: swanApi | ||
// }; | ||
// Object.assign(observable.component.methods, swanApi); | ||
// override the Page array API, as for the native Array data operation API | ||
// only supported in page currently | ||
// let arrApis = Object.assign({}, observableArray, swanArray); | ||
// overrideArrayMethods(arrApis, true); | ||
observable.component.__initProps = initProps; | ||
export default observable; |
@@ -131,3 +131,3 @@ /** | ||
*/ | ||
afterObserverInit() { | ||
__afterObserverInit() { | ||
let watch = this.$rawWatch; | ||
@@ -134,0 +134,0 @@ if (typeof watch === 'function') { |
@@ -21,3 +21,3 @@ /** | ||
/** | ||
* Normalize extended property | ||
* Normalize extended property: convert the extended prop to methods prop | ||
* | ||
@@ -24,0 +24,0 @@ * @param {Object} component the component instance |
@@ -6,26 +6,5 @@ /** | ||
import {isFunction, isObject, isPromise} from '../util/index'; | ||
import {env} from './index'; | ||
import {isFunction, isObject, isPromise, definePropertyValue} from '../util/index'; | ||
let myPromisifyApis; | ||
/** | ||
* Get mini program APIs | ||
* | ||
* @return {Object} | ||
*/ | ||
export function getApis() { | ||
return myPromisifyApis || env; | ||
} | ||
/** | ||
* Set mini program APIs | ||
* | ||
* @param {Object} apis the apis to set | ||
*/ | ||
export function setApis(apis) { | ||
myPromisifyApis = apis; | ||
} | ||
/** | ||
* Promisify the given function. | ||
@@ -79,4 +58,5 @@ * Notice: the function must be async function and the the first param passed to | ||
let allApis = context.$api; | ||
apis.forEach(key => { | ||
let api = context.$api[key]; | ||
let api = allApis[key]; | ||
@@ -87,3 +67,3 @@ if (!api) { | ||
context.$api[key] = promisify(api, context.$api); | ||
definePropertyValue(allApis, key, promisify(api, context.$api)); | ||
}); | ||
@@ -176,5 +156,9 @@ } | ||
if (rawApi) { | ||
allApis[apiName] = proxyAPI.bind(null, ctx, rawApi, apis[apiName]); | ||
definePropertyValue( | ||
allApis, | ||
apiName, | ||
proxyAPI.bind(null, ctx, rawApi, apis[apiName]) | ||
); | ||
} | ||
}); | ||
} |
@@ -118,1 +118,26 @@ /** | ||
} | ||
/** | ||
* Define property value | ||
* | ||
* @param {Object} obj the object to define property | ||
* @param {string} name the property name | ||
* @param {*} value the property value to define | ||
*/ | ||
export function definePropertyValue(obj, name, value) { | ||
let desc = Object.getOwnPropertyDescriptor(obj, name); | ||
if (desc && !desc.configurable) { | ||
obj[name] = value; | ||
return; | ||
} | ||
desc || (desc = {enumerable: true, configurable: true}); | ||
if (desc.get) { | ||
desc.get = () => value; | ||
} | ||
else { | ||
desc.value = value; | ||
} | ||
Object.defineProperty(obj, name, desc); | ||
} |
125232
64
4258