From fa4e8edc4a0161edab10445a6c507412a0a1aa94 Mon Sep 17 00:00:00 2001 From: CJY <375564567@qq.com> Date: Tue, 26 May 2020 20:39:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=BC=E5=AE=B9=20react-router=205.2.0?= =?UTF-8?q?=EF=BC=9BcacheKey=20=E5=85=81=E8=AE=B8=E4=BC=A0=E9=80=92=20Func?= =?UTF-8?q?tion=20=E7=B1=BB=E5=9E=8B=E4=BB=A5=E6=8F=90=E5=8D=87=E5=8F=AF?= =?UTF-8?q?=E7=94=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .npmignore | 1 + README.md | 2 +- README_CN.md | 2 +- dist/cacheRoute.js | 1084 --------------------------------- dist/cacheRoute.min.js | 1 - index.d.ts | 2 +- index.js | 4 +- package.json | 2 +- rollup.config.js | 7 +- src/components/CacheRoute.js | 19 +- src/components/CacheSwitch.js | 24 +- src/core/CacheComponent.js | 28 +- 13 files changed, 46 insertions(+), 1131 deletions(-) delete mode 100644 dist/cacheRoute.js delete mode 100644 dist/cacheRoute.min.js diff --git a/.gitignore b/.gitignore index 3c3629e..3063f07 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +lib node_modules diff --git a/.npmignore b/.npmignore index e352baa..89017a6 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ +docs node_modules src .babelrc diff --git a/README.md b/README.md index 3ec0787..3a49430 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ export default App | when | `String` / `Function` | `"forward"` | Decide when to cache | | className | `String` | - | `className` prop for the wrapper component | | behavior | `Function` | `cached => cached ? { style: { display: "none" }} : undefined` | Return `props` effective on the wrapper component to control rendering behavior | -| cacheKey | `String` | - | For imperative control caching | +| cacheKey | `String` / `Function` | - | For imperative control caching | | multiple (React v16.2+) | `Boolean` / `Number` | `false` | Allows different caches to be distinguished by dynamic routing parameters. When the value is a number, it indicates the maximum number of caches. When the maximum value is exceeded, the oldest updated cache will be cleared. | | unmount (UNSTABLE) | `Boolean` | `false` | Whether to unmount the real dom node after cached, to save performance (Will cause losing the scroll position after recovered, fixed with `saveScrollPosition` props) | | saveScrollPosition (UNSTABLE) | `Boolean` | `false` | Save scroll position | diff --git a/README_CN.md b/README_CN.md index eb6a2d9..c4a8dd8 100644 --- a/README_CN.md +++ b/README_CN.md @@ -85,7 +85,7 @@ export default App | when | `String` / `Function` | `"forward"` | 用以决定何时使用缓存功能 | | className | `String` | - | 作用于包裹容器上的样式类名 | | behavior | `Function` | `cached => cached ? { style: { display: "none" }} : undefined` | 返回一个作用于包裹容器的 `props`,控制包裹容器的渲染方式 | -| cacheKey | `String` | - | 增加此属性用于命令式控制缓存 | +| cacheKey | `String` / `Function` | - | 增加此属性用于命令式控制缓存 | | multiple (React v16.2+) | `Boolean` / `Number` | `false` | 允许按动态路由参数区分不同缓存,值为数字时表示最大缓存份数,超出最大值时将清除最早更新的缓存 | | unmount (实验性) | `Boolean` | `false` | 缓存时是否卸载 dom 节点,用于节约性能(单独使用将导致恢复时滚动位置丢失,可配合 saveScrollPosition 修复) | | saveScrollPosition (实验性) | `Boolean` | `false` | 用以保存滚动位置 | diff --git a/dist/cacheRoute.js b/dist/cacheRoute.js deleted file mode 100644 index cf35a75..0000000 --- a/dist/cacheRoute.js +++ /dev/null @@ -1,1084 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types'), require('react-router-dom')) : - typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types', 'react-router-dom'], factory) : - (factory((global.CacheRoute = {}),global.React,global.PropTypes,global.reactRouterDom)); -}(this, (function (exports,React,PropTypes,reactRouterDom) { 'use strict'; - - var React__default = 'default' in React ? React['default'] : React; - PropTypes = PropTypes && PropTypes.hasOwnProperty('default') ? PropTypes['default'] : PropTypes; - - // 值类型判断 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - var isUndefined = function isUndefined(val) { - return typeof val === 'undefined'; - }; - - var isNull = function isNull(val) { - return val === null; - }; - - var isFunction = function isFunction(val) { - return typeof val === 'function'; - }; - - var isString = function isString(val) { - return typeof val === 'string'; - }; - - var isExist = function isExist(val) { - return !(isUndefined(val) || isNull(val)); - }; - - var isArray = function isArray(val) { - return val instanceof Array; - }; - - var isNaN = function isNaN(val) { - return val !== val; - }; - - var isNumber = function isNumber(val) { - return typeof val === 'number' && !isNaN(val); - }; - // 值类型判断 ------------------------------------------------------------- - - var get = function get(obj) { - var keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var defaultValue = arguments[2]; - - try { - if (isNumber(keys)) { - keys = String(keys); - } - var result = (isString(keys) ? keys.split('.') : keys).reduce(function (res, key) { - return res[key]; - }, obj); - return isUndefined(result) ? defaultValue : result; - } catch (e) { - return defaultValue; - } - }; - - var run = function run(obj) { - for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - args[_key - 2] = arguments[_key]; - } - - var keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - - keys = isString(keys) ? keys.split('.') : keys; - - var func = get(obj, keys); - var context = get(obj, keys.slice(0, -1)); - - return isFunction(func) ? func.call.apply(func, [context].concat(args)) : func; - }; - - var value = function value() { - for (var _len2 = arguments.length, values = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - values[_key2] = arguments[_key2]; - } - - return values.reduce(function (value, nextValue) { - return isUndefined(value) ? run(nextValue) : run(value); - }, undefined); - }; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; - - var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - }; - - var createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - var defineProperty = function (obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; - }; - - var _extends = Object.assign || function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target; - }; - - var inherits = function (subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); - } - - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true - } - }); - if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; - }; - - var objectWithoutProperties = function (obj, keys) { - var target = {}; - - for (var i in obj) { - if (keys.indexOf(i) >= 0) continue; - if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; - target[i] = obj[i]; - } - - return target; - }; - - var possibleConstructorReturn = function (self, call) { - if (!self) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - - return call && (typeof call === "object" || typeof call === "function") ? call : self; - }; - - var slicedToArray = function () { - function sliceIterator(arr, i) { - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"]) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; - } - - return function (arr, i) { - if (Array.isArray(arr)) { - return arr; - } else if (Symbol.iterator in Object(arr)) { - return sliceIterator(arr, i); - } else { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - }; - }(); - - var toConsumableArray = function (arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - - return arr2; - } else { - return Array.from(arr); - } - }; - - var getImplementation = function getImplementation() { - if (typeof self !== 'undefined') { - return self; - } - if (typeof window !== 'undefined') { - return window; - } - if (typeof global !== 'undefined') { - return global; - } - - return Function('return this')(); - }; - - var implementation = getImplementation(); - - var getGlobal = function getGlobal() { - if ((typeof global === 'undefined' ? 'undefined' : _typeof(global)) !== 'object' || !global || global.Math !== Math || global.Array !== Array) { - return implementation; - } - return global; - }; - - var globalThis = getGlobal(); - - var flatten = function flatten(array) { - return array.reduce(function (res, item) { - return [].concat(toConsumableArray(res), toConsumableArray(isArray(item) ? flatten(item) : [item])); - }, []); - }; - - /** - * [钳子] 用来将数字限制在给定范围内 - * @param {Number} value 被限制值 - * @param {Number} min 最小值 - * @param {Number} max 最大值 - */ - var clamp = function clamp(value, min) { - var max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Number.MAX_VALUE; - - if (value < min) { - return min; - } - - if (value > max) { - return max; - } - - return value; - }; - - var body = get(globalThis, 'document.body'); - var screenScrollingElement = get(globalThis, 'document.scrollingElement', get(globalThis, 'document.documentElement', {})); - - function isScrollableNode() { - var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - if (!isExist(node)) { - return false; - } - - return node.scrollWidth > node.clientWidth || node.scrollHeight > node.clientHeight; - } - - function getScrollableNodes(from) { - if (!isFunction(get(globalThis, 'document.getElementById'))) { - return []; - } - - return [].concat(toConsumableArray(value(run(from, 'querySelectorAll', '*'), [])), [from]).filter(isScrollableNode); - } - - function saveScrollPosition(from) { - var nodes = [].concat(toConsumableArray(new Set([].concat(toConsumableArray(flatten((!isArray(from) ? [from] : from).map(getScrollableNodes))), toConsumableArray([screenScrollingElement, body].filter(isScrollableNode)))))); - - var saver = nodes.map(function (node) { - return [node, { - x: node.scrollLeft, - y: node.scrollTop - }]; - }); - - return function revert() { - saver.forEach(function (_ref) { - var _ref2 = slicedToArray(_ref, 2), - node = _ref2[0], - _ref2$ = _ref2[1], - x = _ref2$.x, - y = _ref2$.y; - - node.scrollLeft = x; - node.scrollTop = y; - }); - }; - } - - var __components = {}; - - var getCachedComponentEntries = function getCachedComponentEntries() { - return Object.entries(__components).filter(function (_ref) { - var _ref2 = slicedToArray(_ref, 2), - cache = _ref2[1]; - - return cache instanceof CacheComponent ? cache.state.cached : Object.values(cache).some(function (cache) { - return cache.state.cached; - }); - }); - }; - - var getCache = function getCache() { - return _extends({}, __components); - }; - - var register = function register(key, component) { - __components[key] = component; - }; - - var remove = function remove(key) { - delete __components[key]; - }; - - var dropComponent = function dropComponent(component) { - return run(component, 'reset'); - }; - - var dropByCacheKey = function dropByCacheKey(key) { - var cache = get(__components, [key]); - - if (!cache) { - return; - } - - if (cache instanceof CacheComponent) { - dropComponent(cache); - } else { - Object.values(cache).forEach(dropComponent); - } - }; - - var clearCache = function clearCache() { - getCachedComponentEntries().forEach(function (_ref3) { - var _ref4 = slicedToArray(_ref3, 1), - key = _ref4[0]; - - return dropByCacheKey(key); - }); - }; - - var getCachingKeys = function getCachingKeys() { - return getCachedComponentEntries().map(function (_ref5) { - var _ref6 = slicedToArray(_ref5, 1), - key = _ref6[0]; - - return key; - }); - }; - - var getCachingComponents = function getCachingComponents() { - return getCachedComponentEntries().reduce(function (res, _ref7) { - var _ref8 = slicedToArray(_ref7, 2), - key = _ref8[0], - cache = _ref8[1]; - - return _extends({}, res, cache instanceof CacheComponent ? defineProperty({}, key, cache) : Object.entries(cache).reduce(function (res, _ref10) { - var _ref11 = slicedToArray(_ref10, 2), - pathname = _ref11[0], - cache = _ref11[1]; - - return _extends({}, res, defineProperty({}, key + '.' + pathname, cache)); - }, {})); - }, {}); - }; - - var isUsingNewLifecycle = isExist(React__default.forwardRef); - - var COMPUTED_UNMATCH_KEY = '__isComputedUnmatch'; - var isMatch = function isMatch(match) { - return isExist(match) && get(match, COMPUTED_UNMATCH_KEY) !== true; - }; - - var getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { - var nextPropsMatch = nextProps.match, - _nextProps$when = nextProps.when, - when = _nextProps$when === undefined ? 'forward' : _nextProps$when; - - /** - * Note: - * Turn computedMatch from CacheSwitch to a real null value - * - * 将 CacheSwitch 计算得到的 computedMatch 值转换为真正的 null - */ - - if (!isMatch(nextPropsMatch)) { - nextPropsMatch = null; - } - - if (!prevState.cached && nextPropsMatch) { - return { - cached: true, - matched: true - }; - } - - /** - * Determines whether it needs to cancel the cache based on the next unmatched props action - * - * 根据下个未匹配状态动作决定是否需要取消缓存 - */ - if (prevState.matched && !nextPropsMatch) { - var nextAction = get(nextProps, 'history.action'); - - var __cancel__cache = false; - - if (isFunction(when)) { - __cancel__cache = !when(nextProps); - } else { - switch (when) { - case 'always': - break; - case 'back': - if (['PUSH', 'REPLACE'].includes(nextAction)) { - __cancel__cache = true; - } - - break; - case 'forward': - default: - if (nextAction === 'POP') { - __cancel__cache = true; - } - } - } - - if (__cancel__cache) { - return { - cached: false, - matched: false - }; - } - } - - return { - matched: !!nextPropsMatch - }; - }; - - var CacheComponent = function (_Component) { - inherits(CacheComponent, _Component); - - function CacheComponent(props) { - var _ref; - - classCallCheck(this, CacheComponent); - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var _this = possibleConstructorReturn(this, (_ref = CacheComponent.__proto__ || Object.getPrototypeOf(CacheComponent)).call.apply(_ref, [this, props].concat(args))); - - _this.cacheLifecycles = { - __listener: {}, - didCache: function didCache(listener) { - _this.cacheLifecycles.__listener['didCache'] = listener; - }, - didRecover: function didRecover(listener) { - _this.cacheLifecycles.__listener['didRecover'] = listener; - } - - /** - * New lifecycle for replacing the `componentWillReceiveProps` in React 16.3 + - * React 16.3 + 版本中替代 componentWillReceiveProps 的新生命周期 - */ - }; - _this.componentWillReceiveProps = !isUsingNewLifecycle ? function (nextProps) { - var nextState = getDerivedStateFromProps(nextProps, _this.state); - - _this.setState(nextState); - } : undefined; - - _this.injectDOM = function () { - try { - run(_this.__parentNode, 'insertBefore', _this.wrapper, _this.__placeholderNode); - run(_this.__parentNode, 'removeChild', _this.__placeholderNode); - } catch (err) { - // nothing - } - }; - - _this.ejectDOM = function () { - try { - var parentNode = get(_this.wrapper, 'parentNode'); - _this.__parentNode = parentNode; - - run(_this.__parentNode, 'insertBefore', _this.__placeholderNode, _this.wrapper); - run(_this.__parentNode, 'removeChild', _this.wrapper); - } catch (err) { - // nothing - } - }; - - _this.reset = function () { - delete _this.__revertScrollPos; - - _this.setState({ - cached: false - }); - }; - - _this.__cacheCreateTime = Date.now(); - _this.__cacheUpdateTime = _this.__cacheCreateTime; - if (props.cacheKey) { - if (get(props.cacheKey, 'multiple')) { - var _props$cacheKey = props.cacheKey, - cacheKey = _props$cacheKey.cacheKey, - pathname = _props$cacheKey.pathname; - - register(cacheKey, _extends({}, getCache()[cacheKey], defineProperty({}, pathname, _this))); - } else { - register(props.cacheKey, _this); - } - } - - if (typeof document !== 'undefined') { - _this.__placeholderNode = document.createComment(' Route cached ' + (props.cacheKey ? 'with cacheKey: "' + get(props.cacheKey, 'cacheKey', props.cacheKey) + '" ' : '')); - } - - _this.state = getDerivedStateFromProps(props, { - cached: false, - matched: false - }); - return _this; - } - - /** - * Compatible React 16.3 - - * 兼容 React 16.3 - 版本 - */ - - - createClass(CacheComponent, [{ - key: 'componentDidUpdate', - value: function componentDidUpdate(prevProps, prevState) { - if (!prevState.cached || !this.state.cached) { - return; - } - - if (prevState.matched === true && this.state.matched === false) { - if (this.props.unmount) { - this.ejectDOM(); - } - this.__cacheUpdateTime = Date.now(); - return run(this, 'cacheLifecycles.__listener.didCache'); - } - - if (prevState.matched === false && this.state.matched === true) { - if (this.props.saveScrollPosition) { - run(this.__revertScrollPos); - } - this.__cacheUpdateTime = Date.now(); - return run(this, 'cacheLifecycles.__listener.didRecover'); - } - } - }, { - key: 'shouldComponentUpdate', - value: function shouldComponentUpdate(nextProps, nextState) { - var willRecover = this.state.matched === false && nextState.matched === true; - var willDrop = this.state.cached === true && nextState.cached === false; - var shouldUpdate = this.state.matched || nextState.matched || this.state.cached !== nextState.cached; - - if (shouldUpdate) { - if (this.props.unmount && willDrop || willRecover) { - this.injectDOM(); - } - - if (!(willDrop || willRecover) && this.props.saveScrollPosition) { - this.__revertScrollPos = saveScrollPosition(this.props.unmount ? this.wrapper : undefined); - } - } - - return shouldUpdate; - } - }, { - key: 'componentWillUnmount', - value: function componentWillUnmount() { - var _props = this.props, - cacheKeyConfig = _props.cacheKey, - unmount = _props.unmount; - - - if (get(cacheKeyConfig, 'multiple')) { - var cacheKey = cacheKeyConfig.cacheKey, - pathname = cacheKeyConfig.pathname; - - var cache = _extends({}, getCache()[cacheKey]); - - delete cache[pathname]; - - if (Object.keys(cache).length === 0) { - remove(cacheKey); - } else { - register(cacheKey, cache); - } - } else { - remove(cacheKeyConfig); - } - - if (unmount) { - this.injectDOM(); - } - } - }, { - key: 'render', - value: function render() { - var _this2 = this; - - var _state = this.state, - matched = _state.matched, - cached = _state.cached; - var _props2 = this.props, - _props2$className = _props2.className, - propsClassName = _props2$className === undefined ? '' : _props2$className, - behavior = _props2.behavior, - children = _props2.children; - - var _value = value(run(behavior, undefined, !matched), {}), - _value$className = _value.className, - behaviorClassName = _value$className === undefined ? '' : _value$className, - behaviorProps = objectWithoutProperties(_value, ['className']); - - var className = run(propsClassName + ' ' + behaviorClassName, 'trim'); - var hasClassName = className !== ''; - - return cached ? React__default.createElement( - 'div', - _extends({ - className: hasClassName ? className : undefined - }, behaviorProps, { - ref: function ref(wrapper) { - _this2.wrapper = wrapper; - } - }), - run(children, undefined, this.cacheLifecycles) - ) : null; - } - }]); - return CacheComponent; - }(React.Component); - - CacheComponent.propsTypes = { - history: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, - children: PropTypes.func.isRequired, - className: PropTypes.string, - when: PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf(['forward', 'back', 'always'])]), - behavior: PropTypes.func, - unmount: PropTypes.bool, - saveScrollPosition: PropTypes.bool - }; - CacheComponent.defaultProps = { - when: 'forward', - unmount: false, - saveScrollPosition: false, - behavior: function behavior(cached) { - return cached ? { - style: { - display: 'none' - } - } : undefined; - } - }; - CacheComponent.getDerivedStateFromProps = isUsingNewLifecycle ? getDerivedStateFromProps : undefined; - - var Updatable = function (_Component) { - inherits(Updatable, _Component); - - function Updatable() { - var _ref; - - var _temp, _this, _ret; - - classCallCheck(this, Updatable); - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Updatable.__proto__ || Object.getPrototypeOf(Updatable)).call.apply(_ref, [this].concat(args))), _this), _this.render = function () { - return run(_this.props, 'children'); - }, _this.shouldComponentUpdate = function (_ref2) { - var when = _ref2.when; - return when; - }, _temp), possibleConstructorReturn(_this, _ret); - } - - return Updatable; - }(React.Component); - - Updatable.propsTypes = { - when: PropTypes.bool.isRequired - }; - - var isEmptyChildren = function isEmptyChildren(children) { - return React__default.Children.count(children) === 0; - }; - var isFragmentable = isExist(React.Fragment); - - var CacheRoute = function (_Component) { - inherits(CacheRoute, _Component); - - function CacheRoute() { - var _ref; - - var _temp, _this, _ret; - - classCallCheck(this, CacheRoute); - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = CacheRoute.__proto__ || Object.getPrototypeOf(CacheRoute)).call.apply(_ref, [this].concat(args))), _this), _this.cache = {}, _temp), possibleConstructorReturn(_this, _ret); - } - - createClass(CacheRoute, [{ - key: 'render', - value: function render() { - var _this2 = this; - - var _props = this.props, - children = _props.children, - render = _props.render, - component = _props.component, - className = _props.className, - when = _props.when, - behavior = _props.behavior, - cacheKey = _props.cacheKey, - unmount = _props.unmount, - saveScrollPosition$$1 = _props.saveScrollPosition, - computedMatchForCacheRoute = _props.computedMatchForCacheRoute, - multiple = _props.multiple, - restProps = objectWithoutProperties(_props, ['children', 'render', 'component', 'className', 'when', 'behavior', 'cacheKey', 'unmount', 'saveScrollPosition', 'computedMatchForCacheRoute', 'multiple']); - - /** - * Note: - * If children prop is a React Element, define the corresponding wrapper component for supporting multiple children - * - * 说明:如果 children 属性是 React Element 则定义对应的包裹组件以支持多个子组件 - */ - - if (React__default.isValidElement(children) || !isEmptyChildren(children)) { - render = function render() { - return children; - }; - } - - if (computedMatchForCacheRoute) { - restProps.computedMatch = computedMatchForCacheRoute; - } - - if (multiple && !isFragmentable) { - multiple = false; - } - - if (isNumber(multiple)) { - multiple = clamp(multiple, 1); - } - - return ( - /** - * Only children prop of Route can help to control rendering behavior - * 只有 Router 的 children 属性有助于主动控制渲染行为 - */ - React__default.createElement( - reactRouterDom.Route, - restProps, - function (props) { - var match = props.match, - computedMatch = props.computedMatch, - location = props.location; - - var isMatchCurrentRoute = isMatch(props.match); - var currentPathname = location.pathname; - - var maxMultipleCount = isNumber(multiple) ? multiple : Infinity; - var configProps = { - when: when, - className: className, - behavior: behavior, - cacheKey: cacheKey, - unmount: unmount, - saveScrollPosition: saveScrollPosition$$1 - }; - - var renderSingle = function renderSingle(props) { - return React__default.createElement( - CacheComponent, - props, - function (cacheLifecycles) { - return React__default.createElement( - Updatable, - { when: isMatch(props.match) }, - function () { - Object.assign(props, { cacheLifecycles: cacheLifecycles }); - - if (component) { - return React__default.createElement(component, props); - } - - return run(render || children, undefined, props); - } - ); - } - ); - }; - - if (multiple && isMatchCurrentRoute) { - _this2.cache[currentPathname] = { - updateTime: Date.now(), - render: renderSingle - }; - - Object.entries(_this2.cache).sort(function (_ref2, _ref3) { - var _ref5 = slicedToArray(_ref2, 2), - prev = _ref5[1]; - - var _ref4 = slicedToArray(_ref3, 2), - next = _ref4[1]; - - return next.updateTime - prev.updateTime; - }).forEach(function (_ref6, idx) { - var _ref7 = slicedToArray(_ref6, 1), - pathname = _ref7[0]; - - if (idx >= maxMultipleCount) { - delete _this2.cache[pathname]; - } - }); - } - - return multiple ? React__default.createElement( - React.Fragment, - null, - Object.entries(_this2.cache).map(function (_ref8) { - var _ref9 = slicedToArray(_ref8, 2), - pathname = _ref9[0], - render = _ref9[1].render; - - var recomputedMatch = pathname === currentPathname ? match || computedMatch : null; - - return React__default.createElement( - React.Fragment, - { key: pathname }, - render(_extends({}, props, configProps, { - cacheKey: cacheKey ? { - cacheKey: cacheKey, - pathname: pathname, - multiple: true - } : undefined, - key: pathname, - match: recomputedMatch - })) - ); - }) - ) : renderSingle(_extends({}, props, configProps)); - } - ) - ); - } - }]); - return CacheRoute; - }(React.Component); - - CacheRoute.componentName = 'CacheRoute'; - CacheRoute.propTypes = { - component: PropTypes.elementType || PropTypes.any, - render: PropTypes.func, - children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - computedMatchForCacheRoute: PropTypes.object, - multiple: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]) - }; - CacheRoute.defaultProps = { - multiple: false - }; - - function getFragment() { - if (isExist(React.Fragment)) { - return function (_ref) { - var children = _ref.children; - return React__default.createElement( - React.Fragment, - null, - children - ); - }; - } - - if (isExist(React.PropTypes)) { - return function (_ref2) { - var children = _ref2.children; - return React__default.createElement( - 'div', - null, - children - ); - }; - } - - return function (_ref3) { - var children = _ref3.children; - return children; - }; - } - - var SwitchFragment = getFragment(); - SwitchFragment.displayName = 'SwitchFragment'; - - var isUsingNewContext = isExist(reactRouterDom.__RouterContext); - - var CacheSwitch = function (_Switch) { - inherits(CacheSwitch, _Switch); - - function CacheSwitch() { - var _ref; - - var _temp, _this, _ret; - - classCallCheck(this, CacheSwitch); - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = CacheSwitch.__proto__ || Object.getPrototypeOf(CacheSwitch)).call.apply(_ref, [this].concat(args))), _this), _this.getContext = function () { - if (isUsingNewContext) { - var _this$props = _this.props, - location = _this$props.location, - match = _this$props.match; - - - return { location: location, match: match }; - } else { - var route = _this.context.router.route; - - var _location = _this.props.location || route.location; - - return { - location: _location, - match: route.match - }; - } - }, _temp), possibleConstructorReturn(_this, _ret); - } - - createClass(CacheSwitch, [{ - key: 'render', - value: function render() { - var _props = this.props, - children = _props.children, - which = _props.which; - - var _getContext = this.getContext(), - location = _getContext.location, - contextMatch = _getContext.match; - - var __matchedAlready = false; - - return React__default.createElement( - Updatable, - { when: isMatch(contextMatch) }, - function () { - return React__default.createElement( - SwitchFragment, - null, - React__default.Children.map(children, function (element) { - if (!React__default.isValidElement(element)) { - return null; - } - - var path = element.props.path || element.props.from; - var match = __matchedAlready ? null : path ? reactRouterDom.matchPath(location.pathname, _extends({}, element.props, { - path: path - }), contextMatch) : contextMatch; - - var child = void 0; - - if (which(element)) { - child = React__default.cloneElement(element, _extends({ - location: location, - computedMatch: match - }, isNull(match) ? { - computedMatchForCacheRoute: defineProperty({}, COMPUTED_UNMATCH_KEY, true) - } : null)); - } else { - child = match && !__matchedAlready ? React__default.cloneElement(element, { - location: location, - computedMatch: match - }) : null; - } - - if (!__matchedAlready) { - __matchedAlready = !!match; - } - - return child; - }) - ); - } - ); - } - }]); - return CacheSwitch; - }(reactRouterDom.Switch); - - if (isUsingNewContext) { - CacheSwitch.propTypes = { - children: PropTypes.node, - location: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, - which: PropTypes.func - }; - - CacheSwitch = reactRouterDom.withRouter(CacheSwitch); - } else { - CacheSwitch.contextTypes = { - router: PropTypes.shape({ - route: PropTypes.object.isRequired - }).isRequired - }; - - CacheSwitch.propTypes = { - children: PropTypes.node, - location: PropTypes.object, - which: PropTypes.func - }; - } - - CacheSwitch.defaultProps = { - which: function which(element) { - return get(element, 'type') === CacheRoute; - } - }; - - var CacheSwitch$1 = CacheSwitch; - - exports.default = CacheRoute; - exports.CacheRoute = CacheRoute; - exports.CacheSwitch = CacheSwitch$1; - exports.dropByCacheKey = dropByCacheKey; - exports.getCachingKeys = getCachingKeys; - exports.clearCache = clearCache; - exports.getCachingComponents = getCachingComponents; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); diff --git a/dist/cacheRoute.min.js b/dist/cacheRoute.min.js deleted file mode 100644 index 977fd76..0000000 --- a/dist/cacheRoute.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("prop-types"),require("react-router-dom")):"function"==typeof define&&define.amd?define(["exports","react","prop-types","react-router-dom"],t):t(e.CacheRoute={},e.React,e.PropTypes,e.reactRouterDom)}(this,function(e,w,t,u){"use strict";var g="default"in w?w.default:w;t=t&&t.hasOwnProperty("default")?t.default:t;var o=function(e){return void 0===e},l=function(e){return null===e},i=function(e){return"function"==typeof e},h=function(e){return"string"==typeof e},n=function(e){return!(o(e)||l(e))},r=function(e){return e instanceof Array},O=function(e){return"number"==typeof e&&!((t=e)!=t);var t},s=function(e){var t=1e.clientWidth||e.scrollHeight>e.clientHeight)}function N(e){return i(s(_,"document.getElementById"))?[].concat(a(m(j(e,"querySelectorAll","*"),[])),[e]).filter(T):[]}function A(e){var t=[].concat(a(new Set([].concat(a(function n(e){return e.reduce(function(e,t){return[].concat(a(e),a(r(t)?n(t):[t]))},[])}((r(e)?e:[e]).map(N))),a([S,R].filter(T)))))).map(function(e){return[e,{x:e.scrollLeft,y:e.scrollTop}]});return function(){t.forEach(function(e){var t=E(e,2),n=t[0],r=t[1],o=r.x,c=r.y;n.scrollLeft=o,n.scrollTop=c})}}var K={},M=function(){return Object.entries(K).filter(function(e){var t=E(e,2)[1];return t instanceof H?t.state.cached:Object.values(t).some(function(e){return e.state.cached})})},k=function(){return C({},K)},x=function(e,t){K[e]=t},q=function(e){delete K[e]},D=function(e){return j(e,"reset")},F=function(e){var t=s(K,[e]);t&&(t instanceof H?D(t):Object.values(t).forEach(D))},L=n(g.forwardRef),U="__isComputedUnmatch",B=function(e){return n(e)&&!0!==s(e,U)},W=function(e,t){var n=e.match,r=e.when,o=void 0===r?"forward":r;if(B(n)||(n=null),!t.cached&&n)return{cached:!0,matched:!0};if(t.matched&&!n){var c=s(e,"history.action"),a=!1;if(i(o))a=!o(e);else switch(o){case"always":break;case"back":["PUSH","REPLACE"].includes(c)&&(a=!0);break;case"forward":default:"POP"===c&&(a=!0)}if(a)return{cached:!1,matched:!1}}return{matched:!!n}},H=function(e){function l(e){var t;f(this,l);for(var n=arguments.length,r=Array(1 boolean) behavior?: (isCached: boolean) => object | void - cacheKey?: string + cacheKey?: string | ((props: CacheRouteProps) => string), unmount?: boolean saveScrollPosition?: boolean multiple?: boolean | number diff --git a/index.js b/index.js index af4e379..8b4c81d 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ 'use strict'; if (process.env.NODE_ENV === 'production') { - module.exports = require('./dist/cacheRoute.min.js'); + module.exports = require('./lib/index.min.js'); } else { - module.exports = require('./dist/cacheRoute.js'); + module.exports = require('./lib/index.js'); } \ No newline at end of file diff --git a/package.json b/package.json index 5457f38..e691500 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-router-cache-route", - "version": "1.8.4", + "version": "1.9.0", "description": "cache-route for react-router base on react v15+ and router v4+", "main": "index.js", "scripts": { diff --git a/rollup.config.js b/rollup.config.js index 0a10b81..6b31680 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,8 +7,9 @@ export default [ input: 'src/index.js', output: { name: 'CacheRoute', - file: 'dist/cacheRoute.js', - format: 'umd' + file: 'lib/index.js', + format: 'cjs', + sourcemap: true }, external: [ 'react', @@ -26,7 +27,7 @@ export default [ input: 'src/index.js', output: { name: 'CacheRoute', - file: 'dist/cacheRoute.min.js', + file: 'lib/index.min.js', format: 'umd' }, external: [ diff --git a/src/components/CacheRoute.js b/src/components/CacheRoute.js index 3474656..6b5dc01 100644 --- a/src/components/CacheRoute.js +++ b/src/components/CacheRoute.js @@ -10,7 +10,7 @@ const isEmptyChildren = children => React.Children.count(children) === 0 const isFragmentable = isExist(Fragment) export default class CacheRoute extends Component { - static componentName = 'CacheRoute' + static __name = 'CacheRoute' static propTypes = { component: PropTypes.elementType || PropTypes.any, @@ -128,13 +128,9 @@ export default class CacheRoute extends Component { {render({ ...props, ...configProps, - cacheKey: cacheKey - ? { - cacheKey, - pathname, - multiple: true - } - : undefined, + pathname, + cacheKey, + multiple: true, key: pathname, match: recomputedMatch })} @@ -143,7 +139,12 @@ export default class CacheRoute extends Component { })} ) : ( - renderSingle({ ...props, ...configProps }) + renderSingle({ + ...props, + ...configProps, + pathname: currentPathname, + multiple: false + }) ) }} diff --git a/src/components/CacheSwitch.js b/src/components/CacheSwitch.js index 953f1c3..d8e1f07 100644 --- a/src/components/CacheSwitch.js +++ b/src/components/CacheSwitch.js @@ -4,6 +4,7 @@ import { Switch, matchPath, withRouter, + useHistory, __RouterContext } from 'react-router-dom' @@ -11,9 +12,8 @@ import { COMPUTED_UNMATCH_KEY, isMatch } from '../core/CacheComponent' import Updatable from '../core/Updatable' import SwitchFragment from './SwitchFragment' import { get, isNull, isExist } from '../helpers' -import CacheRoute from './CacheRoute' -const isUsingNewContext = isExist(__RouterContext) +const isUsingNewContext = isExist(__RouterContext) || isExist(useHistory) class CacheSwitch extends Switch { getContext = () => { @@ -51,15 +51,15 @@ class CacheSwitch extends Switch { const match = __matchedAlready ? null : path - ? matchPath( - location.pathname, - { - ...element.props, - path - }, - contextMatch - ) - : contextMatch + ? matchPath( + location.pathname, + { + ...element.props, + path + }, + contextMatch + ) + : contextMatch let child @@ -132,7 +132,7 @@ if (isUsingNewContext) { } CacheSwitch.defaultProps = { - which: element => get(element, 'type') === CacheRoute + which: element => get(element, 'type.__name') === 'CacheRoute' } export default CacheSwitch diff --git a/src/core/CacheComponent.js b/src/core/CacheComponent.js index ae13c77..7f01533 100644 --- a/src/core/CacheComponent.js +++ b/src/core/CacheComponent.js @@ -81,6 +81,8 @@ const getDerivedStateFromProps = (nextProps, prevState) => { } export default class CacheComponent extends Component { + static __name = 'CacheComponent' + static propsTypes = { history: PropTypes.object.isRequired, match: PropTypes.object.isRequired, @@ -115,28 +117,22 @@ export default class CacheComponent extends Component { this.__cacheCreateTime = Date.now() this.__cacheUpdateTime = this.__cacheCreateTime if (props.cacheKey) { - if (get(props.cacheKey, 'multiple')) { - const { cacheKey, pathname } = props.cacheKey + const cacheKey = run(props.cacheKey, undefined, props) + if (props.multiple) { + const { pathname } = props manager.register(cacheKey, { ...manager.getCache()[cacheKey], [pathname]: this }) } else { - manager.register(props.cacheKey, this) + manager.register(cacheKey, this) } } if (typeof document !== 'undefined') { + const cacheKey = run(props.cacheKey, undefined, props) this.__placeholderNode = document.createComment( - ` Route cached ${ - props.cacheKey - ? `with cacheKey: "${get( - props.cacheKey, - 'cacheKey', - props.cacheKey - )}" ` - : '' - }` + ` Route cached ${cacheKey ? `with cacheKey: "${cacheKey}" ` : ''}` ) } @@ -256,10 +252,10 @@ export default class CacheComponent extends Component { } componentWillUnmount() { - const { cacheKey: cacheKeyConfig, unmount } = this.props + const { unmount, pathname, multiple } = this.props + const cacheKey = run(this.props, 'cacheKey', this.props) - if (get(cacheKeyConfig, 'multiple')) { - const { cacheKey, pathname } = cacheKeyConfig + if (multiple) { const cache = { ...manager.getCache()[cacheKey] } delete cache[pathname] @@ -270,7 +266,7 @@ export default class CacheComponent extends Component { manager.register(cacheKey, cache) } } else { - manager.remove(cacheKeyConfig) + manager.remove(cacheKey) } if (unmount) {