@cicada/render
Advanced tools
Comparing version 1.1.22-alpha1 to 1.1.22-alpha10
@@ -45,2 +45,3 @@ 'use strict'; | ||
exports.checkPlainValue = checkPlainValue; | ||
exports.isDebug = isDebug; | ||
@@ -336,2 +337,6 @@ var _isPlainObject = require('lodash/isPlainObject'); | ||
return true; | ||
} | ||
function isDebug() { | ||
return typeof window !== 'undefined' && window.cicadaEnv === 'DEBUG'; | ||
} |
@@ -283,2 +283,7 @@ 'use strict'; | ||
} | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('DidMount: ' + displayName + '(' + this.getResolvedRootPath() + ')'); | ||
/* eslint-enable no-console */ | ||
} | ||
} | ||
@@ -311,4 +316,30 @@ }, { | ||
} | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('UnMount: ' + displayName + '(' + this.getResolvedRootPath() + ')'); | ||
/* eslint-enable no-console */ | ||
} | ||
} | ||
// for debug | ||
}, { | ||
key: 'componentWillUpdate', | ||
value: function componentWillUpdate() { | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('Will Update: ' + displayName + '(' + this.getResolvedRootPath() + ')'); | ||
/* eslint-enable no-console */ | ||
} | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('Update: ' + displayName + '(' + this.getResolvedRootPath() + ')'); | ||
/* eslint-enable no-console */ | ||
} | ||
} | ||
}, { | ||
key: 'convertToControlledListener', | ||
@@ -315,0 +346,0 @@ value: function convertToControlledListener(injectedComponentArg) { |
@@ -45,24 +45,26 @@ 'use strict'; | ||
var _common = require('./common'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function noop() {} /** | ||
* convertFragment 负责将 config 片段转换为金蝉组件。 | ||
* 这里的重点问题有两个: | ||
* | ||
* 1. 由于金蝉组件都是受控,所以我们转换出的组件也必须是受控的。由于之前 | ||
* Render 的设计已经考虑了受控形式,所以这里只要遵照 Render 的规则给其传入 | ||
* onChange 函数即可变为受控。 | ||
* | ||
* 2. 转换为受控形式之后, 组件的渲染都是由外部传入新数据来控制的。如果包装后 | ||
* 的组件也采取这种获取新值后重新渲染的方式,那么在内部结构变大后,可能会产 | ||
* 生性能问题。例如组件中的某一个 input 一直输入,每次都要从外部全部刷新,会 | ||
* 感觉卡顿。为了解决这个问题,首先,转换后的组件声明了 shouldComponentUpdate | ||
* 为 false,永远不重新渲染。然后,我们让内部 Render 在发生变化先用 stateTree 的 | ||
* cache 缓存住变化,不通知组件更新,仍然往外跑一遍流程,然后在外部通知要重渲染时 | ||
* flush 内部的 stateTree,继续使用 Render 内部的精确更新。 | ||
*/ | ||
/** | ||
* convertFragment 负责将 config 片段转换为金蝉组件。 | ||
* 这里的重点问题有两个: | ||
* | ||
* 1. 由于金蝉组件都是受控,所以我们转换出的组件也必须是受控的。由于之前 | ||
* Render 的设计已经考虑了受控形式,所以这里只要遵照 Render 的规则给其传入 | ||
* onChange 函数即可变为受控。 | ||
* | ||
* 2. 转换为受控形式之后, 组件的渲染都是由外部传入新数据来控制的。如果包装后 | ||
* 的组件也采取这种获取新值后重新渲染的方式,那么在内部结构变大后,可能会产 | ||
* 生性能问题。例如组件中的某一个 input 一直输入,每次都要从外部全部刷新,会 | ||
* 感觉卡顿。为了解决这个问题,首先,转换后的组件声明了 shouldComponentUpdate | ||
* 为 false,永远不重新渲染。然后,我们让内部 Render 在发生变化先用 stateTree 的 | ||
* cache 缓存住变化,不通知组件更新,仍然往外跑一遍流程,然后在外部通知要重渲染时 | ||
* flush 内部的 stateTree,继续使用 Render 内部的精确更新。 | ||
*/ | ||
/* eslint-disable no-nested-ternary */ | ||
function noop() {} | ||
function computeFrom(linkState, utilInstances, _ref) { | ||
@@ -260,2 +262,7 @@ var state = _ref.state; | ||
instance.stateTree.flush(); | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('Fragment stateTree flush'); | ||
/* eslint-enable no-console */ | ||
} | ||
} | ||
@@ -346,2 +353,8 @@ | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('Fragment render'); | ||
/* eslint-enable no-console */ | ||
} | ||
return _react2.default.createElement(_Render2.default, { | ||
@@ -348,0 +361,0 @@ stateTree: instance.stateTree, |
@@ -82,3 +82,3 @@ 'use strict'; | ||
// 避免state数据传入到react开发版props属性被设置为frozen导致无法set | ||
function simpleCloneWithoutfrozen(state) { | ||
function simpleCloneWithoutFrozen(state) { | ||
if (!isObject(state)) return state; | ||
@@ -164,4 +164,6 @@ return (0, _util.mapValues)(state, function (v) { | ||
if (!visible) { | ||
if (!lastStyleList.includes(DISPLAY_NONE)) { | ||
ref.setAttribute('data-cicada-' + DISPLAY_NONE, ''); | ||
var displayNoneAttr = 'data-cicada-' + DISPLAY_NONE; | ||
// CAUTION 这里要拿实际的 ref 去检验,因为有可能真实的 ref 被外部更改了。 | ||
if (ref.attributes[displayNoneAttr] === undefined) { | ||
ref.setAttribute(displayNoneAttr, ''); | ||
} | ||
@@ -171,4 +173,5 @@ addedStyleList.push(DISPLAY_NONE); | ||
styleList.forEach(function (cls) { | ||
if (!lastStyleList.includes(cls)) { | ||
ref.setAttribute('data-cicada-' + cls, ''); | ||
var clsAttr = 'data-cicada-' + cls; | ||
if (ref.attributes[clsAttr] === undefined) { | ||
ref.setAttribute(clsAttr, ''); | ||
} | ||
@@ -234,3 +237,3 @@ addedStyleList.push(cls); | ||
componentArg.props = (0, _extends3.default)({}, componentArg.props, injectedExternalProps); | ||
componentArg.state = _constant.isDebug ? simpleCloneWithoutfrozen(componentArg.state) : componentArg.state; | ||
componentArg.state = _constant.isDebug ? simpleCloneWithoutFrozen(componentArg.state) : componentArg.state; | ||
var styleList = appearance[id].styleList; | ||
@@ -237,0 +240,0 @@ |
@@ -11,2 +11,14 @@ 'use strict'; | ||
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); | ||
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); | ||
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); | ||
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); | ||
var _toArray2 = require('babel-runtime/helpers/toArray'); | ||
var _toArray3 = _interopRequireDefault(_toArray2); | ||
exports.default = createDynamicRender; | ||
@@ -22,6 +34,2 @@ | ||
var _cloneDeep = require('lodash/cloneDeep'); | ||
var _cloneDeep2 = _interopRequireDefault(_cloneDeep); | ||
var _Render = require('./Render'); | ||
@@ -37,2 +45,4 @@ | ||
var _common = require('./common'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -45,2 +55,26 @@ | ||
function consumeStateTreeBuffer(stateTree, buffer) { | ||
buffer.forEach(function (_ref) { | ||
var _ref2 = (0, _toArray3.default)(_ref), | ||
path = _ref2[0], | ||
value = _ref2[1], | ||
arg = _ref2.slice(2); | ||
return stateTree.set.apply(stateTree, [path, value].concat((0, _toConsumableArray3.default)(arg))); | ||
}); | ||
} | ||
function constructInitialStateTree(buffer) { | ||
var output = {}; | ||
// CAUTION 当重新创建时,没有 merge,要求用户提供完整的数据 | ||
buffer.forEach(function (_ref3) { | ||
var _ref4 = (0, _slicedToArray3.default)(_ref3, 2), | ||
path = _ref4[0], | ||
value = _ref4[1]; | ||
return _exist2.default.set(output, path, value, true); | ||
}); | ||
return output; | ||
} | ||
function createDynamicRender(createStateTree, createAppearance, createBackground, backgroundDef) { | ||
@@ -60,6 +94,6 @@ var getDefaultState = function getDefaultState() { | ||
var defaultListeners = { | ||
onChange: function onChange(_ref, changeFn, name, path) { | ||
var instance = _ref.instance, | ||
state = _ref.state, | ||
statePath = _ref.statePath; | ||
onChange: function onChange(_ref5, changeFn, name, path) { | ||
var instance = _ref5.instance, | ||
state = _ref5.state, | ||
statePath = _ref5.statePath; | ||
@@ -90,4 +124,4 @@ if (changeFn === undefined) return; | ||
// state 的 proxy 模式,可以接管 stateTree 的变化 | ||
var getStateProxy = function getStateProxy(_ref2) { | ||
var instance = _ref2.instance; | ||
var getStateProxy = function getStateProxy(_ref6) { | ||
var instance = _ref6.instance; | ||
var initialValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
@@ -97,49 +131,80 @@ | ||
instance.config = initialValue.config ? (0, _extends3.default)({}, initialValue.config) : {}; | ||
return { | ||
get: function get(statePath) { | ||
var _instance$stateTree; | ||
// 所有的对 stateTree 的改动都先存到这里,只有真正受到通知的时候才真正的 flush | ||
// 这样设计的原因是,有可能改动了 config,有可能没改。 | ||
// 1 在改动了 config 的时候,这些 value 是当做初始 value。 | ||
// 2 如果没改动,那么这些 value 直接 flush 到 stateTree 中。 | ||
instance.stateTreeBuffer = []; | ||
instance.stateProxy = {}; | ||
for (var _len2 = arguments.length, arg = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
arg[_key2 - 1] = arguments[_key2]; | ||
} | ||
function setStateValue(statePath, value) { | ||
for (var _len2 = arguments.length, arg = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { | ||
arg[_key2 - 2] = arguments[_key2]; | ||
} | ||
if (/^value/.test(statePath)) return (_instance$stateTree = instance.stateTree).get.apply(_instance$stateTree, [removePathHeader(statePath, 'value')].concat(arg)); | ||
if (/^config/.test(statePath)) return _exist2.default.get.apply(_exist2.default, [instance.config, removePathHeader(statePath, 'config')].concat(arg)); | ||
throw new Error('get unknown state name: ' + statePath); | ||
}, | ||
set: function set(statePath, value) { | ||
for (var _len3 = arguments.length, arg = Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) { | ||
arg[_key3 - 2] = arguments[_key3]; | ||
if (/^value/.test(statePath)) { | ||
var path = removePathHeader(statePath, 'value'); | ||
if (path) { | ||
instance.stateTreeBuffer.push([path, value].concat(arg)); | ||
} else { | ||
(0, _util.each)(value, function (v, k) { | ||
instance.stateTreeBuffer.push([k, v].concat(arg)); | ||
}); | ||
} | ||
return; | ||
} | ||
// 注意,config 没有 merge,只是创建不存在的路径 | ||
if (/^config/.test(statePath)) { | ||
// 对于外界的 set,记录一下,之后要用。 | ||
instance.isConfigChanged = true; | ||
var _path = removePathHeader(statePath, 'config'); | ||
if (!_path) { | ||
instance.config = value; | ||
} else { | ||
_exist2.default.set(instance.config, removePathHeader(statePath, 'config'), value, true); | ||
} | ||
return; | ||
} | ||
throw new Error('set unknown state name: ' + statePath); | ||
} | ||
if (/^value/.test(statePath)) { | ||
var path = removePathHeader(statePath, 'value'); | ||
if (path) { | ||
var _instance$stateTree2; | ||
function getStateValue(statePath) { | ||
var _instance$stateTree; | ||
(_instance$stateTree2 = instance.stateTree).set.apply(_instance$stateTree2, [removePathHeader(statePath, 'value'), value].concat(arg)); | ||
} else { | ||
(0, _util.each)(value, function (v, k) { | ||
var _instance$stateTree3; | ||
for (var _len3 = arguments.length, arg = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { | ||
arg[_key3 - 1] = arguments[_key3]; | ||
} | ||
(_instance$stateTree3 = instance.stateTree).set.apply(_instance$stateTree3, [k, v].concat(arg)); | ||
}); | ||
} | ||
return; | ||
if (/^value/.test(statePath)) return (_instance$stateTree = instance.stateTree).get.apply(_instance$stateTree, [removePathHeader(statePath, 'value')].concat(arg)); | ||
if (/^config/.test(statePath)) return _exist2.default.get.apply(_exist2.default, [instance.config, removePathHeader(statePath, 'config')].concat(arg)); | ||
if (statePath === '') { | ||
return { | ||
config: instance.config, | ||
value: instance.stateTree.get('') | ||
}; | ||
} | ||
throw new Error('get unknown state name: ' + statePath); | ||
} | ||
Object.defineProperty(instance.stateProxy, 'get', { | ||
value: getStateValue | ||
}); | ||
Object.defineProperty(instance.stateProxy, 'set', { | ||
value: function set(statePath, value) { | ||
for (var _len4 = arguments.length, arg = Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) { | ||
arg[_key4 - 2] = arguments[_key4]; | ||
} | ||
// 注意,config 没有 merge,只是创建不存在的路径 | ||
if (/^config/.test(statePath)) { | ||
// 对于外界的 set,记录一下,之后要用。 | ||
instance.isConfigChanged = true; | ||
var _path = removePathHeader(statePath, 'config'); | ||
if (!_path) { | ||
instance.config = value; | ||
} else { | ||
_exist2.default.set(instance.config, removePathHeader(statePath, 'config'), value, true); | ||
} | ||
return; | ||
// 这里跟真正的 stateTree 不一样,这里能接受 statePath 为 '' 的形式。 | ||
if (statePath === '') { | ||
(0, _util.each)(value, function (stateValue, stateName) { | ||
setStateValue.apply(undefined, [stateName, stateValue].concat(arg)); | ||
}); | ||
} else { | ||
setStateValue.apply(undefined, [statePath, value].concat(arg)); | ||
} | ||
throw new Error('set unknown state name: ' + statePath); | ||
} | ||
}; | ||
}); | ||
return instance.stateProxy; | ||
}; | ||
@@ -149,4 +214,4 @@ | ||
// 当只有 stateTree 变化时,使用内部的机制更新。 | ||
var shouldComponentUpdate = function shouldComponentUpdate(_ref3) { | ||
var instance = _ref3.instance; | ||
var shouldComponentUpdate = function shouldComponentUpdate(_ref7) { | ||
var instance = _ref7.instance; | ||
@@ -156,4 +221,4 @@ return instance.isConfigChanged; | ||
var componentWillReceiveState = function componentWillReceiveState(_ref4) { | ||
var instance = _ref4.instance; | ||
var componentWillReceiveState = function componentWillReceiveState(_ref8) { | ||
var instance = _ref8.instance; | ||
@@ -173,3 +238,10 @@ // 不管是外部的 set,还是内部的变化,都会走到这里来。 | ||
// 无论哪种情况,这个时候都应该准确地通知到界面更新了,因此不需要再做其他操作。 | ||
consumeStateTreeBuffer(instance.stateTree, instance.stateTreeBuffer); | ||
instance.stateTreeBuffer = []; | ||
instance.stateTree.flush(); | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('DynamicRender stateTree flush'); | ||
/* eslint-enable no-console */ | ||
} | ||
}; | ||
@@ -184,5 +256,5 @@ | ||
defaultListeners: defaultListeners, | ||
render: function render(_ref5) { | ||
var instance = _ref5.instance, | ||
listeners = _ref5.listeners; | ||
render: function render(_ref9) { | ||
var instance = _ref9.instance, | ||
listeners = _ref9.listeners; | ||
@@ -192,9 +264,16 @@ var config = instance.config; | ||
if (instance.isConfigChanged) { | ||
// 如果是二次渲染,stateTree 要重做,否则的 getProxy 的时候已经创建了,不用管 | ||
instance.isConfigChanged = false; | ||
instance.stateTree = createStateTree((0, _cloneDeep2.default)(instance.stateTree.get(''))); | ||
} | ||
// 不管是第一次还是第二次渲染,只要动态组件重新渲染,那么就要重建 stateTree。 | ||
instance.stateTree = createStateTree(constructInitialStateTree(instance.stateTreeBuffer)); | ||
instance.stateTreeBuffer = []; | ||
instance.appearance = createAppearance(); | ||
instance.background = createBackground(backgroundDef, instance.stateTree, instance.appearance); | ||
if ((0, _common.isDebug)()) { | ||
/* eslint-disable no-console */ | ||
console.info('DynamicRender render'); | ||
/* eslint-enable no-console */ | ||
} | ||
return _react2.default.createElement(_Render2.default, { | ||
@@ -201,0 +280,0 @@ config: config, |
@@ -39,3 +39,2 @@ 'use strict'; | ||
/* eslint-disable no-use-before-define */ | ||
/** | ||
@@ -53,2 +52,15 @@ * stateTree 是配合 Render 一起使用的数据结构,它的核心功能有两个: | ||
*/ | ||
function complete(stateTree, statePath, inputState) { | ||
// CAUTION,这里 exist.set 第四个参数 createMissing 非常重要 | ||
if (statePath !== '' && _exist2.default.detect(stateTree, statePath) !== true) { | ||
return _exist2.default.set(stateTree, statePath, inputState, true); | ||
} | ||
if ((typeof inputState === 'undefined' ? 'undefined' : (0, _typeof3.default)(inputState)) === 'object') { | ||
(0, _util.each)(inputState, function (inputSubState, key) { | ||
return complete(stateTree, (0, _common.joinPath)([statePath, key]), inputSubState); | ||
}); | ||
} | ||
} | ||
/* eslint-disable no-use-before-define */ | ||
function createStateTree(initialStateTree) { | ||
@@ -70,3 +82,3 @@ var stateTree = (0, _cloneDeep2.default)(initialStateTree) || {}; | ||
if (_constant.isDebug && _exist2.default.detect(stateTree, statePath) !== true) { | ||
if (_constant.isDebug && statePath !== '' && _exist2.default.detect(stateTree, statePath) !== true) { | ||
var detectArr = _exist2.default.detect(stateTree, statePath); | ||
@@ -102,14 +114,2 @@ // 只检测跳级搜索, 如搜a.b.c,但a.b不存在 | ||
function complete(statePath, inputState) { | ||
// CAUTION,这里 exist.set 第四个参数 createMissing 非常重要 | ||
if (_exist2.default.detect(stateTree, statePath) !== true) { | ||
return _exist2.default.set(stateTree, statePath, inputState, true); | ||
} | ||
if ((typeof inputState === 'undefined' ? 'undefined' : (0, _typeof3.default)(inputState)) === 'object') { | ||
(0, _util.each)(inputState, function (inputSubState, key) { | ||
return complete((0, _common.joinPath)([statePath, key]), inputSubState); | ||
}); | ||
} | ||
} | ||
function defineState(state, id) { | ||
@@ -139,7 +139,10 @@ var pathGetters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
function registerToStateTree(statePath, getInitialState, pathGetters, getStateProxy) { | ||
var initialState = getInitialState(); | ||
if (getStateProxy !== undefined) { | ||
// 有 stateProxy 的情况下,就用这个 Proxy | ||
_exist2.default.set(stateTree, statePath, getStateProxy(_exist2.default.get(stateTree, statePath))); | ||
var proxyInitialState = _exist2.default.get(stateTree, statePath, {}); | ||
complete(proxyInitialState, '', initialState); | ||
_exist2.default.set(stateTree, statePath, getStateProxy(proxyInitialState)); | ||
} else { | ||
complete(statePath, getInitialState()); | ||
complete(stateTree, statePath, initialState); | ||
} | ||
@@ -146,0 +149,0 @@ |
{ | ||
"name": "@cicada/render", | ||
"version": "1.1.22-alpha1", | ||
"version": "1.1.22-alpha10", | ||
"main": "./lib/index.js", | ||
@@ -5,0 +5,0 @@ "scripts": { |
413409
9998