├── .gitignore ├── source-code.lodash ├── README.md └── src │ ├── isObjectLike.js │ ├── isObject.js │ ├── clamp.js │ ├── isFunction.js │ ├── create.js │ ├── isPlainObject.js │ ├── slice.js │ └── .internal │ └── stringToPath.js ├── source-code.lazyestload ├── README.md ├── lazyestload.png └── src │ ├── lazyload.js │ └── lazyestload.js ├── source-code.zepto └── README.md ├── source-code.stat ├── Stat.png ├── README.md └── src │ └── Stats.js ├── source-code.redux ├── Redux.png ├── README.md └── src │ ├── utils │ ├── warning.js │ ├── actionTypes.js │ └── isPlainObject.js │ ├── compose.js │ ├── index.js │ ├── bindActionCreators.js │ └── applyMiddleware.js ├── source-code.pubsub-js ├── PubSub.png └── README.md ├── source-code.reselect ├── reselect.png ├── README.md └── src │ └── index.js ├── source-code.unstated ├── unstated.png └── src │ └── unstated.js ├── source-code.hyperapp ├── hyperapp导图.jpg ├── hyperapp-patch导图.png └── README.md ├── source-code.fullpage └── README.md ├── source-code.jquery-mousewheel └── README.md ├── source-code.react-redux ├── React-Redux.png └── src │ ├── components │ ├── Context.js │ └── Provider.js │ ├── utils │ ├── wrapActionCreators.js │ ├── verifyPlainObject.js │ ├── isPlainObject.js │ ├── warning.js │ └── shallowEqual.js │ ├── index.js │ └── connect │ ├── mapStateToProps.js │ ├── verifySubselectors.js │ ├── mapDispatchToProps.js │ ├── mergeProps.js │ ├── wrapMapToProps.js │ ├── connect.js │ └── selectorFactory.js ├── source-code.reach-router ├── reachRouter.png ├── reachRouter-Focus.gif ├── src │ └── lib │ │ └── history.js └── README.md ├── source-code.react-motion ├── react-motion.png └── src │ ├── presets.js │ ├── spring.js │ ├── reorderKeys.js │ ├── mapToZero.js │ ├── react-motion.js │ ├── stripStyle.js │ ├── shouldStopAnimation.js │ ├── stepper.js │ ├── Types.js │ └── mergeDiff.js ├── source-code.signature_pad ├── signature_pad.png ├── src │ ├── point.ts │ ├── throttle.ts │ └── bezier.ts └── README.md ├── source-code.react-loadable ├── react-loadabel.png └── src │ ├── webpack.js │ └── babel.js ├── source-code.react-snapshot ├── react-snapshot.png ├── src │ ├── index.js │ ├── Writer.js │ ├── snapshot.js │ ├── Server.js │ ├── cli.js │ └── Crawler.js └── README.md ├── source-code.react-waypoint ├── react-waypoint.png ├── src │ ├── constants.js │ ├── debugLog.js │ ├── isDOMElement.js │ ├── resolveScrollableAncestorProp.js │ ├── ensureChildrenIsValid.js │ ├── computeOffsetPixels.js │ ├── ensureRefIsUsedByChild.js │ ├── parseOffsetAsPixels.js │ ├── parseOffsetAsPercentage.js │ ├── onNextTick.js │ └── getCurrentPosition.js └── README.md ├── source-code.redux-observable ├── redux-observable.png ├── redux-observable-next.png ├── redux-observable-pipe.png ├── redux-observable-subscribe.png └── src │ ├── index.js │ ├── utils │ └── console.js │ ├── operators.js │ ├── StateObservable.js │ ├── ActionsObservable.js │ ├── combineEpics.js │ └── createEpicMiddleware.js ├── source-code.fast-memoizeVSnano-memoize ├── fast-memoize.png ├── README.md └── src │ ├── fast-memoize.js │ └── nano-memoize.js ├── source-code.ant-design ├── src │ ├── tabs │ │ └── TabPane.md │ ├── _util │ │ ├── warning.md │ │ ├── getScroll.md │ │ └── throttleByAnimationFrame.md │ ├── card │ │ ├── Grid.md │ │ └── Meta.md │ ├── rc-animate │ │ ├── util.md │ │ ├── AnimateChild.md │ │ └── ChildrenUtils.md │ ├── rc-dialog │ │ ├── LazyRenderBox.md │ │ └── DialogWrap.md │ ├── rc-util │ │ ├── Portal.md │ │ └── getScrollBarSize.md │ ├── rc-tabs │ │ ├── TabPane.md │ │ ├── ScrollableInkTabBar.md │ │ └── TabContent.md │ ├── anchor │ │ └── AnchorLink.md │ └── modal │ │ └── Modal.md └── README.md ├── README.md ├── source-code.graphql-request ├── README.md └── src │ ├── types.ts │ └── index.ts ├── source-code.create-react-app(webpack配置) └── README.md └── navigation.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /source-code.lodash/README.md: -------------------------------------------------------------------------------- 1 | ## Lodash 源码注释 -------------------------------------------------------------------------------- /source-code.lazyestload/README.md: -------------------------------------------------------------------------------- 1 | 一个简单的图片延迟加载 2 | 3 | ![](./lazyestload.png) -------------------------------------------------------------------------------- /source-code.zepto/README.md: -------------------------------------------------------------------------------- 1 | # source-code.zepto 2 | Zepto v1.2.0 3 | 4 | 阅读[zepto源码](./src/zepto.js) 5 | -------------------------------------------------------------------------------- /source-code.stat/Stat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.stat/Stat.png -------------------------------------------------------------------------------- /source-code.redux/Redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.redux/Redux.png -------------------------------------------------------------------------------- /source-code.pubsub-js/PubSub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.pubsub-js/PubSub.png -------------------------------------------------------------------------------- /source-code.reselect/reselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.reselect/reselect.png -------------------------------------------------------------------------------- /source-code.unstated/unstated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.unstated/unstated.png -------------------------------------------------------------------------------- /source-code.hyperapp/hyperapp导图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.hyperapp/hyperapp导图.jpg -------------------------------------------------------------------------------- /source-code.fullpage/README.md: -------------------------------------------------------------------------------- 1 | # source-code.fullpage 2 | 3 | fullPage 2.5.4 4 | 5 | 阅读[fullpage源码(依赖jquery)](src/jquery.fullPage.js) 6 | -------------------------------------------------------------------------------- /source-code.jquery-mousewheel/README.md: -------------------------------------------------------------------------------- 1 | # source-code.jquery-mousewheel 2 | v3.1.13 3 | 4 | 阅读[jquery-mousewheel源码](src/JQmousewheel.js) 5 | -------------------------------------------------------------------------------- /source-code.lazyestload/lazyestload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.lazyestload/lazyestload.png -------------------------------------------------------------------------------- /source-code.react-redux/React-Redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.react-redux/React-Redux.png -------------------------------------------------------------------------------- /source-code.hyperapp/hyperapp-patch导图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.hyperapp/hyperapp-patch导图.png -------------------------------------------------------------------------------- /source-code.reach-router/reachRouter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.reach-router/reachRouter.png -------------------------------------------------------------------------------- /source-code.react-motion/react-motion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.react-motion/react-motion.png -------------------------------------------------------------------------------- /source-code.signature_pad/signature_pad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.signature_pad/signature_pad.png -------------------------------------------------------------------------------- /source-code.reach-router/reachRouter-Focus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.reach-router/reachRouter-Focus.gif -------------------------------------------------------------------------------- /source-code.react-loadable/react-loadabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.react-loadable/react-loadabel.png -------------------------------------------------------------------------------- /source-code.react-snapshot/react-snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.react-snapshot/react-snapshot.png -------------------------------------------------------------------------------- /source-code.react-waypoint/react-waypoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.react-waypoint/react-waypoint.png -------------------------------------------------------------------------------- /source-code.redux-observable/redux-observable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.redux-observable/redux-observable.png -------------------------------------------------------------------------------- /source-code.react-waypoint/src/constants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | above: 'above', 3 | inside: 'inside', 4 | below: 'below', 5 | invisible: 'invisible', 6 | }; 7 | -------------------------------------------------------------------------------- /source-code.redux-observable/redux-observable-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.redux-observable/redux-observable-next.png -------------------------------------------------------------------------------- /source-code.redux-observable/redux-observable-pipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.redux-observable/redux-observable-pipe.png -------------------------------------------------------------------------------- /source-code.fast-memoizeVSnano-memoize/fast-memoize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.fast-memoizeVSnano-memoize/fast-memoize.png -------------------------------------------------------------------------------- /source-code.redux-observable/redux-observable-subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonehank/sourcecode-analysis/HEAD/source-code.redux-observable/redux-observable-subscribe.png -------------------------------------------------------------------------------- /source-code.react-redux/src/components/Context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const ReactReduxContext = React.createContext(null) 4 | 5 | export default ReactReduxContext 6 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/debugLog.js: -------------------------------------------------------------------------------- 1 | export default function debugLog() { 2 | if (process.env.NODE_ENV !== 'production') { 3 | console.log(arguments); // eslint-disable-line no-console 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /source-code.react-redux/src/utils/wrapActionCreators.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux' 2 | 3 | export default function wrapActionCreators(actionCreators) { 4 | return dispatch => bindActionCreators(actionCreators, dispatch) 5 | } 6 | -------------------------------------------------------------------------------- /source-code.react-motion/src/presets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | export default { 3 | noWobble: {stiffness: 170, damping: 26}, // the default, if nothing provided 4 | gentle: {stiffness: 120, damping: 14}, 5 | wobbly: {stiffness: 180, damping: 12}, 6 | stiff: {stiffness: 210, damping: 20}, 7 | }; 8 | -------------------------------------------------------------------------------- /source-code.stat/README.md: -------------------------------------------------------------------------------- 1 | 一个js性能监控器,使用canvas绘制,简洁美观,思想也不复杂 2 | 3 | 1. 通过`performance.now`获取当前开始时间,每次调用时,计算时间差,每隔1秒计算每一帧平均消耗的毫秒数,从而计算1000毫秒的帧数。 4 | 5 | 2. 根据传入的当前fps和给定的最高fps绘制canvas,分为整体层,字体层,柱状条层。 6 | 7 | 3. 因为不使用clearRect清除画布,每次新创建的柱状条不会被清除,从而展示出监测器的效果。 8 | 9 | stat: 10 | 11 | ![](./Stat.png) -------------------------------------------------------------------------------- /source-code.react-redux/src/index.js: -------------------------------------------------------------------------------- 1 | import Provider from './components/Provider' 2 | import connectAdvanced from './components/connectAdvanced' 3 | import { ReactReduxContext } from './components/Context' 4 | import connect from './connect/connect' 5 | 6 | export { Provider, connectAdvanced, ReactReduxContext, connect } 7 | -------------------------------------------------------------------------------- /source-code.redux-observable/src/index.js: -------------------------------------------------------------------------------- 1 | export { createEpicMiddleware } from './createEpicMiddleware'; 2 | export { ActionsObservable } from './ActionsObservable'; 3 | export { StateObservable } from './StateObservable'; 4 | export { combineEpics } from './combineEpics'; 5 | export { ofType } from './operators'; 6 | 7 | -------------------------------------------------------------------------------- /source-code.ant-design/src/tabs/TabPane.md: -------------------------------------------------------------------------------- 1 | ### Tabs.TabPane 2 | 3 | * 作为```Tabs```的children使用 4 | * 在```Tabs```渲染的时候,会被传到rc-tabs的```ScrollableInkTabBar```和```TabContent```中 5 | * 在上面2个组件中会分别获取标签tab内容和children内容作为tab的菜单标题和tab的内容 6 | * 详细见[ScrollableInkTabBar](../rc-tabs/ScrollableInkTabBar.md)和[TabContent](../rc-tabs/TabContent.md) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 关于阅读项目 2 | 3 | * 所有源码文件(带注释)放到src目录中。 4 | 5 | * 不定期寻找一些较小的框架、库、工具等热门项目进行阅读分析,以后使用或者扩展都会方便许多。 6 | 7 | * 进行流程导图制作,能对其内部整体结构和执行流动有一个直观整体的认识。 8 | 9 | * 每次阅读项目会对学习到的新方法、新想法进行记录到[个人博客](https://stonehank.github.io)里面 10 | 11 | * 个人水平有限,阅读过程中也有遇到无法理解的,会用`//todo xxx`标注,如有发现纰漏,请不吝指正。 12 | 13 | 14 | [*进入目录*](./navigation.md) 15 | -------------------------------------------------------------------------------- /source-code.graphql-request/README.md: -------------------------------------------------------------------------------- 1 | `graphql-request`一个极小的`graphql`请求方法 2 | 3 | 如果不太了解`graphql`,看一下这个库,或许能知道它的请求就可以这么简单... 4 | 5 | 就是熟悉的`fetch`,这里使用了`cross-fetch`(兼容的fetch) 6 | 7 | 8 | 1. `Content-Type`设置为`application/json` 9 | 2. `method`设置为`POST` 10 | 3. 合并自定义`headers` 11 | 4. 传入`query`(`graphql`的查询模板),`variables`(查询模板中的变量,可选) 12 | 13 | 剩下的只需要等待数据返回。 14 | 15 | 16 | -------------------------------------------------------------------------------- /source-code.redux/README.md: -------------------------------------------------------------------------------- 1 | # source-code.redux 2 | 3 | v4.0.0 4 | 5 | redux源码设计真的很精妙 6 | 7 | 改变状态是从store一层一层往下找,找到对应的type修改状态,再一层一层返回 8 | 9 | middleware是链式调用,每一环的返回值作为下一环的dispatch,就像滚雪球一样,最终能得到一个强大的dispatch 10 | 11 | 那么读完了是否能将这种设计思想变为自己的? 12 | 13 | 不能...刚读完去回想整个流程,缺漏的很多,真的是环环相扣 14 | 15 | 但多读几遍,或许哪天写代码的时候灵光一现,用上了某种设计,或许就真的变成你自己的东西了 16 | 17 | 详细解释在源码注释中,思维导图: 18 | 19 | ![](./Redux.png) -------------------------------------------------------------------------------- /source-code.react-redux/src/utils/verifyPlainObject.js: -------------------------------------------------------------------------------- 1 | import isPlainObject from './isPlainObject' 2 | import warning from './warning' 3 | 4 | export default function verifyPlainObject(value, displayName, methodName) { 5 | if (!isPlainObject(value)) { 6 | warning( 7 | `${methodName}() in ${displayName} must return a plain object. Instead received ${value}.` 8 | ) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source-code.react-motion/src/spring.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import presets from './presets'; 3 | import type {OpaqueConfig, SpringHelperConfig} from './Types'; 4 | 5 | const defaultConfig = { 6 | ...presets.noWobble, 7 | precision: 0.01, 8 | }; 9 | 10 | export default function spring(val: number, config?: SpringHelperConfig): OpaqueConfig { 11 | return {...defaultConfig, ...config, val}; 12 | } 13 | -------------------------------------------------------------------------------- /source-code.ant-design/src/_util/warning.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import warning from 'warning'; 3 | 4 | const warned: { [msg: string]: boolean} = {}; 5 | export default (valid: boolean, message: string): void => { 6 | // valid为false 并且 第一次warning 7 | if (!valid && !warned[message]) { 8 | // warning,当第一个参数为false,发出message 9 | warning(false, message); 10 | warned[message] = true; 11 | } 12 | }; 13 | 14 | ``` -------------------------------------------------------------------------------- /source-code.react-motion/src/reorderKeys.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let hasWarned = false; 4 | export default function reorderKeys() { 5 | if (process.env.NODE_ENV === 'development') { 6 | if (!hasWarned) { 7 | hasWarned = true; 8 | console.error( 9 | '`reorderKeys` has been removed, since it is no longer needed for TransitionMotion\'s new styles array API.' 10 | ); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source-code.react-motion/src/mapToZero.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import type {PlainStyle, Style} from './Types'; 3 | 4 | // currently used to initiate the velocity style object to 0 5 | export default function mapToZero(obj: Style | PlainStyle): PlainStyle { 6 | let ret = {}; 7 | for (const key in obj) { 8 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 9 | ret[key] = 0; 10 | } 11 | } 12 | return ret; 13 | } 14 | -------------------------------------------------------------------------------- /source-code.react-motion/src/react-motion.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | export {default as Motion} from './Motion'; 3 | export {default as StaggeredMotion} from './StaggeredMotion'; 4 | export {default as TransitionMotion} from './TransitionMotion'; 5 | export {default as spring} from './spring'; 6 | export {default as presets} from './presets'; 7 | export {default as stripStyle} from './stripStyle'; 8 | 9 | // deprecated, dummy warning function 10 | export {default as reorderKeys} from './reorderKeys'; 11 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/isDOMElement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * When an element's type is a string, it represents a DOM node with that tag name 3 | * https://facebook.github.io/react/blog/2015/12/18/react-components-elements-and-instances.html#dom-elements 4 | * 5 | * @param {React.element} Component 6 | * @return {bool} Whether the component is a DOM Element 7 | */ 8 | export default function isDOMElement(Component) { 9 | // 确认是原生DOM 10 | return (typeof Component.type === 'string'); 11 | } 12 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/resolveScrollableAncestorProp.js: -------------------------------------------------------------------------------- 1 | export default function resolveScrollableAncestorProp(scrollableAncestor) { 2 | // When Waypoint is rendered on the server, `window` is not available. 3 | // To make Waypoint easier to work with, we allow this to be specified in 4 | // string form and safely convert to `window` here. 5 | // 当传入字符串`window`,返回server-rendering的window 6 | if (scrollableAncestor === 'window') { 7 | return global.window; 8 | } 9 | 10 | return scrollableAncestor; 11 | } 12 | -------------------------------------------------------------------------------- /source-code.create-react-app(webpack配置)/README.md: -------------------------------------------------------------------------------- 1 | * 7-29:更新至webpack-config-dev.js 2 | 3 | * 9-25:更新webpackDevServer.config.js 4 | 5 | * 10-16:更新webpack.config.prod.js 6 | 7 | 粗读了一遍`create-react-app.js`,主要是用node对检测和创建上的操作,还有安装依赖, 8 | 下次会将它粗略分析下,并且将导图画出 9 | 10 | `webpack-config-dev.js`是开发模式下的配置,基本上每一行都有注释,解释了代码的功能和相关插件的作用 11 | (等待后续更新) 12 | 13 | ---------- 14 | 看完config.prod和config.dev,觉得`CRA`的webpack配置真的是解决了大量的issue才能达到今天的优化地步,基本每一次选用插件或者 15 | 修改配置options都有独特的issue。 16 | 17 | 要真正做到理解`CRA`,还需要去理解这些问题的产生,去看一看他们的讨论,要不然以后遇到同样的问题,还要重新去寻求解决办法。 -------------------------------------------------------------------------------- /source-code.react-snapshot/src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import ReactDOMServer from 'react-dom/server'; 3 | 4 | export const render = (rootComponent, domElement) => { 5 | // 是在Node的jsdom的环境下,并且window.reactSnapshotRender已经创建 6 | if (navigator.userAgent.match(/Node\.js/i) && window && window.reactSnapshotRender) { 7 | // 执行服务端渲染,插入innerHTML中 8 | domElement.innerHTML = ReactDOMServer.renderToString(rootComponent) 9 | // 改变flag 10 | window.reactSnapshotRender() 11 | } else { 12 | ReactDOM.render(rootComponent, domElement) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source-code.react-redux/src/utils/isPlainObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {any} obj The object to inspect. 3 | * @returns {boolean} True if the argument appears to be a plain object. 4 | */ 5 | export default function isPlainObject(obj) { 6 | if (typeof obj !== 'object' || obj === null) return false 7 | 8 | let proto = Object.getPrototypeOf(obj) 9 | if (proto === null) return true 10 | 11 | let baseProto = proto 12 | while (Object.getPrototypeOf(baseProto) !== null) { 13 | baseProto = Object.getPrototypeOf(baseProto) 14 | } 15 | 16 | return proto === baseProto 17 | } 18 | -------------------------------------------------------------------------------- /source-code.ant-design/README.md: -------------------------------------------------------------------------------- 1 | # antd-comments 2 | 3 | 阅读ant-design源码(部分) 4 | 5 | ### 什么是整体? 6 | 7 |  在我们阅读项目源码之前,如果你对它的功能一无所知,那简直是最可怕的事情... 8 | 经常不知不觉就深入其中的逻辑,就像进入一团团迷雾,甚至会觉得有很多重复没必要的东西,完全不知道它们的区别。 9 | 10 |  当然,值的我们阅读的源码,绝大部分每个功能都是有意义的,而极少重复。 11 | 12 |  因此,不能一开始就深入其中,而往往先需要知道功能,会用功能,再去阅读源码。 13 | 14 |  ant-design是一个华丽的项目,将最常用组件及其逻辑搭配大量css进行组装, 15 | 我们只需外部调用API,即可轻松搭建美观的界面,同时,ant-design也是可扩展的,底层是react-component, 16 | 更为复杂,功能也更加强大。 17 | 18 |  此项目的理想目的就是希望能从整体入手,除了源码细节注释外,在每一个页面的开头会有一段注释,对当前页面的整体功能做了个介绍, 19 | 如果需要自行配置,只看介绍就可以知道要去哪里细看进行配置;如果需要深入源码,看完整体介绍,再去细看源码, 20 | 能提升阅读效率。 21 | 22 | 23 | -------------------------------------------------------------------------------- /source-code.react-motion/src/stripStyle.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | // turn {x: {val: 1, stiffness: 1, damping: 2}, y: 2} generated by 3 | // `{x: spring(1, {stiffness: 1, damping: 2}), y: 2}` into {x: 1, y: 2} 4 | // 转换x或者y本身或者它的val 5 | 6 | import type {Style, PlainStyle} from './Types'; 7 | 8 | export default function stripStyle(style: Style): PlainStyle { 9 | let ret = {}; 10 | for (const key in style) { 11 | if (!Object.prototype.hasOwnProperty.call(style, key)) { 12 | continue; 13 | } 14 | ret[key] = typeof style[key] === 'number' ? style[key] : style[key].val; 15 | } 16 | return ret; 17 | } 18 | -------------------------------------------------------------------------------- /source-code.hyperapp/README.md: -------------------------------------------------------------------------------- 1 | # source-code.hyperapp 2 | 3 | v1.2.6 4 | 5 | 一个极小的(1kb)类react框架,实现了virtual-dom,diff比较(需要key),数据绑定到actions上,通过actions改变数据,会自动更新页面 6 | 7 | 主要步骤: 8 | * 通过app()对其参数进行处理 9 | * 克隆state(不在原state上修改) 10 | * 增强actions,当调用时会自动检查结果result和原state是否有变化,有变化进行render 11 | * 将state和增强actions绑定到view上 12 | * 初始化render,执行patch 13 | 14 | patch是hyperapp进行节点更新的核心,主要步骤: 15 | * 先判断node节点 16 | * 当节点相同--->进行key判断--->递归patch,直到指针到尾部 17 | * 最后判断是否需要删除节点 18 | * 返回更新的element 19 | 20 | 思维导图(整体): 21 | 22 | ![](hyperapp导图.jpg) 23 | 24 | 思维导图(patch): 25 | 26 | ![](hyperapp-patch导图.png) 27 | 28 | [具体源码](./src/hyperapp.js) -------------------------------------------------------------------------------- /source-code.redux-observable/src/utils/console.js: -------------------------------------------------------------------------------- 1 | let deprecationsSeen = {}; 2 | export const resetDeprecationsSeen = () => { 3 | deprecationsSeen = {}; 4 | }; 5 | 6 | const consoleWarn = (typeof console === 'object' && typeof console.warn === 'function') 7 | ? (...args) => console.warn(...args) 8 | : () => { }; 9 | 10 | export const deprecate = msg => { 11 | if (!deprecationsSeen[msg]) { 12 | deprecationsSeen[msg] = true; 13 | consoleWarn(`redux-observable | DEPRECATION: ${msg}`); 14 | } 15 | }; 16 | 17 | export const warn = msg => { 18 | consoleWarn(`redux-observable | WARNING: ${msg}`); 19 | }; 20 | -------------------------------------------------------------------------------- /source-code.redux-observable/src/operators.js: -------------------------------------------------------------------------------- 1 | import { filter } from 'rxjs/operators'; 2 | 3 | const keyHasType = (type, key) => { 4 | return type === key || typeof key === 'function' && type === key.toString(); 5 | }; 6 | 7 | export const ofType = (...keys) => (source) => source.pipe( 8 | filter(({ type }) => { 9 | const len = keys.length; 10 | if (len === 1) { 11 | return keyHasType(type, keys[0]); 12 | } else { 13 | for (let i = 0; i < len; i++) { 14 | if (keyHasType(type, keys[i])) { 15 | return true; 16 | } 17 | } 18 | } 19 | return false; 20 | }) 21 | ); 22 | -------------------------------------------------------------------------------- /source-code.ant-design/src/card/Grid.md: -------------------------------------------------------------------------------- 1 | ### Card.Grid 2 | 3 | 整体: 4 | * 就是按默认宽度33%(如果未自定义,一行最多放3个)放置 5 | 6 | 源码: 7 | ```jsx 8 | import * as React from 'react'; 9 | import classNames from 'classnames'; 10 | 11 | export interface CardGridProps { 12 | prefixCls?: string; 13 | style?: React.CSSProperties; 14 | className?: string; 15 | } 16 | //div 默认宽度33% 左浮动,hover有效果 17 | export default (props: CardGridProps) => { 18 | const { prefixCls = 'ant-card', className, ...others } = props; 19 | const classString = classNames(`${prefixCls}-grid`, className); 20 | return
; 21 | }; 22 | 23 | ``` -------------------------------------------------------------------------------- /source-code.react-waypoint/src/ensureChildrenIsValid.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const errorMessage = 4 | ' expected to receive a single React element child.\n\n' + 5 | 'See https://goo.gl/LrBNgw for more info.'; 6 | 7 | /** 8 | * Raise an error if more that one child was provided to "children" 9 | * 10 | * @param {?React.element} children 11 | * @return {undefined} 12 | */ 13 | export default function ensureChildrenIsValid(children) { 14 | // 确保 15 | // children存在 16 | // 只存在1个children,不是text 17 | if (children) { 18 | try { 19 | React.Children.only(children); 20 | } catch (e) { 21 | throw new Error(errorMessage); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source-code.redux/src/utils/warning.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prints a warning in the console if it exists. 3 | * 4 | * @param {String} message The warning message. 5 | * @returns {void} 6 | */ 7 | export default function warning(message) { 8 | /* eslint-disable no-console */ 9 | if (typeof console !== 'undefined' && typeof console.error === 'function') { 10 | console.error(message) 11 | } 12 | /* eslint-enable no-console */ 13 | try { 14 | // This error was thrown as a convenience so that if you enable 15 | // "break on all exceptions" in your console, 16 | // it would pause the execution at this line. 17 | throw new Error(message) 18 | } catch (e) {} // eslint-disable-line no-empty 19 | } 20 | -------------------------------------------------------------------------------- /source-code.ant-design/src/_util/getScroll.md: -------------------------------------------------------------------------------- 1 | ```js 2 | export default function getScroll(target: any, top: boolean): number { 3 | if (typeof window === 'undefined') { 4 | return 0; 5 | } 6 | 7 | //如果是window则用pageYoffset,非window用scrollTop(pageYoffset是只读的,性能更好,但只能用于整个文档) 8 | const prop = top ? 'pageYOffset' : 'pageXOffset'; 9 | const method = top ? 'scrollTop' : 'scrollLeft'; 10 | const isWindow = target === window; 11 | 12 | let ret = isWindow ? target[prop] : target[method]; 13 | // ie6,7,8 standard mode 14 | // 兼容模式,使用document.documentElement.scrollTop; 15 | if (isWindow && typeof ret !== 'number') { 16 | ret = window.document.documentElement[method]; 17 | } 18 | 19 | return ret; 20 | } 21 | 22 | ``` -------------------------------------------------------------------------------- /source-code.redux-observable/src/StateObservable.js: -------------------------------------------------------------------------------- 1 | import { Observable, Subject } from 'rxjs'; 2 | 3 | export class StateObservable extends Observable { 4 | constructor(stateSubject, initialState) { 5 | super(subscriber => { 6 | const subscription = this.__notifier.subscribe(subscriber); 7 | if (subscription && !subscription.closed) { 8 | subscriber.next(this.value); 9 | } 10 | return subscription; 11 | }); 12 | 13 | this.value = initialState; 14 | this.__notifier = new Subject(); 15 | this.__subscription = stateSubject.subscribe(value => { 16 | if (value !== this.value) { 17 | this.value = value; 18 | this.__notifier.next(value); 19 | } 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source-code.lodash/src/isObjectLike.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if `value` is object-like. A value is object-like if it's not `null` 3 | * and has a `typeof` result of "object". 4 | * 5 | * @since 4.0.0 6 | * @category Lang 7 | * @param {*} value The value to check. 8 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`. 9 | * @example 10 | * 11 | * isObjectLike({}) 12 | * // => true 13 | * 14 | * isObjectLike([1, 2, 3]) 15 | * // => true 16 | * 17 | * isObjectLike(Function) 18 | * // => false 19 | * 20 | * isObjectLike(null) 21 | * // => false 22 | */ 23 | function isObjectLike(value) { 24 | // 只检查typeof为object除了null 25 | return typeof value == 'object' && value !== null 26 | } 27 | 28 | export default isObjectLike 29 | -------------------------------------------------------------------------------- /source-code.redux/src/utils/actionTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * These are private action types reserved by Redux. 3 | * For any unknown actions, you must return the current state. 4 | * If the current state is undefined, you must return the initial state. 5 | * Do not reference these action types directly in your code. 6 | */ 7 | 8 | // 取一个随机36进制数最多前6位,并用"."分离 9 | const randomString = () => 10 | Math.random() 11 | .toString(36) 12 | .substring(7) 13 | .split('') 14 | .join('.') 15 | 16 | // todo 17 | const ActionTypes = { 18 | INIT: `@@redux/INIT${randomString()}`, 19 | REPLACE: `@@redux/REPLACE${randomString()}`, 20 | PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}` 21 | } 22 | 23 | export default ActionTypes 24 | -------------------------------------------------------------------------------- /source-code.react-redux/src/utils/warning.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prints a warning in the console if it exists. 3 | * 4 | * @param {String} message The warning message. 5 | * @returns {void} 6 | */ 7 | export default function warning(message) { 8 | /* eslint-disable no-console */ 9 | if (typeof console !== 'undefined' && typeof console.error === 'function') { 10 | console.error(message) 11 | } 12 | /* eslint-enable no-console */ 13 | try { 14 | // This error was thrown as a convenience so that if you enable 15 | // "break on all exceptions" in your console, 16 | // it would pause the execution at this line. 17 | throw new Error(message) 18 | /* eslint-disable no-empty */ 19 | } catch (e) {} 20 | /* eslint-enable no-empty */ 21 | } 22 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/computeOffsetPixels.js: -------------------------------------------------------------------------------- 1 | import parseOffsetAsPercentage from './parseOffsetAsPercentage'; 2 | import parseOffsetAsPixels from './parseOffsetAsPixels'; 3 | 4 | /** 5 | * @param {string|number} offset 6 | * @param {number} contextHeight 7 | * @return {number} A number representing `offset` converted into pixels. 8 | */ 9 | export default function computeOffsetPixels(offset, contextHeight) { 10 | // 转化用户输入字符串offset 11 | const pixelOffset = parseOffsetAsPixels(offset); 12 | // 转化为数字直接返回 13 | if (typeof pixelOffset === 'number') { 14 | return pixelOffset; 15 | } 16 | // 处理带百分号的情况 17 | const percentOffset = parseOffsetAsPercentage(offset); 18 | if (typeof percentOffset === 'number') { 19 | return percentOffset * contextHeight; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/ensureRefIsUsedByChild.js: -------------------------------------------------------------------------------- 1 | import isDOMElement from './isDOMElement'; 2 | 3 | export const errorMessage = 4 | ' needs a DOM element to compute boundaries. The child you passed is neither a ' + 5 | 'DOM element (e.g.
) nor does it use the innerRef prop.\n\n' + 6 | 'See https://goo.gl/LrBNgw for more info.'; 7 | 8 | /** 9 | * Raise an error if "children" is not a DOM Element and there is no ref provided to Waypoint 10 | * 11 | * @param {?React.element} children 12 | * @param {?HTMLElement} ref 13 | * @return {undefined} 14 | */ 15 | export default function ensureRefIsProvidedByChild(children, ref) { 16 | // children不是原生DOM 并且 ref获取不到值 17 | if (children && !isDOMElement(children) && !ref) { 18 | throw new Error(errorMessage); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/parseOffsetAsPixels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Attempts to parse the offset provided as a prop as a pixel value. If 3 | * parsing fails, then `undefined` is returned. Three examples of values that 4 | * will be successfully parsed are: 5 | * `20` 6 | * "20px" 7 | * "20" 8 | * 9 | * @param {string|number} str A string of the form "{number}" or "{number}px", 10 | * or just a number. 11 | * @return {number|undefined} The numeric version of `str`. Undefined if `str` 12 | * was neither a number nor string ending in "px". 13 | */ 14 | // 转化用户输入字符串offset 15 | export default function parseOffsetAsPixels(str) { 16 | if (!isNaN(parseFloat(str)) && isFinite(str)) { 17 | return parseFloat(str); 18 | } else if (str.slice(-2) === 'px') { 19 | return parseFloat(str.slice(0, -2)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source-code.redux-observable/src/ActionsObservable.js: -------------------------------------------------------------------------------- 1 | import { Observable, of, from } from 'rxjs'; 2 | import { ofType } from './operators'; 3 | 4 | export class ActionsObservable extends Observable { 5 | // of 创建一个 observable 6 | static of(...actions) { 7 | return new this(of(...actions)); 8 | } 9 | // from 创建一个 observable 10 | static from(actions, scheduler) { 11 | return new this(from(actions, scheduler)); 12 | } 13 | 14 | constructor(actionsSubject) { 15 | super(); 16 | this.source = actionsSubject; 17 | } 18 | 19 | lift(operator) { 20 | const observable = new ActionsObservable(this); 21 | observable.operator = operator; 22 | return observable; 23 | } 24 | 25 | ofType(...keys) { 26 | return ofType(...keys)(this); 27 | } 28 | } 29 | window.ActionsObservable=ActionsObservable -------------------------------------------------------------------------------- /source-code.react-waypoint/src/parseOffsetAsPercentage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Attempts to parse the offset provided as a prop as a percentage. For 3 | * instance, if the component has been provided with the string "20%" as 4 | * a value of one of the offset props. If the value matches, then it returns 5 | * a numeric version of the prop. For instance, "20%" would become `0.2`. 6 | * If `str` isn't a percentage, then `undefined` will be returned. 7 | * 8 | * @param {string} str The value of an offset prop to be converted to a 9 | * number. 10 | * @return {number|undefined} The numeric version of `str`. Undefined if `str` 11 | * was not a percentage. 12 | */ 13 | // 处理百分号情况 14 | export default function parseOffsetAsPercentage(str) { 15 | if (str.slice(-1) === '%') { 16 | return parseFloat(str.slice(0, -1)) / 100; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source-code.pubsub-js/README.md: -------------------------------------------------------------------------------- 1 | > pubsub-js: 一个用 JavaScript 编写的`基于主题`的发布/订阅库 2 | 3 | 4 | ----- 5 | 6 | `pubsub-js`代码量不大,代码逻辑也较简单,也确实完成了一个发布订阅库,基于`javascript`的`key:value`普通对象。 7 | 8 | 代码结构 9 | ```js 10 | messages={ 11 | topic1:{ 12 | token1_1:Function, 13 | token1_2:Function, 14 | }, 15 | topic2:{ 16 | token2_1:Function, 17 | token2_2:Function, 18 | ... 19 | }, 20 | ... 21 | } 22 | ``` 23 | 24 | 这个`基于主题`结构,允许对同一个主题进行多次不同的订阅,因此每次订阅会返回一个`token`可以通过`unsubscribe`对 25 | `token`取消订阅, 26 | 27 | `unsubscribe`也接收`topic`(主题的名称),对当前主题下所有`token`取消订阅,也接收`function`,删除所有与参数一致的订阅函数。 28 | 29 | 同时`pubsub-js`也提供了层级订阅,通过`.`进行分级,例如当订阅了`a`和`a.b`时,发布`a.b`同时也会触发`a`的订阅,而通配符`*`则表示任何发布都将触发它的订阅。 30 | 31 | > **注意:`pubsub-js`是一个简单的发布订阅库,并不能解决真正的异步问题。** 32 | 33 | ---- 34 | 35 | 思维导图: 36 | ![](./PubSub.png) 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /source-code.react-redux/src/connect/mapStateToProps.js: -------------------------------------------------------------------------------- 1 | import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps' 2 | 3 | // 如果 mapStateToProps 是函数, 返回一个执行函数, 4 | // 可以给这个函数传入state或者dispatch(取决于是 mapStateToProps 还是 mapDispatchToProps) 和displayName 5 | // 这个返回的函数执行后的作用是,检测是否需要ownProps并且执行 mapStateToProps() 6 | // 最终返回一个朴素对象,如果不是朴素对象则报错 7 | export function whenMapStateToPropsIsFunction(mapStateToProps) { 8 | return typeof mapStateToProps === 'function' 9 | ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps') 10 | : undefined 11 | } 12 | 13 | // 如果不存在 mapStateToProps,默认传递 空函数 14 | export function whenMapStateToPropsIsMissing(mapStateToProps) { 15 | return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined 16 | } 17 | 18 | // 除了函数,其它格式会报错 19 | export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing] 20 | -------------------------------------------------------------------------------- /source-code.lodash/src/isObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if `value` is the 3 | * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) 4 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 5 | * 6 | * @since 0.1.0 7 | * @category Lang 8 | * @param {*} value The value to check. 9 | * @returns {boolean} Returns `true` if `value` is an object, else `false`. 10 | * @example 11 | * 12 | * isObject({}) 13 | * // => true 14 | * 15 | * isObject([1, 2, 3]) 16 | * // => true 17 | * 18 | * isObject(Function) 19 | * // => true 20 | * 21 | * isObject(null) 22 | * // => false 23 | */ 24 | function isObject(value) { 25 | const type = typeof value 26 | // 不是null ,是object或者function 27 | return value != null && (type == 'object' || type == 'function') 28 | } 29 | 30 | export default isObject 31 | -------------------------------------------------------------------------------- /source-code.react-redux/src/utils/shallowEqual.js: -------------------------------------------------------------------------------- 1 | const hasOwn = Object.prototype.hasOwnProperty 2 | 3 | function is(x, y) { 4 | if (x === y) { 5 | return x !== 0 || y !== 0 || 1 / x === 1 / y 6 | } else { 7 | return x !== x && y !== y 8 | } 9 | } 10 | 11 | export default function shallowEqual(objA, objB) { 12 | if (is(objA, objB)) return true 13 | 14 | if ( 15 | typeof objA !== 'object' || 16 | objA === null || 17 | typeof objB !== 'object' || 18 | objB === null 19 | ) { 20 | return false 21 | } 22 | 23 | const keysA = Object.keys(objA) 24 | const keysB = Object.keys(objB) 25 | 26 | if (keysA.length !== keysB.length) return false 27 | 28 | for (let i = 0; i < keysA.length; i++) { 29 | if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { 30 | return false 31 | } 32 | } 33 | 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /source-code.redux/src/compose.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Composes single-argument functions from right to left. The rightmost 3 | * function can take multiple arguments as it provides the signature for 4 | * the resulting composite function. 5 | * 6 | * @param {...Function} funcs The functions to compose. 7 | * @returns {Function} A function obtained by composing the argument functions 8 | * from right to left. For example, compose(f, g, h) is identical to doing 9 | * (...args) => f(g(h(...args))). 10 | */ 11 | 12 | // 将(fun1,fun2,fun3)转换成fun1(fun2(fun3())) 13 | // 这么嵌套执行的意义是传递统一的参数,后续有说明 14 | export default function compose(...funcs) { 15 | if (funcs.length === 0) { 16 | return arg => arg 17 | } 18 | 19 | if (funcs.length === 1) { 20 | return funcs[0] 21 | } 22 | // 重点理解的一句 这里的reduce内部还返回了一个函数 23 | // 具体展开在applyMiddleware中 24 | return funcs.reduce((a, b) => (...args) => a(b(...args))) 25 | } 26 | -------------------------------------------------------------------------------- /source-code.react-redux/src/connect/verifySubselectors.js: -------------------------------------------------------------------------------- 1 | import warning from '../utils/warning' 2 | 3 | function verify(selector, methodName, displayName) { 4 | if (!selector) { 5 | throw new Error(`Unexpected value for ${methodName} in ${displayName}.`) 6 | } else if ( 7 | methodName === 'mapStateToProps' || 8 | methodName === 'mapDispatchToProps' 9 | ) { 10 | if (!selector.hasOwnProperty('dependsOnOwnProps')) { 11 | warning( 12 | `The selector for ${methodName} of ${displayName} did not specify a value for dependsOnOwnProps.` 13 | ) 14 | } 15 | } 16 | } 17 | 18 | export default function verifySubselectors( 19 | mapStateToProps, 20 | mapDispatchToProps, 21 | mergeProps, 22 | displayName 23 | ) { 24 | verify(mapStateToProps, 'mapStateToProps', displayName) 25 | verify(mapDispatchToProps, 'mapDispatchToProps', displayName) 26 | verify(mergeProps, 'mergeProps', displayName) 27 | } 28 | -------------------------------------------------------------------------------- /source-code.lodash/src/clamp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Clamps `number` within the inclusive `lower` and `upper` bounds. 3 | * 4 | * @since 4.0.0 5 | * @category Number 6 | * @param {number} number The number to clamp. 7 | * @param {number} lower The lower bound. 8 | * @param {number} upper The upper bound. 9 | * @returns {number} Returns the clamped number. 10 | * @example 11 | * 12 | * clamp(-10, -5, 5) 13 | * // => -5 14 | * 15 | * clamp(10, -5, 5) 16 | * // => 5 17 | */ 18 | // 取中间值 19 | function clamp(number, lower, upper) { 20 | // 通过+ 转化为数字 21 | number = +number 22 | lower = +lower 23 | upper = +upper 24 | // 如果非数字,则为0 25 | lower = lower === lower ? lower : 0 26 | upper = upper === upper ? upper : 0 27 | 28 | // number必须是数字 29 | if (number === number) { 30 | // 设定number为中间值 31 | number = number <= upper ? number : upper 32 | number = number >= lower ? number : lower 33 | } 34 | return number 35 | } 36 | 37 | export default clamp 38 | -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-animate/util.md: -------------------------------------------------------------------------------- 1 | 2 | * appear:首次出现动画 3 | * enter:后续出现动画 4 | * leave:后续离开动画 5 | * animation:自定义动画(使用js)而不是className 6 | 7 | 8 | ```js 9 | 10 | 11 | const util = { 12 | // isXXXSupported 指既可以使用className,也可以使用自定义js 13 | isAppearSupported(props) { 14 | return props.transitionName && props.transitionAppear || props.animation.appear; 15 | }, 16 | isEnterSupported(props) { 17 | return props.transitionName && props.transitionEnter || props.animation.enter; 18 | }, 19 | isLeaveSupported(props) { 20 | return props.transitionName && props.transitionLeave || props.animation.leave; 21 | }, 22 | allowAppearCallback(props) { 23 | return props.transitionAppear || props.animation.appear; 24 | }, 25 | allowEnterCallback(props) { 26 | return props.transitionEnter || props.animation.enter; 27 | }, 28 | allowLeaveCallback(props) { 29 | return props.transitionLeave || props.animation.leave; 30 | }, 31 | }; 32 | export default util; 33 | 34 | 35 | ``` -------------------------------------------------------------------------------- /source-code.redux-observable/src/combineEpics.js: -------------------------------------------------------------------------------- 1 | import { merge } from 'rxjs'; 2 | 3 | /** 4 | Merges all epics into a single one. 5 | */ 6 | export const combineEpics = (...epics) => { 7 | const merger = (...args) => merge( 8 | ...epics.map(epic => { 9 | const output$ = epic(...args); 10 | if (!output$) { 11 | throw new TypeError(`combineEpics: one of the provided Epics "${epic.name || ''}" does not return a stream. Double check you\'re not missing a return statement!`); 12 | } 13 | return output$; 14 | }) 15 | ); 16 | 17 | // Technically the `name` property on Function's are supposed to be read-only. 18 | // While some JS runtimes allow it anyway (so this is useful in debugging) 19 | // some actually throw an exception when you attempt to do so. 20 | try { 21 | Object.defineProperty(merger, 'name', { 22 | value: `combineEpics(${epics.map(epic => epic.name || '').join(', ')})`, 23 | }); 24 | } catch (e) {} 25 | 26 | return merger; 27 | }; 28 | -------------------------------------------------------------------------------- /source-code.signature_pad/src/point.ts: -------------------------------------------------------------------------------- 1 | // Interface for point data structure used e.g. in SignaturePad#fromData method 2 | export interface IBasicPoint { 3 | x: number; 4 | y: number; 5 | time: number; 6 | } 7 | 8 | // 建立点的对象 9 | export class Point implements IBasicPoint { 10 | public time: number; 11 | 12 | constructor(public x: number, public y: number, time?: number) { 13 | this.time = time || Date.now(); 14 | } 15 | 16 | // 计算当前点到 start 的直线距离 17 | public distanceTo(start: IBasicPoint): number { 18 | return Math.sqrt( 19 | Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2), 20 | ); 21 | } 22 | 23 | // 判断两个点对象是否完全相同 24 | public equals(other: IBasicPoint): boolean { 25 | return this.x === other.x && this.y === other.y && this.time === other.time; 26 | } 27 | 28 | // 计算从 start 点到当前点的速度 v=△s/△t 29 | public velocityFrom(start: IBasicPoint): number { 30 | return this.time !== start.time 31 | ? this.distanceTo(start) / (this.time - start.time) 32 | : 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source-code.lodash/src/isFunction.js: -------------------------------------------------------------------------------- 1 | import baseGetTag from './.internal/baseGetTag.js' 2 | import isObject from './isObject.js' 3 | 4 | /** 5 | * Checks if `value` is classified as a `Function` object. 6 | * 7 | * @since 0.1.0 8 | * @category Lang 9 | * @param {*} value The value to check. 10 | * @returns {boolean} Returns `true` if `value` is a function, else `false`. 11 | * @example 12 | * 13 | * isFunction(_) 14 | * // => true 15 | * 16 | * isFunction(/abc/) 17 | * // => false 18 | */ 19 | function isFunction(value) { 20 | // 不是object并且不是function 21 | if (!isObject(value)) { 22 | return false 23 | } 24 | // The use of `Object#toString` avoids issues with the `typeof` operator 25 | // in Safari 9 which returns 'object' for typed arrays and other constructors. 26 | // 除了symbol和undefined和null,都是toString.call() 27 | const tag = baseGetTag(value) 28 | return tag == '[object Function]' || tag == '[object AsyncFunction]' || 29 | tag == '[object GeneratorFunction]' || tag == '[object Proxy]' 30 | } 31 | 32 | export default isFunction 33 | -------------------------------------------------------------------------------- /source-code.react-motion/src/shouldStopAnimation.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import type {PlainStyle, Style, Velocity} from './Types'; 3 | 4 | // usage assumption: currentStyle values have already been rendered but it says 5 | // nothing of whether currentStyle is stale (see unreadPropStyle) 6 | 7 | // 停止动画的条件: 8 | // 1. 速度为0 9 | // 2. 目标为当前位置 10 | export default function shouldStopAnimation( 11 | currentStyle: PlainStyle, 12 | style: Style, 13 | currentVelocity: Velocity, 14 | ): boolean { 15 | for (let key in style) { 16 | // 原型链的属性跳过 17 | if (!Object.prototype.hasOwnProperty.call(style, key)) { 18 | continue; 19 | } 20 | if (currentVelocity[key] !== 0) { 21 | return false; 22 | } 23 | 24 | const styleValue = typeof style[key] === 'number' 25 | ? style[key] 26 | : style[key].val; 27 | // stepper will have already taken care of rounding precision errors, so 28 | // won't have such thing as 0.9999 !=== 1 29 | if (currentStyle[key] !== styleValue) { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | -------------------------------------------------------------------------------- /source-code.redux/src/utils/isPlainObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {any} obj The object to inspect. 3 | * @returns {boolean} True if the argument appears to be a plain object. 4 | */ 5 | // 判断是否简单对象 6 | // 此处的目的是为了让action不要有原型链,即不要使用这种模式 7 | /* 8 | ```jsx 9 | function reducer(state, action) { 10 | if (action instanceof SomeAction) { 11 | return ... 12 | } else { 13 | return ... 14 | } 15 | } 16 | ``` 17 | 根据Dan Abramov所说 18 | >The "plain objects" isn't really the point. 19 | >What we want to enforce is that you don't rely on actions being instances of specific classes in your reducers, 20 | >because this will never be the case after (de)serialization. 21 | 22 | 使用上面的模式,当通过对象序列化和反序列化后,原型链上的方法都会失效. 23 | */ 24 | 25 | export default function isPlainObject(obj) { 26 | // 先判断typeof 27 | if (typeof obj !== 'object' || obj === null) return false 28 | 29 | // 通过prototype向上查找,一直查找到原型链的顶端 30 | let proto = obj 31 | while (Object.getPrototypeOf(proto) !== null) { 32 | proto = Object.getPrototypeOf(proto) 33 | } 34 | // 判断obj的prototype是否是原型链的顶端 35 | return Object.getPrototypeOf(obj) === proto 36 | } 37 | -------------------------------------------------------------------------------- /source-code.react-waypoint/src/onNextTick.js: -------------------------------------------------------------------------------- 1 | let timeout; 2 | const timeoutQueue = []; 3 | 4 | export default function onNextTick(cb) { 5 | // 放入队列 6 | timeoutQueue.push(cb); 7 | 8 | // 如果timeout不存在,setTimeout 0 执行队列,确保任务不冲突 9 | // 如果timeout存在,说明任务队列已经开始执行 10 | if (!timeout) { 11 | timeout = setTimeout(() => { 12 | timeout = null; 13 | 14 | // Drain the timeoutQueue 15 | let item; 16 | // eslint-disable-next-line no-cond-assign 17 | while (item = timeoutQueue.shift()) { 18 | item(); 19 | } 20 | }, 0); 21 | } 22 | 23 | let isSubscribed = true; 24 | 25 | // 返回一个取消的函数 26 | return function unsubscribe() { 27 | if (!isSubscribed) { 28 | return; 29 | } 30 | 31 | isSubscribed = false; 32 | 33 | const index = timeoutQueue.indexOf(cb); 34 | // 当前cb已经执行完毕 35 | if (index === -1) { 36 | return; 37 | } 38 | 39 | // 清除 40 | timeoutQueue.splice(index, 1); 41 | 42 | // 如果任务队列无任务 并且 计时器还存在 43 | if (!timeoutQueue.length && timeout) { 44 | // 清除计时器 45 | clearTimeout(timeout); 46 | timeout = null; 47 | } 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-dialog/LazyRenderBox.md: -------------------------------------------------------------------------------- 1 | ## rc-lazyRenderBox 2 | 3 | 整体: 4 | * 渲染一个div,当传入hiddenClassName存在时,根据visible来判断是否需要添加hidden的className 5 | 6 | 7 | ```tsx 8 | import * as React from 'react'; 9 | 10 | export interface ILazyRenderBoxPropTypes { 11 | className?: string; 12 | visible?: boolean; 13 | hiddenClassName?: string; 14 | role?: string; 15 | style?: {}; 16 | } 17 | 18 | export default class LazyRenderBox extends React.Component { 19 | shouldComponentUpdate(nextProps: ILazyRenderBoxPropTypes) { 20 | // 未传递hiddenClassName并且不可见,才会阻止渲染 21 | return !!nextProps.hiddenClassName || !!nextProps.visible; 22 | } 23 | render() { 24 | let className = this.props.className; 25 | // hiddenClassName存在 并且 不可见 26 | if (!!this.props.hiddenClassName && !this.props.visible) { 27 | // className 添加 hiddenClassName 28 | className += ` ${this.props.hiddenClassName}`; 29 | } 30 | // 删除visible和hiddenC,传递给div,渲染 31 | const props: any = { ...this.props }; 32 | delete props.hiddenClassName; 33 | delete props.visible; 34 | props.className = className; 35 | return
; 36 | } 37 | } 38 | 39 | ``` -------------------------------------------------------------------------------- /source-code.lodash/src/create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates an object that inherits from the `prototype` object. If a 3 | * `properties` object is given, its own enumerable string keyed properties 4 | * are assigned to the created object. 5 | * 6 | * @since 2.3.0 7 | * @category Object 8 | * @param {Object} prototype The object to inherit from. 9 | * @param {Object} [properties] The properties to assign to the object. 10 | * @returns {Object} Returns the new object. 11 | * @example 12 | * 13 | * function Shape() { 14 | * this.x = 0 15 | * this.y = 0 16 | * } 17 | * Circle 继承 Shape 18 | * function Circle() { 19 | * Shape.call(this) 20 | * } 21 | * 22 | * // 重写Circle的prototype为一个新对象 23 | * Circle.prototype = create(Shape.prototype, { 24 | * 'constructor': Circle 25 | * }) 26 | * 27 | * const circle = new Circle 28 | * circle instanceof Circle 29 | * // => true 30 | * 31 | * circle instanceof Shape 32 | * // => true 33 | */ 34 | // 创建非引用对象 35 | function create(prototype, properties) { 36 | prototype = prototype === null ? null : Object(prototype) 37 | // prototype作为新对象的prototype 38 | const result = Object.create(prototype) 39 | // properties浅合并 40 | return properties == null ? result : Object.assign(result, properties) 41 | } 42 | 43 | export default create 44 | -------------------------------------------------------------------------------- /source-code.signature_pad/src/throttle.ts: -------------------------------------------------------------------------------- 1 | // Slightly simplified version of http://stackoverflow.com/a/27078401/815507 2 | // 一个简化版的throttle 3 | export function throttle(fn: (...args: any[]) => any, wait = 250) { 4 | let previous = 0; 5 | let timeout: number | null = null; 6 | let result: any; 7 | let storedContext: any; 8 | let storedArgs: any[]; 9 | 10 | const later = () => { 11 | previous = Date.now(); 12 | timeout = null; 13 | result = fn.apply(storedContext, storedArgs); 14 | 15 | if (!timeout) { 16 | storedContext = null; 17 | storedArgs = []; 18 | } 19 | }; 20 | 21 | return function wrapper(this: any, ...args: any[]) { 22 | const now = Date.now(); 23 | const remaining = wait - (now - previous); 24 | 25 | storedContext = this; 26 | storedArgs = args; 27 | 28 | if (remaining <= 0 || remaining > wait) { 29 | if (timeout) { 30 | clearTimeout(timeout); 31 | timeout = null; 32 | } 33 | 34 | previous = now; 35 | result = fn.apply(storedContext, storedArgs); 36 | 37 | if (!timeout) { 38 | storedContext = null; 39 | storedArgs = []; 40 | } 41 | } else if (!timeout) { 42 | timeout = window.setTimeout(later, remaining); 43 | } 44 | 45 | return result; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /source-code.react-snapshot/src/Writer.js: -------------------------------------------------------------------------------- 1 | /* Simple wrapper around fs so I can concentrate on what's going on */ 2 | import fs from 'fs' 3 | import path from 'path' 4 | import { sync as mkDirPSync } from 'mkdirp' 5 | 6 | export default class Writer { 7 | constructor(baseDir, outputDir) { 8 | this.baseDir = baseDir 9 | // 如果用户自定义output 则创建 10 | // 默认 outputDIr===baseDir==='xxx/build' 11 | if (outputDir !== baseDir) { 12 | mkDirPSync(outputDir) 13 | } 14 | this.outputDir = outputDir 15 | } 16 | 17 | move(from, to) { 18 | /* Only do this if we still have an index.html 19 | (i.e. this is the first run post build) */ 20 | // 解析为绝对路径 21 | const fromPath = path.resolve(this.baseDir, from); 22 | // 如果存在 23 | if (fs.existsSync(fromPath)) { 24 | /* This _must_ be in the BUILD directory, not the OUTPUT directory, since 25 | * that's how our Server is configured. */ 26 | // 重命名 27 | fs.renameSync(fromPath, path.resolve(this.baseDir, to)) 28 | } 29 | } 30 | 31 | write(filename, content) { 32 | // 合并路径 33 | const newPath = path.join(this.outputDir, filename) 34 | // 获取目录路径 35 | const dirName = path.dirname(newPath) 36 | // 创建目录 37 | mkDirPSync(dirName) 38 | // 写入内容 39 | fs.writeFileSync(newPath, content) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-util/Portal.md: -------------------------------------------------------------------------------- 1 | 整体: 2 | * React >16 的 createPortal 传送门方法 3 | * [createPortal 传送门方法](https://zhuanlan.zhihu.com/p/29880992?utm_source=wechat_session&utm_medium=social&from=singlemessage) 4 | 5 | 6 | ```js 7 | import React from 'react'; 8 | import ReactDOM from 'react-dom'; 9 | import PropTypes from 'prop-types'; 10 | 11 | export default class Portal extends React.Component { 12 | static propTypes = { 13 | getContainer: PropTypes.func.isRequired, 14 | children: PropTypes.node.isRequired, 15 | didUpdate: PropTypes.func, 16 | } 17 | 18 | componentDidMount() { 19 | this.createContainer(); 20 | } 21 | 22 | componentDidUpdate(prevProps) { 23 | const { didUpdate } = this.props; 24 | if (didUpdate) { 25 | didUpdate(prevProps); 26 | } 27 | } 28 | 29 | componentWillUnmount() { 30 | this.removeContainer(); 31 | } 32 | 33 | createContainer() { 34 | this._container = this.props.getContainer(); 35 | this.forceUpdate(); 36 | } 37 | 38 | removeContainer() { 39 | if (this._container) { 40 | this._container.parentNode.removeChild(this._container); 41 | } 42 | } 43 | 44 | render() { 45 | if (this._container) { 46 | return ReactDOM.createPortal(this.props.children, this._container); 47 | } 48 | return null; 49 | } 50 | } 51 | 52 | ``` -------------------------------------------------------------------------------- /source-code.react-redux/src/connect/mapDispatchToProps.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux' 2 | import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps' 3 | 4 | export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) { 5 | // 如果是function 返回一个执行函数, 6 | // 可以给这个函数传入state或者dispatch(取决于是 mapStateToProps 还是 mapDispatchToProps)和displayName 7 | // 这个返回的函数执行后的作用是,检测是否需要ownProps并且执行 mapStateToProps() 8 | // 最终返回一个朴素对象,如果不是朴素对象则报错 9 | return typeof mapDispatchToProps === 'function' 10 | ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps') 11 | : undefined 12 | } 13 | 14 | export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) { 15 | // 如果不存在,则默认提供 dispatch 16 | return !mapDispatchToProps 17 | ? wrapMapToPropsConstant(dispatch => ({ dispatch })) 18 | : undefined 19 | } 20 | 21 | // 如果type是object,通过 bindActionCreators 绑定dispatch到定义的object 22 | export function whenMapDispatchToPropsIsObject(mapDispatchToProps) { 23 | return mapDispatchToProps && typeof mapDispatchToProps === 'object' 24 | ? wrapMapToPropsConstant(dispatch => 25 | bindActionCreators(mapDispatchToProps, dispatch) 26 | ) 27 | : undefined 28 | } 29 | 30 | export default [ 31 | whenMapDispatchToPropsIsFunction, 32 | whenMapDispatchToPropsIsMissing, 33 | whenMapDispatchToPropsIsObject 34 | ] 35 | -------------------------------------------------------------------------------- /source-code.lodash/src/isPlainObject.js: -------------------------------------------------------------------------------- 1 | import baseGetTag from './.internal/baseGetTag.js' 2 | import isObjectLike from './isObjectLike.js' 3 | 4 | /** 5 | * Checks if `value` is a plain object, that is, an object created by the 6 | * `Object` constructor or one with a `[[Prototype]]` of `null`. 7 | * 8 | * @since 0.8.0 9 | * @category Lang 10 | * @param {*} value The value to check. 11 | * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. 12 | * @example 13 | * 14 | * function Foo() { 15 | * this.a = 1 16 | * } 17 | * 18 | * isPlainObject(new Foo) 19 | * // => false 20 | * 21 | * isPlainObject([1, 2, 3]) 22 | * // => false 23 | * 24 | * isPlainObject({ 'x': 0, 'y': 0 }) 25 | * // => true 26 | * 27 | * isPlainObject(Object.create(null)) 28 | * // => true 29 | */ 30 | function isPlainObject(value) { 31 | // typeof 为object不为null 并且 toString为[object Object],才继续处理 32 | if (!isObjectLike(value) || baseGetTag(value) != '[object Object]') { 33 | return false 34 | } 35 | // 无prototype 36 | if (Object.getPrototypeOf(value) === null) { 37 | return true 38 | } 39 | let proto = value 40 | 41 | while (Object.getPrototypeOf(proto) !== null) { 42 | // 找到原型链最顶端(非null),也就是 Object 43 | proto = Object.getPrototypeOf(proto) 44 | } 45 | // 判断prototype是否为Object 46 | return Object.getPrototypeOf(value) === proto 47 | } 48 | 49 | export default isPlainObject 50 | -------------------------------------------------------------------------------- /source-code.redux/src/index.js: -------------------------------------------------------------------------------- 1 | import createStore from './createStore' 2 | import combineReducers from './combineReducers' 3 | import bindActionCreators from './bindActionCreators' 4 | import applyMiddleware from './applyMiddleware' 5 | import compose from './compose' 6 | import warning from './utils/warning' 7 | import __DO_NOT_USE__ActionTypes from './utils/actionTypes' 8 | 9 | /* 10 | * This is a dummy function to check if the function name has been altered by minification. 11 | * If the function has been minified and NODE_ENV !== 'production', warn the user. 12 | */ 13 | 14 | function isCrushed() {} 15 | 16 | // 判断是否在非生产环境下使用了压缩 17 | if ( 18 | // node环境变量 19 | process.env.NODE_ENV !== 'production' && 20 | // 当前函数名是isCrushed 21 | // 用来检测是否被minified 22 | typeof isCrushed.name === 'string' && 23 | isCrushed.name !== 'isCrushed' 24 | ) { 25 | warning( 26 | 'You are currently using minified code outside of NODE_ENV === "production". ' + 27 | 'This means that you are running a slower development build of Redux. ' + 28 | 'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' + 29 | 'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' + 30 | 'to ensure you have the correct code for your production build.' 31 | ) 32 | } 33 | 34 | export { 35 | createStore, 36 | combineReducers, 37 | bindActionCreators, 38 | applyMiddleware, 39 | compose, 40 | __DO_NOT_USE__ActionTypes 41 | } 42 | -------------------------------------------------------------------------------- /source-code.ant-design/src/card/Meta.md: -------------------------------------------------------------------------------- 1 | ### Card.Meta 2 | 3 | * 整体: 可以定义头像,标题,内容 4 | * 渲染后的排版格式大致如下: 5 | * ______________________ 6 | |avatar| title | 7 | | | //此处BFC | 8 | | | description | 9 | |______|_____________| 10 | 11 | 12 | 源码: 13 | ```jsx 14 | import * as React from 'react'; 15 | import classNames from 'classnames'; 16 | 17 | export interface CardMetaProps { 18 | prefixCls?: string; 19 | style?: React.CSSProperties; 20 | className?: string; 21 | avatar?: React.ReactNode; 22 | title?: React.ReactNode; 23 | description?: React.ReactNode; 24 | } 25 | //多个div封装格式,通过改变className来定义样式 26 | export default (props: CardMetaProps) => { 27 | const { prefixCls = 'ant-card', className, avatar, title, description, ...others } = props; 28 | const classString = classNames(`${prefixCls}-meta`, className); 29 | const avatarDom = avatar ?
{avatar}
: null; 30 | const titleDom = title ?
{title}
: null; 31 | const descriptionDom = description ? 32 |
{description}
: null; 33 | const MetaDetail = titleDom || descriptionDom ? 34 |
35 | {titleDom} 36 | {descriptionDom} 37 |
: null; 38 | return ( 39 |
40 | {avatarDom} 41 | {MetaDetail} 42 |
43 | ); 44 | }; 45 | 46 | ``` -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-util/getScrollBarSize.md: -------------------------------------------------------------------------------- 1 | 整体: 2 | * 获取默认滚动条宽度,如果之前已经获取过了,并且参数fresh为false,则从缓存调取 3 | 4 | 5 | ```js 6 | let cached; 7 | 8 | export default function getScrollBarSize(fresh) { 9 | // 如果参数为true 则不使用cache,重新计算 10 | if (fresh || cached === undefined) { 11 | const inner = document.createElement('div'); 12 | inner.style.width = '100%'; 13 | inner.style.height = '200px'; 14 | 15 | const outer = document.createElement('div'); 16 | const outerStyle = outer.style; 17 | 18 | outerStyle.position = 'absolute'; 19 | outerStyle.top = 0; 20 | outerStyle.left = 0; 21 | // 不会成为鼠标事件的目标 22 | outerStyle.pointerEvents = 'none'; 23 | outerStyle.visibility = 'hidden'; 24 | outerStyle.width = '200px'; 25 | outerStyle.height = '150px'; 26 | // 初始无滚动条 27 | outerStyle.overflow = 'hidden'; 28 | 29 | outer.appendChild(inner); 30 | 31 | document.body.appendChild(outer); 32 | 33 | // 无滚动条下inner宽度 34 | const widthContained = inner.offsetWidth; 35 | // 设置滚动条 36 | outer.style.overflow = 'scroll'; 37 | // 有滚动条下 inner宽度 38 | let widthScroll = inner.offsetWidth; 39 | 40 | // todo 特殊情况 offsetWidth两者相等? 41 | if (widthContained === widthScroll) { 42 | // 如果 inner.offsetWidth 算出相等,则用outer的clientWidth 43 | // clientWidth===offsetWidth-边框-滚动轴宽度 44 | widthScroll = outer.clientWidth; 45 | } 46 | 47 | document.body.removeChild(outer); 48 | // 保存滚动条宽度为cache 49 | cached = widthContained - widthScroll; 50 | } 51 | return cached; 52 | } 53 | 54 | ``` -------------------------------------------------------------------------------- /source-code.ant-design/src/_util/throttleByAnimationFrame.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import raf from 'raf'; 3 | 4 | 5 | export default function throttleByAnimationFrame(fn: (...args: any[]) => void) { 6 | let requestId: number | null; 7 | 8 | const later = (args: any[]) => () => { 9 | requestId = null; 10 | fn(...args); 11 | }; 12 | 13 | const throttled = (...args: any[]) => { 14 | if (requestId == null) { 15 | // raf:requestAnimationFrame 16 | requestId = raf(later(args)); 17 | } 18 | }; 19 | 20 | (throttled as any).cancel = () => raf.cancel(requestId!); 21 | 22 | return throttled; 23 | } 24 | 25 | 26 | export function throttleByAnimationFrameDecorator() { 27 | //传入类的(静态成员)构造函数或者(实例成员)类的原型,成员的key,成员的属性描述 28 | return function(target: any, key: string, descriptor: any) { 29 | //成员的值 30 | let fn = descriptor.value; 31 | let definingProperty = false; 32 | //返回成员的属性描述 33 | return { 34 | configurable: true, 35 | get() { 36 | //对于实例成员 37 | //this是组件 38 | if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) { 39 | //返回值 40 | return fn; 41 | } 42 | 43 | let boundFn = throttleByAnimationFrame(fn.bind(this)); 44 | definingProperty = true; 45 | Object.defineProperty(this, key, { 46 | value: boundFn, 47 | configurable: true, 48 | //Object.defineProperty后writable默认为false,因此要设置为true 49 | writable: true, 50 | }); 51 | definingProperty = false; 52 | //返回配置了 requestAnimationFrame 的fn 53 | return boundFn; 54 | }, 55 | }; 56 | }; 57 | } 58 | 59 | ``` -------------------------------------------------------------------------------- /source-code.lodash/src/slice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a slice of `array` from `start` up to, but not including, `end`. 3 | * 4 | * **Note:** This method is used instead of 5 | * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are 6 | * returned. 7 | * 8 | * @since 3.0.0 9 | * @category Array 10 | * @param {Array} array The array to slice. 11 | * @param {number} [start=0] The start position. A negative index will be treated as an offset from the end. 12 | * @param {number} [end=array.length] The end position. A negative index will be treated as an offset from the end. 13 | * @returns {Array} Returns the slice of `array`. 14 | * @example 15 | * 16 | * var array = [1, 2, 3, 4] 17 | * 18 | * _.slice(array, 2) 19 | * // => [3, 4] 20 | */ 21 | // 比原生更快 22 | // https://jsperf.com/hank-test-slice 23 | function slice(array, start, end) { 24 | let length = array == null ? 0 : array.length 25 | if (!length) { 26 | return [] 27 | } 28 | // 设定start和end默认值 29 | start = start == null ? 0 : start 30 | end = end === undefined ? length : end 31 | 32 | // start为负数,相当于倒序,绝对值不能超过length(不允许负数,因为后面要用 >>> ) 33 | if (start < 0) { 34 | start = -start > length ? 0 : (length + start) 35 | } 36 | // end负数为倒序 37 | end = end > length ? length : end 38 | if (end < 0) { 39 | end += length 40 | } 41 | // x>>>0 相当于 Math.floor(x)向下取整(这里start一定大于等于0) 42 | length = start > end ? 0 : ((end - start) >>> 0) 43 | start >>>= 0 44 | 45 | let index = -1 46 | const result = new Array(length) 47 | // 逐个添加进result,返回result 48 | while (++index < length) { 49 | result[index] = array[index + start] 50 | } 51 | return result 52 | } 53 | 54 | export default slice 55 | -------------------------------------------------------------------------------- /source-code.react-motion/src/stepper.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // stepper is used a lot. Saves allocation to return the same array wrapper. 4 | // This is fine and danger-free against mutations because the callsite 5 | // immediately destructures it and gets the numbers inside without passing the 6 | // array reference around. 7 | let reusedTuple: [number, number] = [0, 0]; 8 | export default function stepper( 9 | // 每一帧所用秒数 10 | secondPerFrame: number, 11 | // 当前位置 12 | x: number, 13 | // 当前速度 14 | v: number, 15 | // 目标位置 16 | destX: number, 17 | // 弹性 18 | k: number, 19 | // 阻力 20 | b: number, 21 | // 精度(用于停止动画) 22 | precision: number): [number, number] { 23 | // Spring stiffness, in kg / s^2 24 | 25 | // for animations, destX is really spring length (spring at rest). initial 26 | // position is considered as the stretched/compressed position of a spring 27 | // 计算当前弹性力(离目标越近,弹性越小) 28 | const Fspring = -k * (x - destX); 29 | 30 | // Damping, in kg / s 31 | // 计算当前阻力(速度越快,阻力越大) 32 | const Fdamper = -b * v; 33 | 34 | // usually we put mass here, but for animation purposes, specifying mass is a 35 | // bit redundant. you could simply adjust k and b accordingly 36 | // let a = (Fspring + Fdamper) / mass; 37 | // 当前加速度,忽略质量 38 | const a = Fspring + Fdamper; 39 | 40 | // 新的速度 41 | const newV = v + a * secondPerFrame; 42 | // 新的位置 43 | const newX = x + newV * secondPerFrame; 44 | 45 | // 停止动画的条件,小于当前精度,则速度为0,位置为目标 46 | if (Math.abs(newV) < precision && Math.abs(newX - destX) < precision) { 47 | reusedTuple[0] = destX; 48 | reusedTuple[1] = 0; 49 | return reusedTuple; 50 | } 51 | 52 | // 返回 53 | reusedTuple[0] = newX; 54 | reusedTuple[1] = newV; 55 | return reusedTuple; 56 | } 57 | -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-tabs/TabPane.md: -------------------------------------------------------------------------------- 1 | 整体: 2 | * 作为```TabContent```的children被渲染 3 | * 根据传入的属性,添加不同的className 4 | * TabPane渲染出的格式: 5 | ``` 6 | div .prefix-tabpane prefix-tabpane-(active|inactive) 7 | children(或者placeholder) 8 | ``` 9 | 源码: 10 | ```jsx 11 | import React from 'react'; 12 | import PropTypes from 'prop-types'; 13 | import createReactClass from 'create-react-class'; 14 | import classnames from 'classnames'; 15 | import { getDataAttr } from './utils'; 16 | 17 | const TabPane = createReactClass({ 18 | displayName: 'TabPane', 19 | propTypes: { 20 | className: PropTypes.string, 21 | active: PropTypes.bool, 22 | style: PropTypes.any, 23 | destroyInactiveTabPane: PropTypes.bool, 24 | forceRender: PropTypes.bool, 25 | placeholder: PropTypes.node, 26 | }, 27 | getDefaultProps() { 28 | return { placeholder: null }; 29 | }, 30 | render() { 31 | const { 32 | className, destroyInactiveTabPane, active, forceRender, 33 | rootPrefixCls, style, children, placeholder, ...restProps, 34 | } = this.props; 35 | this._isActived = this._isActived || active; 36 | const prefixCls = `${rootPrefixCls}-tabpane`; 37 | const cls = classnames({ 38 | [prefixCls]: 1, 39 | [`${prefixCls}-inactive`]: !active, 40 | [`${prefixCls}-active`]: active, 41 | [className]: className, 42 | }); 43 | const isRender = destroyInactiveTabPane ? active : this._isActived; 44 | return ( 45 |
52 | {isRender || forceRender ? children : placeholder} 53 |
54 | ); 55 | }, 56 | }); 57 | 58 | export default TabPane; 59 | 60 | ``` -------------------------------------------------------------------------------- /source-code.react-redux/src/connect/mergeProps.js: -------------------------------------------------------------------------------- 1 | import verifyPlainObject from '../utils/verifyPlainObject' 2 | 3 | export function defaultMergeProps(stateProps, dispatchProps, ownProps) { 4 | return { ...ownProps, ...stateProps, ...dispatchProps } 5 | } 6 | 7 | export function wrapMergePropsFunc(mergeProps) { 8 | return function initMergePropsProxy( 9 | dispatch, 10 | // areMergedPropsEqual 就是 shallowEqual 11 | { displayName, pure, areMergedPropsEqual } 12 | ) { 13 | let hasRunOnce = false 14 | let mergedProps 15 | 16 | return function mergePropsProxy(stateProps, dispatchProps, ownProps) { 17 | // 执行 mergeProps 18 | const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps) 19 | 20 | // 后续是否重复更新(判断结果是否相等) 21 | if (hasRunOnce) { 22 | // 后续执行 23 | // 非pure 或者 不等(浅比较),更新数据 24 | if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps)) 25 | mergedProps = nextMergedProps 26 | } else { 27 | // 第一次执行 直接更新数据 28 | hasRunOnce = true 29 | mergedProps = nextMergedProps 30 | 31 | if (process.env.NODE_ENV !== 'production') 32 | verifyPlainObject(mergedProps, displayName, 'mergeProps') 33 | } 34 | 35 | return mergedProps 36 | } 37 | } 38 | } 39 | 40 | // 返回一个高阶函数,这个函数的需求是 dispatch 和 { displayName, pure, areMergedPropsEqual } 41 | export function whenMergePropsIsFunction(mergeProps) { 42 | return typeof mergeProps === 'function' 43 | ? wrapMergePropsFunc(mergeProps) 44 | : undefined 45 | } 46 | 47 | export function whenMergePropsIsOmitted(mergeProps) { 48 | // 如果没有传 mergeProps,提供函数 默认返回包含所有数据(state,props)的对象 49 | return !mergeProps ? () => defaultMergeProps : undefined 50 | } 51 | 52 | export default [whenMergePropsIsFunction, whenMergePropsIsOmitted] 53 | -------------------------------------------------------------------------------- /source-code.graphql-request/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Variables = { [key: string]: any } 2 | 3 | export interface Headers { 4 | [key: string]: string 5 | } 6 | 7 | export interface Options { 8 | method?: RequestInit['method'] 9 | headers?: Headers 10 | mode?: RequestInit['mode'] 11 | credentials?: RequestInit['credentials'] 12 | cache?: RequestInit['cache'] 13 | redirect?: RequestInit['redirect'] 14 | referrer?: RequestInit['referrer'] 15 | referrerPolicy?: RequestInit['referrerPolicy'] 16 | integrity?: RequestInit['integrity'] 17 | } 18 | 19 | export interface GraphQLError { 20 | message: string 21 | locations: { line: number, column: number }[] 22 | path: string[] 23 | } 24 | 25 | export interface GraphQLResponse { 26 | data?: any 27 | errors?: GraphQLError[] 28 | extensions?: any 29 | status: number 30 | [key: string]: any 31 | } 32 | 33 | export interface GraphQLRequestContext { 34 | query: string 35 | variables?: Variables 36 | } 37 | 38 | export class ClientError extends Error { 39 | 40 | response: GraphQLResponse 41 | request: GraphQLRequestContext 42 | 43 | constructor (response: GraphQLResponse, request: GraphQLRequestContext) { 44 | const message = `${ClientError.extractMessage(response)}: ${JSON.stringify({ response, request })}` 45 | 46 | super(message) 47 | 48 | this.response = response 49 | this.request = request 50 | 51 | // this is needed as Safari doesn't support .captureStackTrace 52 | /* tslint:disable-next-line */ 53 | if (typeof Error.captureStackTrace === 'function') { 54 | Error.captureStackTrace(this, ClientError) 55 | } 56 | } 57 | 58 | private static extractMessage (response: GraphQLResponse): string { 59 | try { 60 | return response.errors![0].message 61 | } catch (e) { 62 | return `GraphQL Error (Code: ${response.status})` 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source-code.lodash/src/.internal/stringToPath.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 关于此处正则,更详细解释查看 3 | * https://github.com/stonehank/blogs/blob/master/07-18-一个长正则的深入理解.md 4 | * */ 5 | 6 | import memoizeCapped from './memoizeCapped.js' 7 | 8 | const charCodeOfDot = '.'.charCodeAt(0) 9 | const reEscapeChar = /\\(\\)?/g 10 | const rePropName = RegExp( 11 | // Match anything that isn't a dot or bracket. 12 | // 匹配任意 非.和非[]的 13 | '[^.[\\]]+' + '|' + 14 | // Or match property names within brackets. 15 | // 匹配 [ 16 | '\\[(?:' + 17 | // Match a non-string expression. 18 | // 匹配(非" 或者 ')和后面任意数量值,即非字符串 19 | '([^"\'].*)' + '|' + 20 | // Or match strings (supports escaping characters). 21 | // ?: 不捕获括号 22 | // ?! 正向否定预查,即匹配除了 \\2,然后获取这个匹配值前面的内容 23 | // \\2 反向引用第二个捕获的括号 24 | // \\\\ 实际就是 \的转义 25 | // \\\\. 就是\的转义+任意字符 26 | // *? 就是匹配任意个,非贪婪 27 | // | 符号前面就是匹配非转义 (非\),后面就是匹配转义(\任意字符),最后*?)\\2 就是在匹配任意数量直到 \\2出现 28 | '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' + 29 | ')\\]'+ '|' + 30 | // Or match "" as the space between consecutive dots or empty brackets. 31 | '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))' 32 | , 'g') 33 | 34 | /** 35 | * Converts `string` to a property path array. 36 | * 37 | * @private 38 | * @param {string} string The string to convert. 39 | * @returns {Array} Returns the property path array. 40 | */ 41 | // 缓存化 42 | const stringToPath = memoizeCapped((string) => { 43 | const result = [] 44 | if (string.charCodeAt(0) === charCodeOfDot) { 45 | result.push('') 46 | } 47 | // match是匹配的值 expression匹配中括号内非字符串 quote匹配中括号的引号 subString是中括号具体字符串的值(无引号) 48 | string.replace(rePropName, (match, expression, quote, subString) => { 49 | 50 | let key = match 51 | if (quote) { 52 | // 将 \\ 转换成 \ 53 | key = subString.replace(reEscapeChar, '$1') 54 | } 55 | else if (expression) { 56 | key = expression.trim() 57 | } 58 | result.push(key) 59 | }) 60 | return result 61 | }) 62 | 63 | export default stringToPath 64 | -------------------------------------------------------------------------------- /navigation.md: -------------------------------------------------------------------------------- 1 | 按字母排序 2 | 3 | * [anime:一个轻量级JS现代动画库](./source-code.anime/README.md) 4 | 5 | * [ant-design:一整套React的UI组件(部分)](./source-code.ant-design/README.md) 6 | 7 | * [create-react-app(webpack配置)](./source-code.create-react-app(webpack配置)/README.md) 8 | 9 | * [fullpage:基于jQuery的全屏滚动插件](./source-code.fullpage/README.md) 10 | 11 | * [hyperapp:一个极小的(1kb)类React框架](./source-code.hyperapp/README.md) 12 | 13 | * [jquery-mousewheel:基于jQuery的鼠标滚轮插件](./source-code.jquery-mousewheel/README.md) 14 | 15 | * [fast-memoize && nano-memoize:目前最快的2个JS缓存工具对比](./source-code.fast-memoizeVSnano-memoize/README.md) 16 | 17 | * [graphql-request:适用于小型App的极小型GraphQL请求处理](./source-code.graphql-request/README.md) 18 | 19 | * [lazyestload.js 一个延迟加载图片的小工具](./source-code.lazyestload/README.md) 20 | 21 | * [pubsub-js 一个用 JavaScript 编写的`基于主题`的发布/订阅库](./source-code.pubsub-js/README.md) 22 | 23 | * [reach-router:React上全新的router组件(轻量级,支持React16.4api)](./source-code.reach-router/README.md) 24 | 25 | * [react-infinite-scroller:一个简单、轻量的滚动加载组件](./source-code.react-infinite-scroller/README.md) 26 | 27 | * [react-loadable:react的延迟加载组件,比较全面的处理各种情况](./source-code.react-loadable/README.md) 28 | 29 | * [react-motion:一个依据弹力算法动画的react组件,简洁美观](./source-code.react-motion/README.md) 30 | 31 | * [react-redux:高性能和可扩展的官方绑定React的Redux](./source-code.react-redux/README.md) 32 | 33 | * [react-snapshot:一个零配置静态预渲染工具](./source-code.react-snapshot/README.md) 34 | 35 | * [react-waypoint:判断元素是否出现某个滚动范围内](./source-code.react-waypoint/README.md) 36 | 37 | * [redux:一个状态管理工具](./source-code.redux/README.md) 38 | 39 | * [redux-observable:一个基于rxjs6的redux中间件,强大的异步操作功能](./source-code.redux-observable/README.md) 40 | 41 | * [reselect:一个redux的选择器(数据处理器)](./source-code.reselect/README.md) 42 | 43 | * [signature_pad:一个基于Canvas的平滑手写画板工具](./source-code.signature_pad/README.md) 44 | 45 | * [stat.js:一个简洁美观的JavaScript性能监视器](./source-code.stat/README.md) 46 | 47 | * [unstated:一个react极简状态管理组件](./source-code.unstated/README.md) 48 | 49 | * [zepto:轻量级的js库(类似Jquery的api)](./source-code.zepto/README.md) 50 | 51 | 52 | -------------------------------------------------------------------------------- /source-code.ant-design/src/anchor/AnchorLink.md: -------------------------------------------------------------------------------- 1 | ## AnchorLink组件 2 | 3 | 4 | 整体: 5 | * AnchorLink 挂载时添加href到links数组中(属于Anchor组件),links作用是遍历后计算每个href对应的dom的高度; 6 | * 点击后会执行context的toScroll方法 7 | 8 | 9 | ```jsx 10 | import * as React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import classNames from 'classnames'; 13 | 14 | export interface AnchorLinkProps { 15 | prefixCls?: string; 16 | href: string; 17 | title: React.ReactNode; 18 | children?: any; 19 | } 20 | 21 | export default class AnchorLink extends React.Component { 22 | static defaultProps = { 23 | prefixCls: 'ant-anchor', 24 | href: '#', 25 | }; 26 | 27 | // 接收context(旧版react) 28 | static contextTypes = { 29 | antAnchor: PropTypes.object, 30 | }; 31 | 32 | context: { 33 | antAnchor: any; 34 | }; 35 | 36 | // 给Anchor组件里的links添加 37 | componentDidMount() { 38 | this.context.antAnchor.registerLink(this.props.href); 39 | } 40 | // 从Anchor组建立的links删除 41 | componentWillUnmount() { 42 | this.context.antAnchor.unregisterLink(this.props.href); 43 | } 44 | 45 | // 点击锚链接,动画跳转到锚点 46 | handleClick = () => { 47 | this.context.antAnchor.scrollTo(this.props.href); 48 | } 49 | 50 | render() { 51 | const { 52 | prefixCls, 53 | href, 54 | title, 55 | children, 56 | } = this.props; 57 | const active = this.context.antAnchor.activeLink === href; 58 | const wrapperClassName = classNames(`${prefixCls}-link`, { 59 | [`${prefixCls}-link-active`]: active, 60 | }); 61 | const titleClassName = classNames(`${prefixCls}-link-title`, { 62 | [`${prefixCls}-link-title-active`]: active, 63 | }); 64 | return ( 65 |
66 | 72 | {title} 73 | 74 | {children} 75 |
76 | ); 77 | } 78 | } 79 | 80 | 81 | ``` -------------------------------------------------------------------------------- /source-code.react-loadable/src/webpack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const url = require('url'); 5 | 6 | function buildManifest(compiler, compilation) { 7 | let context = compiler.options.context; 8 | let manifest = {}; 9 | 10 | compilation.chunks.forEach(chunk => { 11 | chunk.files.forEach(file => { 12 | chunk.forEachModule(module => { 13 | let id = module.id; 14 | let name = typeof module.libIdent === 'function' ? module.libIdent({ context }) : null; 15 | let publicPath = url.resolve(compilation.outputOptions.publicPath || '', file); 16 | 17 | let currentModule = module; 18 | if (module.constructor.name === 'ConcatenatedModule') { 19 | currentModule = module.rootModule; 20 | } 21 | if (!manifest[currentModule.rawRequest]) { 22 | manifest[currentModule.rawRequest] = []; 23 | } 24 | 25 | manifest[currentModule.rawRequest].push({ id, name, file, publicPath }); 26 | }); 27 | }); 28 | }); 29 | 30 | return manifest; 31 | } 32 | 33 | class ReactLoadablePlugin { 34 | constructor(opts = {}) { 35 | this.filename = opts.filename; 36 | } 37 | 38 | apply(compiler) { 39 | compiler.plugin('emit', (compilation, callback) => { 40 | const manifest = buildManifest(compiler, compilation); 41 | var json = JSON.stringify(manifest, null, 2); 42 | const outputDirectory = path.dirname(this.filename); 43 | try { 44 | fs.mkdirSync(outputDirectory); 45 | } catch (err) { 46 | if (err.code !== 'EEXIST') { 47 | throw err; 48 | } 49 | } 50 | fs.writeFileSync(this.filename, json); 51 | callback(); 52 | }); 53 | } 54 | } 55 | 56 | function getBundles(manifest, moduleIds) { 57 | // moduleIds中每一个作为key,找到manifest[moduleId]合并到bundles(一个数组)中 58 | return moduleIds.reduce((bundles, moduleId) => { 59 | return bundles.concat(manifest[moduleId]); 60 | }, []); 61 | } 62 | 63 | exports.ReactLoadablePlugin = ReactLoadablePlugin; 64 | exports.getBundles = getBundles; 65 | -------------------------------------------------------------------------------- /source-code.react-snapshot/README.md: -------------------------------------------------------------------------------- 1 | `react-snapshot`一个服务端渲染组件,由于内部依赖库和相关`API`较老,而且有1年没更新了, 2 | 就不去太详细解释,有兴趣可以自行看源码内容`src目录`(带详细注释)。 3 | 4 | 读了这个库,要了解`服务端渲染`是什么流程,因此这里主要讲`执行流程`。 5 | 6 | ### 配置 7 | 8 | 这个库需要更改`package.json`的`build`,更改如下: 9 | 10 | ``` 11 | - "build": "react-scripts build" 12 | + "build": "react-scripts build && react-snapshot" 13 | ``` 14 | 15 | 然后改变`render` 16 | 17 | ``` 18 | - import ReactDOM from 'react-dom'; 19 | + import { render } from 'react-snapshot'; 20 | 21 | - ReactDOM.render( 22 | + render( 23 | , 24 | document.getElementById('root') 25 | ); 26 | ``` 27 | 28 | ### 执行流程 29 | 30 | 1. 输入`npm run build`,先执行`react-scripts build`,再执行`react-snapshot` 31 | 32 | 2. 通过`bin`内部文件执行`cli` 33 | 3. 执行`cli`,处理相关配置(目的是转换路径,确认需要快照的页面路径) 34 | 1. 获取`package.json`内部的`homepage`,确保以`/`结尾,如果不存在则用`/`表示。 35 | 2. 将路径转换为绝对路径。 36 | 3. 将`homepage`加入`include`,后面将会不断`.shift()`处理内部的路径。 37 | 4. 重命名之前的`index.html`为`200.html` 38 | 5. 定义服务器`Server` 39 | 1. 在`build`内部使用了`historyApiFallback`(防止SPA应用404)。 40 | 2. 托管静态`build`内资源到用户定义的`outputFile`中,默认也是`build`。 41 | 3. 处理了存在`proxy`(`package.json`内部的`proxy`)的情况。 42 | 6. 开启服务器(使用任意未占用端口) 43 | 7. 在上面端口中开启爬虫`Crawler` 44 | 1. 对参数`include`(数组)执行`.shift()`,开始爬行`snapshot` 45 | 1. 使用`jsdom`,创建虚拟dom。 46 | 2. 监听`window`创建,定义`window.reactSnapshotRender`,它的作用是改变一个`flag`。 47 | 48 | 这个`flag`的改变说明了当前`path`已经成功获取到`js`文件,并且执行了定义在客户端的`render`,已经将`React` 49 | 首页的`document`结构放到了虚拟`document`内部。 50 | 3. 拦截当前`path`上的外部资源请求,查看是否相同的`host`,并且符合`jsdom.feature`的配置。 51 | 52 | 当资源符合要求,便会请求并且执行(也就是`build`中`xxx.main.js`的代码),执行它便会触发`render`, 53 | 结果就回到第1步中说的改变`flag` 54 | 55 | 2. 此时虚拟`document`结构已经生成,经过`scriptJS`检查(匹配则删除)和`window.react_snapshot_state`, 56 | 它的作用是定义一个数据,客户端运行时,能从`window.react_snapshot_state`获取到这个数据。 57 | 3. 序列化这个`document`,检查`document`内部的``和` { 6 | return new Promise((resolve, reject) => { 7 | let reactSnapshotRenderCalled = false 8 | const url = `${protocol}//${host}${path}` 9 | jsdom.env({ 10 | // 当访问这个url时 11 | url, 12 | // 响应头设置 13 | headers: { Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" }, 14 | // 当前urlPath有外部资源请求时,拦截请求并且进行配置 15 | resourceLoader(resource, callback) { 16 | // host相同 17 | if (resource.url.host === host) { 18 | // 继续获取资源 19 | // 相当于 20 | /* 21 | resource.defaultFetch(function(err,body){ 22 | if(err) return callback(err); 23 | callback(null,body) 24 | }); 25 | */ 26 | resource.defaultFetch(callback); 27 | } else { 28 | // 不获取资源(未传参数) 29 | callback() 30 | } 31 | }, 32 | features: { 33 | // 允许jsdom 获取哪种类型的外部资源 34 | FetchExternalResources: ["script"], 35 | // 是否允许js的执行 ["script"] or `false` 36 | ProcessExternalResources: ["script"], 37 | // 需要过滤的特定资源 38 | SkipExternalResources: false 39 | }, 40 | // 将window.console 转到node的输出 41 | virtualConsole: jsdom.createVirtualConsole().sendTo(console), 42 | // 当window属性被创建的时候,执行 43 | created: (err, window) => { 44 | if (err) return reject(err) 45 | if (!window) return reject(`Looks like no page exists at ${url}`) 46 | // 定义 window.reactSnapshotRender 47 | window.reactSnapshotRender = () => { 48 | // 改变flag,说明已经执行了成功处理了请求拦截并且执行了render 49 | reactSnapshotRenderCalled = true 50 | setTimeout(() => { 51 | resolve(window) 52 | }, delay) 53 | } 54 | }, 55 | done: (err, window) => { 56 | // 如果flag未改变,说明出现问题 57 | if (!reactSnapshotRenderCalled) { 58 | reject("'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?") 59 | } 60 | } 61 | }) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /source-code.react-snapshot/src/Server.js: -------------------------------------------------------------------------------- 1 | /* Spin up a simple express server */ 2 | import express from 'express' 3 | import httpProxyMiddleware from 'http-proxy-middleware' 4 | import historyApiFallback from 'connect-history-api-fallback' 5 | 6 | export default class Server { 7 | // baseDir:./build 8 | // publicPath: package.json['homepage'] || '/' 9 | // port : 0 10 | // proxy : package.json['proxy'] || null 11 | constructor(baseDir, publicPath, port, proxy) { 12 | const app = express() 13 | 14 | app.get('*', (req, res, next) => { 15 | // This makes sure the sockets close down so that 16 | // we can gracefully shutdown the server 17 | // 服务器给客户端发送信息之后就断开 18 | res.set('Connection', 'close'); 19 | next() 20 | }) 21 | 22 | // Yes I just copied most of this from react-scripts ¯\_(ツ)_/¯ 23 | app.use(publicPath, 24 | historyApiFallback({ 25 | // 当SPA 刷新后路由路径无效,会请求 200.html 26 | index: '/200.html', 27 | // 允许使用 .(点)作为路径名(最后的/之后的) 例如 :http://xxx.com/a/b.com 28 | disableDotRule: true, 29 | // 允许的请求头type 30 | htmlAcceptHeaders: ['text/html'], 31 | }), 32 | // 静态资源文件 可以使用 /{publicPath}/xxx 请求去加载 {baseDir}/xxx 内部文件 33 | // index 为静态文件夹的主页 34 | express.static(baseDir, { index: '200.html' }) 35 | ) 36 | 37 | // 使用代理 38 | if (proxy) { 39 | if (typeof proxy !== "string") throw new Error("Only string proxies are implemented currently.") 40 | app.use(httpProxyMiddleware({ 41 | target: proxy, 42 | onProxyReq: proxyReq => { 43 | if (proxyReq.getHeader('origin')) proxyReq.setHeader('origin', proxy) 44 | }, 45 | changeOrigin: true, 46 | xfwd: true, 47 | })) 48 | } 49 | 50 | // 注入配置好的app参数 51 | this.start = this.start.bind(this, app, port) 52 | } 53 | 54 | start(app, port) { 55 | return new Promise((resolve, reject) => { 56 | // port默认为0 即任意一个未使用端口 57 | this.instance = app.listen(port, (err) => { 58 | if (err) { 59 | return reject(err) 60 | } 61 | 62 | resolve() 63 | }) 64 | }) 65 | } 66 | 67 | port() { 68 | return this.instance.address().port 69 | } 70 | 71 | stop() { 72 | this.instance.close() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /source-code.reselect/README.md: -------------------------------------------------------------------------------- 1 | ## reselect源码介绍 2 | 3 | v3.0.1 4 | 5 | 导图: 6 | ![](./reselect.png) 7 | 8 | ### defaultMemoize 9 | 10 | 一个缓存函数,其内部: 11 | 1. 通过闭包保存参数和结果 12 | 2. 每次调用对参数进行浅比较 13 | 3. 参数比较结果相同,返回缓存的结果 14 | 15 | ```js 16 | function defaultMemoize(func, equalityCheck = defaultEqualityCheck) { 17 | let lastArgs = null 18 | let lastResult = null 19 | return function () { 20 | // 判断当前参数和上一次的参数是否相同,默认为 ===(全等) 21 | if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) { 22 | // apply arguments instead of spreading for performance. 23 | // 不同则调用func 24 | lastResult = func.apply(null, arguments) 25 | } 26 | // 保存当前参数 27 | lastArgs = arguments 28 | // 返回result 29 | return lastResult 30 | } 31 | } 32 | ``` 33 | -------- 34 | ### createSelectorCreator, createSelector 35 | 36 | reselect最主要函数 37 | 1. `createSelectorCreator`接收一个参数,缓存函数,返回一个函数,称为`createSelector` 38 | 39 | `const createSelector = createSelectorCreator(defaultMemoize)` 40 | 41 | 2. `createSelector`接受2类参数 42 | 43 | `依赖数据函数`(可以有多个): 44 | 45 | 它的结果会传递给`数据处理函数`作为它的参数 46 | 47 | `数据处理函数`(必须放在参数的最后): 48 | 49 | 它接受的参数就是`依赖数据函数`的返回值 50 | 51 | 3. `createSelector`的内部操作 52 | 1. 对`依赖数据函数`和`数据处理函数`执行缓存函数 53 | 2. 每次执行`createSelector`的时候,会先执行`依赖数据函数`的缓存函数,检查参数是否相等,相等则返回旧的结果; 54 | 如果不相等,才会再执行`数据处理函数`的缓存函数,进行比较。 55 | 56 | 这样处理就可以知道要想返回缓存的结果,必须要达到以下条件任一: 57 | 1. `依赖数据函数`的参数(一般为`store`)全等比较为true 58 | 2. `依赖数据函数`的参数(一般为`store`)全等比较为false, `数据处理函数`的参数全等比较为true 59 | 60 | 4. 返回`依赖数据函数` 61 | 62 | [reselect使用例子](https://codesandbox.io/s/jlpozpjprw) 63 | 64 | ------------- 65 | 66 | ### createStructuredSelector 67 | 68 | 一个便利的函数,可以用于变更数据的key值,通过嵌套可以变更数据的结构 69 | 70 | 它的内部正是调用了`createSelector` 71 | 72 | 1. 接受2个函数,分别为一个(参数1)对象,一个(参数2)`selectorCreator`(默认就是createSelector) 73 | 74 | 2. 调用`createSelector`,将参数1(对象)的value值作为`依赖数据函数`, 75 | 其`数据处理函数`就是一个将参数1(对象)的key值和`依赖数据函数`的返回值组成一个新的对象的过程。 76 | 77 | [createStructuredSelector使用例子](https://codesandbox.io/s/53kvl30564) 78 | 79 | 80 | -------- 81 | 注意点: 82 | 83 | 1. 缓存函数只能保存上一次缓存的值(单个)。 84 | 85 | 2. 缓存函数是通过对比参数而进行判断的,因此必须保证所提供的`依赖数据函数`和`数据处理函数`都是纯函数,而且它只保存上一次函数。 86 | 87 | [非纯函数例子](https://codesandbox.io/s/n6y126v2p) 88 | 89 | 3. 要想取消缓存,必须取消引用,包括`依赖数据函数`参数(store)的引用和内部`数据处理函数`的参数引用 90 | 91 | [取消缓存例子](https://codesandbox.io/s/lx1kq3lj39) 92 | -------------------------------------------------------------------------------- /source-code.redux/src/bindActionCreators.js: -------------------------------------------------------------------------------- 1 | function bindActionCreator(actionCreator, dispatch) { 2 | return function() { 3 | // 此处的dispatch如果有middleware那就是已经加强的 4 | return dispatch(actionCreator.apply(this, arguments)) 5 | } 6 | } 7 | 8 | /** 9 | * Turns an object whose values are action creators, into an object with the 10 | * same keys, but with every function wrapped into a `dispatch` call so they 11 | * may be invoked directly. This is just a convenience method, as you can call 12 | * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. 13 | * 14 | * For convenience, you can also pass a single function as the first argument, 15 | * and get a function in return. 16 | * 17 | * @param {Function|Object} actionCreators An object whose values are action 18 | * creator functions. One handy way to obtain it is to use ES6 `import * as` 19 | * syntax. You may also pass a single function. 20 | * 21 | * @param {Function} dispatch The `dispatch` function available on your Redux 22 | * store. 23 | * 24 | * @returns {Function|Object} The object mimicking the original object, but with 25 | * every action creator wrapped into the `dispatch` call. If you passed a 26 | * function as `actionCreators`, the return value will also be a single 27 | * function. 28 | */ 29 | // 绑定dispatch和action,可以在组件里面直接调用,不需要给组件传递dispatch 30 | export default function bindActionCreators(actionCreators, dispatch) { 31 | // actionCreators是function就直接dispatch执行 32 | if (typeof actionCreators === 'function') { 33 | return bindActionCreator(actionCreators, dispatch) 34 | } 35 | 36 | // actionCreators不是object 或者 为null 报错 37 | if (typeof actionCreators !== 'object' || actionCreators === null) { 38 | throw new Error( 39 | `bindActionCreators expected an object or a function, instead received ${ 40 | actionCreators === null ? 'null' : typeof actionCreators 41 | }. ` + 42 | `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` 43 | ) 44 | } 45 | 46 | // actionCreators是obj的情况 47 | const keys = Object.keys(actionCreators) 48 | const boundActionCreators = {} 49 | for (let i = 0; i < keys.length; i++) { 50 | const key = keys[i] 51 | // 遍历获取每一个key对应的值 52 | const actionCreator = actionCreators[key] 53 | // 是function就执行后添加到boundActionCreators,其他类型不处理 54 | if (typeof actionCreator === 'function') { 55 | boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) 56 | } 57 | } 58 | return boundActionCreators 59 | } 60 | -------------------------------------------------------------------------------- /source-code.react-redux/src/components/Provider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { ReactReduxContext } from './Context' 4 | 5 | class Provider extends Component { 6 | constructor(props) { 7 | super(props) 8 | 9 | const { store } = props 10 | 11 | this.state = { 12 | storeState: store.getState(), 13 | store 14 | } 15 | } 16 | 17 | componentDidMount() { 18 | this._isMounted = true 19 | // 首次加载需要订阅 20 | this.subscribe() 21 | } 22 | 23 | componentWillUnmount() { 24 | if (this.unsubscribe) this.unsubscribe() 25 | 26 | this._isMounted = false 27 | } 28 | 29 | // store 和 state的关系 30 | // store.getState() === state 31 | componentDidUpdate(prevProps) { 32 | // 比较store是否相等,如果不相等则需要订阅 33 | if (this.props.store !== prevProps.store) { 34 | if (this.unsubscribe) this.unsubscribe() 35 | 36 | this.subscribe() 37 | } 38 | } 39 | 40 | subscribe() { 41 | const { store } = this.props 42 | 43 | // redux 的 store 订阅 44 | // 订阅后,每当state改变 则自动执行这个函数 45 | this.unsubscribe = store.subscribe(() => { 46 | // store.getState() 获取最新的 state 47 | const newStoreState = store.getState() 48 | 49 | if (!this._isMounted) { 50 | return 51 | } 52 | 53 | // 比较state是否相等,不等则更新 54 | this.setState(providerState => { 55 | // If the value is the same, skip the unnecessary state update. 56 | if (providerState.storeState === newStoreState) { 57 | return null 58 | } 59 | 60 | return { storeState: newStoreState } 61 | }) 62 | }) 63 | 64 | // actions 有可能在render 和 mount 之间被执行, 修正 state 65 | // Actions might have been dispatched between render and mount - handle those 66 | const postMountStoreState = store.getState() 67 | if (postMountStoreState !== this.state.storeState) { 68 | this.setState({ storeState: postMountStoreState }) 69 | } 70 | } 71 | 72 | render() { 73 | const Context = this.props.context || ReactReduxContext 74 | 75 | return ( 76 | 77 | {this.props.children} 78 | 79 | ) 80 | } 81 | } 82 | 83 | Provider.propTypes = { 84 | store: PropTypes.shape({ 85 | subscribe: PropTypes.func.isRequired, 86 | dispatch: PropTypes.func.isRequired, 87 | getState: PropTypes.func.isRequired 88 | }), 89 | context: PropTypes.object, 90 | children: PropTypes.any 91 | } 92 | 93 | export default Provider 94 | -------------------------------------------------------------------------------- /source-code.lazyestload/src/lazyload.js: -------------------------------------------------------------------------------- 1 | ! function() { 2 | 3 | // wrapper, so we can call the function on both load and scroll events 4 | 5 | function lazyload() { 6 | 7 | // all the images with class lazyload 8 | // 获取class为lazyload的img 9 | var images = document.querySelectorAll("img.lazyload"); 10 | var i = images.length; 11 | 12 | // remove the event listener if there are no images with the class lazyload 13 | 14 | // 如果无class为lazyload,清除监听 15 | !i && window.removeEventListener("scroll", lazyload); 16 | 17 | // loop de loop 18 | // 遍历images 19 | while (i--) { 20 | var wH = window.innerHeight; 21 | // 触发加载的距离 22 | var offset = 100; 23 | // 到达这个元素的距离 24 | var yPosition = images[i].getBoundingClientRect().top - wH; 25 | 26 | // if the top of the image is within 100px from the bottom of the viewport 27 | // 到达图片顶部距离小于 offset,认为在视口内 28 | if (yPosition <= offset) { 29 | // 调用自定义属性中的src 30 | if (images[i].getAttribute("data-src")) { 31 | images[i].src = images[i].getAttribute("data-src"); 32 | } 33 | 34 | // replace the srcset with the data-srcset 35 | // 替换srcset,响应式图片 36 | if (images[i].getAttribute("data-srcset")) { 37 | images[i].srcset = images[i].getAttribute("data-srcset"); 38 | } 39 | 40 | // replace the source srcset's with the data-srcset's 41 | 42 | // 处理 picktur标签内部的source标签的 srcset 属性 43 | if (images[i].parentElement.tagName === "PICTURE") { 44 | var sources = images[i].parentElement.querySelectorAll("source"); 45 | var j = sources.length; 46 | while (j--) { 47 | sources[j].srcset = sources[j].getAttribute("data-srcset"); 48 | } 49 | } 50 | 51 | // wait until the new image is loaded 52 | 53 | images[i].addEventListener('load', function() { 54 | 55 | // remove the class lazyload 56 | // 图片加载完毕,删除lazyload的classname 57 | this.classList.remove("lazyload"); 58 | }); 59 | 60 | } 61 | } 62 | } 63 | 64 | // run on load 65 | 66 | lazyload(); 67 | 68 | // run on scroll event 69 | // 添加scroll监听 70 | window.addEventListener("scroll", lazyload); 71 | }(); -------------------------------------------------------------------------------- /source-code.ant-design/src/rc-tabs/ScrollableInkTabBar.md: -------------------------------------------------------------------------------- 1 | ### ScrollableInkTabBar 2 | 3 | 4 | 整体: 5 | * 此处使用了mixins继承了其他组件的方法 6 | * 不展开每个mixin组件,只说明此处每个方法的作用 7 | * 主要处理了以下事情: 8 | 1. 配置了tab-nav的标题(children的tab属性) 9 | 2. 定义了每个tab-nav的active,disabled,绑定了传递的event 10 | 3. 渲染了墨水条,指示当前active(如果是editable-card),如果设置墨水条不显示,但也加载DOM 11 | 4. 渲染左右按钮,如果tab很少,左右按钮不显示,但同样会加载DOM 12 | 5. 渲染tabs,定义tabPosition(tab-nav的方向) 13 | 6. 定义多层div,自定义传递prefix 14 | 15 | * 最终返回格式大概为(后面为className) 16 | ``` 17 | div .prefix-bar 18 | extraContent(如果有) 19 | div .prefix-nav-container 20 | span .prefix-tab-prev [prefix-tab-btn-disabled] [prefix-tab-arrow-show] 21 | span .prefix-tab-next 22 | div .prefix-nav-wrap 23 | div .prefix-nav-scroll 24 | div .prefix-nav .prefix-nav-animated 25 | inkBarNode 26 | tabs 27 | ``` 28 | 29 | 源码: 30 | 31 | ```jsx 32 | 33 | import createReactClass from 'create-react-class'; 34 | import InkTabBarMixin from './InkTabBarMixin'; 35 | import ScrollableTabBarMixin from './ScrollableTabBarMixin'; 36 | import TabBarMixin from './TabBarMixin'; 37 | import RefMixin from './RefMixin'; 38 | 39 | const ScrollableInkTabBar = createReactClass({ 40 | displayName: 'ScrollableInkTabBar', 41 | mixins: [RefMixin, TabBarMixin, InkTabBarMixin, ScrollableTabBarMixin], 42 | render() { 43 | /* 44 | * 创建墨水条(activeTab的指示器)如下 45 | * ___________________ 46 | * |tab1 tab2 tab3 | 47 | * |¯¯¯¯ | 48 | * |当前是tab1 | 49 | * |_________________| 50 | * */ 51 | const inkBarNode = this.getInkBarNode(); 52 | // 返回一个div,里面有菜单的标题内容 53 | const tabs = this.getTabs(); 54 | {/* 创建了可滚动的菜单,定义了很多className 55 | * 大概如下格式 56 | * div .prefix-nav-container 57 | * span .prefix-nav-prev 58 | * span .prefix-nav-next 59 | * div .prefix-nav-wrap 60 | * div .prefix-nav-scroll 61 | * div .prefix-nav 62 | * 参数[0]:inkBarNode 63 | * 参数[1]:tabs 64 | */} 65 | const scrollbarNode = this.getScrollBarNode([inkBarNode, tabs]); 66 | /* 67 | * 结合以上的组件,并且会从props判断是否有extraContent 68 | * 69 | * 有extraContent(右上角附加内容) 70 | * div .prefix-bar role->tablist 71 | * extraContent 72 | * div .prefix-nav-container 也就是scrollbarNode 73 | * ... 74 | * 75 | * 没有extraContent 76 | * div .prefix-bar role->tablist 77 | * div .prefix-nav-container 78 | * ... 79 | * 80 | * */ 81 | return this.getRootNode(scrollbarNode); 82 | }, 83 | }); 84 | 85 | export default ScrollableInkTabBar; 86 | 87 | ``` -------------------------------------------------------------------------------- /source-code.react-waypoint/src/getCurrentPosition.js: -------------------------------------------------------------------------------- 1 | import constants from './constants'; 2 | 3 | /** 4 | * @param {object} bounds An object with bounds data for the waypoint and 5 | * scrollable parent 6 | * @return {string} The current position of the waypoint in relation to the 7 | * visible portion of the scrollable parent. One of `constants.above`, 8 | * `constants.below`, or `constants.inside`. 9 | */ 10 | export default function getCurrentPosition(bounds) { 11 | // 如果下边线和上边线重合,说明元素不可见 12 | if (bounds.viewportBottom - bounds.viewportTop === 0) { 13 | return constants.invisible; 14 | } 15 | /* 16 | 17 | ___________ 视口顶端 18 | | | 19 | |-----------| 上边线 viewportTop 20 | | | 21 | |--△元素---| 下边线 22 | |___________| viewportBottom 23 | 视口底端 24 | 25 | */ 26 | // 头还在范围内 27 | // top is within the viewport 28 | if (bounds.viewportTop <= bounds.waypointTop && 29 | bounds.waypointTop <= bounds.viewportBottom) { 30 | return constants.inside; 31 | } 32 | 33 | /* 34 | 35 | ___________ 视口顶端 36 | | | 37 | |--△元素---| 上边线 viewportTop 38 | | | 39 | |___________| 下边线 40 | |___________| viewportBottom 41 | 视口底端 42 | 43 | */ 44 | // 尾巴还在范围内 45 | // bottom is within the viewport 46 | if (bounds.viewportTop <= bounds.waypointBottom && 47 | bounds.waypointBottom <= bounds.viewportBottom) { 48 | return constants.inside; 49 | } 50 | 51 | /* 52 | 53 | waypointTop 元素顶端到视口距离___________ 视口顶端 54 | | | |___________| 上边线 viewportTop 55 | | | | | 56 | | | △元素 | 57 | waypointBottom 元素底端到视口距离 |___________| 下边线 58 | |___________| viewportBottom 59 | 视口底端 60 | */ 61 | // 完全处于范围内 62 | // 上边线到视口顶端的距离 <= 元素顶端到视口顶端的距离 并且 63 | // 下边线到视口顶端的距离 >= 元素底端到视口顶端的距离 64 | // top is above the viewport and bottom is below the viewport 65 | if (bounds.waypointTop <= bounds.viewportTop && 66 | bounds.viewportBottom <= bounds.waypointBottom) { 67 | return constants.inside; 68 | } 69 | 70 | // 除了以上情况,元素顶端到视口顶端距离 > 下边线 说明在底下 71 | if (bounds.viewportBottom < bounds.waypointTop) { 72 | return constants.below; 73 | } 74 | 75 | // 元素顶端到视口顶端距离 < 上边线 说明在上面 76 | if (bounds.waypointTop < bounds.viewportTop) { 77 | return constants.above; 78 | } 79 | 80 | return constants.invisible; 81 | } 82 | -------------------------------------------------------------------------------- /source-code.react-motion/src/Types.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // Babel 5.x doesn't support type parameters, so we make this alias here out of 4 | // Babel's sight. 5 | /* eslint-disable spaced-comment, no-undef */ 6 | /*:: 7 | import type {Element} from 'react'; 8 | export type ReactElement = Element<*>; 9 | */ 10 | 11 | // === basic reused types === 12 | // type of the second parameter of `spring(val, config)` all fields are optional 13 | export type SpringHelperConfig = { 14 | stiffness?: number, 15 | damping?: number, 16 | precision?: number, 17 | }; 18 | // the object returned by `spring(value, yourConfig)`. For internal usage only! 19 | export type OpaqueConfig = { 20 | val: number, 21 | stiffness: number, 22 | damping: number, 23 | precision: number, 24 | }; 25 | // your typical style object given in props. Maps to a number or a spring config 26 | export type Style = {[key: string]: number | OpaqueConfig}; 27 | // the interpolating style object, with the same keys as the above Style object, 28 | // with the values mapped to numbers, naturally 29 | export type PlainStyle = {[key: string]: number}; 30 | // internal velocity object. Similar to PlainStyle, but whose numbers represent 31 | // speed. Might be exposed one day. 32 | export type Velocity = {[key: string]: number}; 33 | 34 | // === Motion === 35 | export type MotionProps = { 36 | defaultStyle?: PlainStyle, 37 | style: Style, 38 | children: (interpolatedStyle: PlainStyle) => ReactElement, 39 | onRest?: () => void, 40 | }; 41 | 42 | // === StaggeredMotion === 43 | export type StaggeredProps = { 44 | defaultStyles?: Array, 45 | styles: (previousInterpolatedStyles: ?Array) => Array