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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章