React之Context源码分析与实践

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"React之Context"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Context设计目的是为了共享哪些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"使用示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,要在公共位置定义创建一个Context:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ColorContext.js"}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// default colors\nconst colors = {\n\tthemeColor: ‘red'\n}\n\nexport const ColorContext = React.createContext(colors)\n// 可以给Context指定展示名称\nColorContext.displayName = \"ColorContext”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:只有当消费组件所处的组件树中没有匹配的Provider时,default参数才会生效。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在组件树的顶部,使用Provider:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { ColorContext } from \"./ColorContext”\n\nfunction Root() {\n return (\n \n \t\n \n\t)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Provider内的所有组件都可以接收ColorContext,并且Provider接收"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"属性并传递给消费组件,"},{"type":"text","marks":[{"type":"strong"}],"text":"一个Provider内可以有多个消费组件,并且Provider可以嵌套使用,此时里层的会覆盖外层的数据"},{"type":"text","text":",多个嵌套时可以参考文档"},{"type":"link","attrs":{"href":"https://zh-hans.reactjs.org/docs/context.html#consuming-multiple-contexts","title":""},"content":[{"type":"text","text":"Context – React"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要注意的是,当"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"变化时,它内部的所有消费组件都会重新渲染,且Provider及内部消费组件都不受"},{"type":"codeinline","content":[{"type":"text","text":"shouldComponentUpdate"}]},{"type":"text","text":"函数影响,而"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"值变化的检测则是使用与"},{"type":"codeinline","content":[{"type":"text","text":"Object.is"}]},{"type":"text","text":"相同的方法。可以对Consumer进行缓存,如使用"},{"type":"codeinline","content":[{"type":"text","text":"React.memo()"}]},{"type":"text","text":"来缓存组件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然我们也可以基于上面的代码进行封装,提供一个ColorProvider,并提供修改Color的API:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export const ColorProvider = (props) => {\n\tconst [color, setColor] = React.useState(colors)\n\treturn (\n\t\t\n\t\t\t{props.children}\n\t\t\n\t)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于Class的ColorProvider如下:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"class ColorProvider extends React.Component {\n readonly state = { count: 0 };\n\n increment = (delta: number) => this.setState({\n count: this.state.count + delta\n })\n\n render() {\n return (\n \n {props.children}\n \n );\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Class版-使用Consumer"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Provider内部的任务子组件内,都可以使用Context提供的Consumer组件来接收Context内的值:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { ColorContext } from \"./ColorContext”\nclass Header extends React.Component {\n\treturn (\n\t\t\n\t\t\t{colors => \n\t\t\n\t)\n}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Hook版-使用Consumer"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与Class版类似,我们可以在子组件内使用"},{"type":"codeinline","content":[{"type":"text","text":"useContext"}]},{"type":"text","text":"来接收Context:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import React, { useContext } from “react”\nimport { ColorContext } from \"./ColorContext”\nfunction Header() {\n\tconst { colors } = useContext(ColorContext)\n\treturn (\n\t\t\n\t)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React在渲染一个消费组件时,该组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的Context值。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"源码分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先上源码,过滤dev环境代码后,比较少的代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/facebook/react/blob/3ca1904b37ad1f527ff5e31b51373caea67478c5/packages/react/src/ReactContext.js","title":""},"content":[{"type":"text","text":"react/ReactContext.js at 3ca1904b37ad1f527ff5e31b51373caea67478c5 · facebook/react · GitHub"}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE } from \"shared/ReactSymbols\"\n\nimport type {ReactContext} from \"shared/ReactTypes\"\n\nexport function createContext(\n defaultValue: T,\n calculateChangedBits: ?(a: T, b: T) => number,\n): ReactContext {\n if (calculateChangedBits === undefined) {\n calculateChangedBits = null;\n }\n\n const context: ReactContext = {\n $$typeof: REACT_CONTEXT_TYPE,\n _calculateChangedBits: calculateChangedBits,\n _currentValue: defaultValue,\n _currentValue2: defaultValue,\n _threadCount: 0,\n Provider: (null: any),\n Consumer: (null: any),\n }\n\n context.Provider = {\n $$typeof: REACT_PROVIDER_TYPE,\n _context: context\n }\n\n context.Consumer = context;\n\n return context\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建全局Context的方法非常简单,对外提供Provider、Consumer,其中Provider内部属性"},{"type":"codeinline","content":[{"type":"text","text":"_context"}]},{"type":"text","text":"又指向自身,Provider组件内部value改变时其实会作用到context的_currentValue,而最重要的地方是:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"context.Consumer = context"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让Consumer直接指向Context本身,则Context值变化,Consumer中都可以立即拿到。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1d/1d59f872d14011c228d0e531e2ffb145.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"无论是在Class组件或新的Fiber架构中,最终对外提供Context的方法都是"},{"type":"codeinline","content":[{"type":"text","text":"readContext"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberNewContext.new.js","title":""},"content":[{"type":"text","text":"ReactFiberNewContext.new.js"}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export function readContext(\n context: ReactContext,\n observedBits: void | number | boolean,\n): T {\n let contextItem = {\n context: ((context: any): ReactContext),\n observedBits: resolvedObservedBits,\n next: null,\n };\n\n if (lastContextDependency === null) {\n // This is the first dependency for this component. Create a new list.\n lastContextDependency = contextItem;\n currentlyRenderingFiber.contextDependencies = {\n first: contextItem,\n expirationTime: NoWork,\n };\n } else {\n // Append a new context item.\n lastContextDependency = lastContextDependency.next = contextItem;\n }\n }\n\n return isPrimaryRenderer ? context._currentValue : context._currentValue2;\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看下"},{"type":"codeinline","content":[{"type":"text","text":"useContext"}]},{"type":"text","text":"的实现:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"useContext: readContext"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就是这么简单的实现~"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"React-Router之Context使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这部分主要是通过解读React-Router源码中对Context的使用,来加深对其的了解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React-Router项目中主要定义了两个Context: "},{"type":"codeinline","content":[{"type":"text","text":"HistoryContext"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"RouterContext"}]},{"type":"text","text":",对应代码在:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/HistoryContext.js","title":""},"content":[{"type":"text","text":"HistoryContext.js"}]},{"type":"text","text":" "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/RouterContext.js","title":""},"content":[{"type":"text","text":"RouterContext.js"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如RouterContext源码:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// mini-create-react-context,类createContext API, 计划替换中\nimport createContext from \"mini-create-react-context”\nconst createNamedContext = name => {\n\tconst context = createContext();\n\tcontext.displayName = name;\n\treturn contex\n}\n\nconst context = createNamedContext(“Router”)\nexport default context;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Router.js#L56","title":""},"content":[{"type":"text","text":"Router.js"}]},{"type":"text","text":"中使用对应的Context:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"render() {\n\treturn (\n\t\t\n\t\t\t\n\t\t\n\t)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在高版本的React-Router中,也提供了对应的Hook API ,参考源码"},{"type":"link","attrs":{"href":"https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/hooks.js","title":""},"content":[{"type":"text","text":"react-router/hooks.js at master · ReactTraining/react-router · GitHub"}]},{"type":"text","text":",如"},{"type":"codeinline","content":[{"type":"text","text":"useLocation"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"useHisotry"}]},{"type":"text","text":"同样是基于上面讲到的HistoryContext和RouterContext,如useHistory:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import React, { useContext } from “react”\nimport HistoryContext from \"./HistoryContext”\nexport function useHistory() {\n\treturn useContext(HistoryContext)\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"MobX-React之Context使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/mobxjs/mobx-react","title":""},"content":[{"type":"text","text":"GitHub - mobxjs/mobx-react: React bindings for MobX"}]},{"type":"text","text":" MobX-React早期版本提供一对API来方便传递store: "},{"type":"codeinline","content":[{"type":"text","text":"Provider"}]},{"type":"text","text":"/"},{"type":"codeinline","content":[{"type":"text","text":"inject"}]},{"type":"text","text":",内部实现就是基于context。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,通常在新的代码实现中已经不在需要使用"},{"type":"codeinline","content":[{"type":"text","text":"Provider"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"inject"}]},{"type":"text","text":",其大部分功能已经被"},{"type":"codeinline","content":[{"type":"text","text":"React.createContext"}]},{"type":"text","text":"覆盖"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Provider"}]},{"type":"text","text":"组件可以传递store或其他内容给子组件,而不需要遍历各层级组件。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"inject"}]},{"type":"text","text":"可以用来选中Provider中传递的store,该方法作为一个HOC高阶组件,接收指定的字符串或字符串数组(store名称),并将其传入被包裹的子组件内;或者接收一个函数,其回到参数为全部store,并返回要传递给子组件的stores。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"使用示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定义最外层组件容器,使用Provider传递想要传递的内容"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"class MessageList extends React.Component {\n render() {\n const children = this.props.messages.map(message => )\n return (\n \n
{children}
\n
\n )\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此处只传递单个属性color,也可以结合mobx定义store,将整个store对象传递下去。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后在子组件内通过inject选择指定的值:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"@inject(“color”)\n@observer\nclass Button extends React.Component {\n render() {\n return \n }\n}\n\nclass Message extends React.Component {\n render() {\n return (\n
\n {this.props.text} \n
\n )\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Provider源码分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Provider内部使用"},{"type":"codeinline","content":[{"type":"text","text":"React.createContext"}]},{"type":"text","text":"来定义Context"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"export const MobXProviderContext = React.createContext({})\n\nexport interface ProviderProps extends IValueMap {\n children: React.ReactNode\n}\n\nexport function Provider(props: ProviderProps) {\n\tconst { children, ...stores } = props\n\t// 通过useContext消费Context\n\tconst parentValue = React.useContext(MobXProviderContext)\n\t// 通过ref保持所有context值\n\tconst mutableProviderRef = React.useRef({ …parentValue, …stores })\n const value = mutableProviderRef.current\n\treturn {children}\n}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"inject源码分析"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { MobXProvider } from \"./Provider”\n/**\n* 可接收一个字符串数组,或一个回调函数:storesToProps(mobxStores, props, context) => newProps\n*/\nexport function inject(...storeNames: Array) {\n\tif (typeof arguments[0] === \"function”) {\n\t\tlet grabStoreFn = arguments[0]\n\t\treturn (componentClass: React.ComponentClass) =>\n createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true)\n\n\t} else {\n\t\treturn (componentClass: React.ComponentClass) =>\n createStoreInjector(\n grabStoresByName(storeNames),\n componentClass,\n storeNames.join(“-“),\n false\n )\n\t}\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可见其内部调用了"},{"type":"codeinline","content":[{"type":"text","text":"createStoreInjector(grabStoreFn, componentClass, storesName, boolean)"}]}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"grabStoreFn: 用来处理选择哪些store,当参数为函数时则使用自定义函数作为处理函数"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"componentClass: 子组件"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"storesName: 需要选择的store名称"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"boolean: 是否将组件监听变为observer"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function createStoreInjector(\n grabStoresFn: IStoresToProps,\n component: IReactComponent,\n injectNames: string,\n makeReactive: boolean\n): IReactComponent {\n // 支持forward refs\n let Injector: IReactComponent = React.forwardRef((props, ref) => {\n const newProps = { …props }\n\t\t\t// 通过useContext来消费全局的Context\n const context = React.useContext(MobXProviderContext)\n\t\t\t// 赋值操作,将指定store作为子组件的最新props\n Object.assign(newProps, grabStoresFn(context || {}, newProps) || {})\n if (ref) {\n newProps.ref = ref\n }\n\t\t\t// 返回包裹后的子组件\n return React.createElement(component, newProps)\n })\n\t // inject接收函数回调时,则默认讲组件变为observer\n if (makeReactive) Injector = observer(Injector)\n Injector[“isMobxInjector\"] = true // assigned late to suppress observer warning\n // 拷贝子组件的静态方法\n copyStaticProperties(component, Injector)\n\t // 将wrappedComponent指向原始子组件\n Injector[“wrappedComponent”] = component\n Injector.displayName = getInjectName(component, injectNames)\n return Injector\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面关于React的Context内容已经结束了,包括基本使用方式,又通过源码解读来深入了解其原理,最后学习React-Router和MobX-React库的源码彻底掌握Context的使用场景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"不想结束的部分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Provider与Consumer本身,作为React中的特殊组件类型,有其特殊的实现方式,本文并没有仔细去分析。如果想深入了解其实现原理,可以自行去阅读React源码,但是直接阅读React代码库是比较费力的,分析定位起来会比较复杂。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"给爱学习的同学推荐React-Router依赖的"},{"type":"codeinline","content":[{"type":"text","text":"mini-create-react-context"}]},{"type":"text","text":",该库单纯作为对React中"},{"type":"codeinline","content":[{"type":"text","text":"createContext"}]},{"type":"text","text":"方法的polyfill实现,其内部基于Class语法定义了Provider和Consumer两种组件,可以很好地理解内部原理,传送门:"},{"type":"link","attrs":{"href":"https://github.com/StringEpsilon/mini-create-react-context/blob/master/src/implementation.ts","title":""},"content":[{"type":"text","text":"mini-create-react-context"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"核心代码:内部定义了一个EventEmitter,在Provider中value改变时,emit change事件,而在Consumer中则监听value的update事件,从而实现子组件接收Context的值,典型的跨组件通信实现方式,对该方式不提熟悉的同学可以自行了解[EventBus]通信方式,Vue中使用很常见,通过定义一个空的Vue示例作为EventBus,然后组件间通过"},{"type":"codeinline","content":[{"type":"text","text":"$emit"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"$on"}]},{"type":"text","text":"来发布/订阅消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ee/eea9c1fb04cf40f92e9e3231ce8b4852.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章