React-native
,順便我也複習下React-native,後續寫做計劃應該是主攻Node.js和跨平臺方向、架構、Debug
爲主https://mp.weixin.qq.com/s/RsvI5AFzbp3rm6sOlTmiYQ
穩定的代理工具
(若是沒有穩定的代理工具,基本上能夠考慮放棄了)npx react-native init App cd App yarn cd cd ios pod install (注意不要+sudo,此處必須全局開啓代理,不然下載會失敗) cd .. yarn ios
注意 0.60 版本以後的主項目文件是.xcworkspace,不是.xcodeproj。
/** * @format */ import {AppRegistry} from 'react-native'; import App from './App'; import {name as appName} from './app.json'; AppRegistry.registerComponent(appName, () => App);
import React from 'react'; import { SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar, } from 'react-native'; import { Header, LearnMoreLinks, Colors, DebugInstructions, ReloadInstructions, } from 'react-native/Libraries/NewAppScreen'; const App: () => React$Node = () => { return ( <> <StatusBar barStyle="dark-content" /> <SafeAreaView> <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.scrollView}> <Header /> {global.HermesInternal == null ? null : ( <View style={styles.engine}> <Text style={styles.footer}>Engine: Hermes</Text> </View> )} <View style={styles.body}> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Step One</Text> <Text style={styles.sectionDescription}> Edit <Text style={styles.highlight}>App.js</Text> to change this screen and then come back to see your edits. </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>See Your Changes</Text> <Text style={styles.sectionDescription}> <ReloadInstructions /> </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Debug</Text> <Text style={styles.sectionDescription}> <DebugInstructions /> </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Learn More</Text> <Text style={styles.sectionDescription}> Read the docs to discover what to do next: </Text> </View> <LearnMoreLinks /> </View> </ScrollView> </SafeAreaView> </> ); }; const styles = StyleSheet.create({ ... }); export default App;
import { SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar, } from 'react-native';
'use strict'; import typeof Button from './Libraries/Components/Button'; .... export type HostComponent<T> = _HostComponentInternal<T>; const invariant = require('invariant'); const warnOnce = require('./Libraries/Utilities/warnOnce'); module.exports = { // Components get Button(): Button { return require('./Libraries/Components/Button'); }, ... }; if (__DEV__) { // $FlowFixMe This is intentional: Flow will error when attempting to access ART. Object.defineProperty(module.exports, 'ART', { configurable: true, get() { invariant( false, 'ART has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. " + 'See https://github.com/react-native-community/art', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access ListView. Object.defineProperty(module.exports, 'ListView', { configurable: true, get() { invariant( false, 'ListView has been removed from React Native. ' + 'See https://fb.me/nolistview for more information or use ' + '`deprecated-react-native-listview`.', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access SwipeableListView. Object.defineProperty(module.exports, 'SwipeableListView', { configurable: true, get() { invariant( false, 'SwipeableListView has been removed from React Native. ' + 'See https://fb.me/nolistview for more information or use ' + '`deprecated-react-native-swipeable-listview`.', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access WebView. Object.defineProperty(module.exports, 'WebView', { configurable: true, get() { invariant( false, 'WebView has been removed from React Native. ' + "It can now be installed and imported from 'react-native-webview' instead of 'react-native'. " + 'See https://github.com/react-native-community/react-native-webview', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access NetInfo. Object.defineProperty(module.exports, 'NetInfo', { configurable: true, get() { invariant( false, 'NetInfo has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/netinfo' instead of 'react-native'. " + 'See https://github.com/react-native-community/react-native-netinfo', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access CameraRoll. Object.defineProperty(module.exports, 'CameraRoll', { configurable: true, get() { invariant( false, 'CameraRoll has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/cameraroll' instead of 'react-native'. " + 'See https://github.com/react-native-community/react-native-cameraroll', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access ImageStore. Object.defineProperty(module.exports, 'ImageStore', { configurable: true, get() { invariant( false, 'ImageStore has been removed from React Native. ' + 'To get a base64-encoded string from a local image use either of the following third-party libraries:' + "* expo-file-system: `readAsStringAsync(filepath, 'base64')`" + "* react-native-fs: `readFile(filepath, 'base64')`", ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access ImageEditor. Object.defineProperty(module.exports, 'ImageEditor', { configurable: true, get() { invariant( false, 'ImageEditor has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/image-editor' instead of 'react-native'. " + 'See https://github.com/react-native-community/react-native-image-editor', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access TimePickerAndroid. Object.defineProperty(module.exports, 'TimePickerAndroid', { configurable: true, get() { invariant( false, 'TimePickerAndroid has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/datetimepicker' instead of 'react-native'. " + 'See https://github.com/react-native-community/datetimepicker', ); }, }); // $FlowFixMe This is intentional: Flow will error when attempting to access ViewPagerAndroid. Object.defineProperty(module.exports, 'ViewPagerAndroid', { configurable: true, get() { invariant( false, 'ViewPagerAndroid has been removed from React Native. ' + "It can now be installed and imported from '@react-native-community/viewpager' instead of 'react-native'. " + 'See https://github.com/react-native-community/react-native-viewpager', ); }, }); }
這個源碼文件大概有650行,module.export暴露出來了不少東西,可是,區分多種react
get AppRegistry(): AppRegistry { return require('./Libraries/ReactNative/AppRegistry'); },
get Image(): Image { return require('./Libraries/Image/Image'); },
./Libraries/Image/Image
源碼
module.exports = ((Image: any): React.AbstractComponent< ImagePropsType, React.ElementRef<typeof RCTImageView>, > & ImageComponentStatics);
return ( <RCTImageView {...props} ref={forwardedRef} style={style} resizeMode={resizeMode} tintColor={tintColor} source={sources} /> );
RCTImageView
,ImageViewNativeComponent.js
這個文件let ImageViewNativeComponent; if (global.RN$Bridgeless) { ImageViewNativeComponent = codegenNativeComponent<NativeProps>( 'RCTImageView', ); } else { ImageViewNativeComponent = requireNativeComponent<NativeProps>( 'RCTImageView', ); } module.exports = (ImageViewNativeComponent: HostComponent<NativeProps>);
ImageViewNativeComponent
,關於上面這段源碼我查閱了一些的外文資料和其餘源碼,最終發現了一個註釋const NativeModules = require('../BatchedBridge/NativeModules'); const turboModuleProxy = global.__turboModuleProxy; export function get < T: TurboModule > (name: string): ? T { if (!global.RN$Bridgeless) { // Backward compatibility layer during migration. const legacyModule = NativeModules[name]; if (legacyModule != null) { return ((legacyModule: any): T); } } if (turboModuleProxy != null) { const module: ? T = turboModuleProxy(name); return module; } return null; } export function getEnforcing < T: TurboModule > (name: string): T { const module = get(name); return module; }
Backward compatibility layer during migration.
,即遷移過程當中向後兼容,即兼容性處理codegenNativeComponent
就是圖片展現最終的一環,咱們去看看是什麼let componentNameInUse = options && options.paperComponentName ? options.paperComponentName : componentName; if (options != null && options.paperComponentNameDeprecated != null) { if (UIManager.getViewManagerConfig(componentName)) { componentNameInUse = componentName; } else if ( options.paperComponentNameDeprecated != null && UIManager.getViewManagerConfig(options.paperComponentNameDeprecated) ) { componentNameInUse = options.paperComponentNameDeprecated; } else { throw new Error( `Failed to find native component for either ${componentName} or ${options.paperComponentNameDeprecated || '(unknown)'}`, ); } } // If this function is run at runtime then that means the view configs were not // generated with the view config babel plugin, so we need to require the native component. // // This will be useful during migration, but eventually this will error. return (requireNativeComponent<Props>( componentNameInUse, ): HostComponent<Props>);
UIManager.getViewManagerConfig
'use strict'; import type {Spec} from './NativeUIManager'; interface UIManagerJSInterface extends Spec { +getViewManagerConfig: (viewManagerName: string) => Object; +createView: ( reactTag: ?number, viewName: string, rootTag: number, props: Object, ) => void; +updateView: (reactTag: number, viewName: string, props: Object) => void; +manageChildren: ( containerTag: ?number, moveFromIndices: Array<number>, moveToIndices: Array<number>, addChildReactTags: Array<number>, addAtIndices: Array<number>, removeAtIndices: Array<number>, ) => void; } const UIManager: UIManagerJSInterface = global.RN$Bridgeless === true ? require('./DummyUIManager') // No UIManager in bridgeless mode : require('./PaperUIManager'); module.exports = UIManager;
PaperUIManager
找到getViewManagerConfiggetViewManagerConfig: function(viewManagerName: string): any { if ( viewManagerConfigs[viewManagerName] === undefined && NativeUIManager.getConstantsForViewManager ) { try { viewManagerConfigs[ viewManagerName ] = NativeUIManager.getConstantsForViewManager(viewManagerName); } catch (e) { viewManagerConfigs[viewManagerName] = null; } } const config = viewManagerConfigs[viewManagerName]; if (config) { return config; } // If we're in the Chrome Debugger, let's not even try calling the sync // method. if (!global.nativeCallSyncHook) { return config; } if ( NativeUIManager.lazilyLoadView && !triedLoadingConfig.has(viewManagerName) ) { const result = NativeUIManager.lazilyLoadView(viewManagerName); triedLoadingConfig.add(viewManagerName); if (result.viewConfig) { getConstants()[viewManagerName] = result.viewConfig; lazifyViewManagerConfig(viewManagerName); } } return viewManagerConfigs[viewManagerName]; },
viewManagerConfigs
初始化是一個空對象,key-value形式存儲、管理這些原生視圖配置return (requireNativeComponent<Props>( componentNameInUse, ): HostComponent<Props>);
requireNativeComponent
,根據componentName去加載原生組件,找到源碼'use strict'; const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass'); const getNativeComponentAttributes = require('./getNativeComponentAttributes'); import type {HostComponent} from '../Renderer/shims/ReactNativeTypes'; const requireNativeComponent = <T>(uiViewClassName: string): HostComponent<T> => ((createReactNativeComponentClass(uiViewClassName, () => getNativeComponentAttributes(uiViewClassName), ): any): HostComponent<T>); module.exports = requireNativeComponent;
最重要的加載原生組件的代碼找到了
((createReactNativeComponentClass(uiViewClassName, () => getNativeComponentAttributes(uiViewClassName), ): any): HostComponent<T>)
createReactNativeComponentClass
傳入uiViewClassName
即組件name,傳入回調函數,返回 getNativeComponentAttributes(uiViewClassName)
createReactNativeComponentClass
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format * @flow strict-local */ 'use strict'; import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; import type {ViewConfigGetter} from './ReactNativeTypes'; const {register} = ReactNativeViewConfigRegistry; /** * Creates a renderable ReactNative host component. * Use this method for view configs that are loaded from UIManager. * Use createReactNativeComponentClass() for view configs defined within JavaScript. * * @param {string} config iOS View configuration. * @private */ const createReactNativeComponentClass = function( name: string, callback: ViewConfigGetter, ): string { return register(name, callback); }; module.exports = createReactNativeComponentClass;
ReactNativePrivateInterface.js
裏面的ReactNativeViewConfigRegistryget ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry { return require('../Renderer/shims/ReactNativeViewConfigRegistry'); },
exports.register = function(name: string, callback: ViewConfigGetter): string { invariant( !viewConfigCallbacks.has(name), 'Tried to register two views with the same name %s', name, ); invariant( typeof callback === 'function', 'View config getter callback for component `%s` must be a function (received `%s`)', name, callback === null ? 'null' : typeof callback, ); viewConfigCallbacks.set(name, callback); return name; };
按名稱註冊本機視圖/組件。 提供了一個回調函數來從UIManager加載視圖配置。 回調被延遲直到視圖被實際呈現。
function getNativeComponentAttributes(uiViewClassName: string): any { const viewConfig = UIManager.getViewManagerConfig(uiViewClassName); invariant( viewConfig != null && viewConfig.NativeProps != null, 'requireNativeComponent: "%s" was not found in the UIManager.', uiViewClassName, ); // TODO: This seems like a whole lot of runtime initialization for every // native component that can be either avoided or simplified. let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig; let nativeProps = viewConfig.NativeProps; while (baseModuleName) { const baseModule = UIManager.getViewManagerConfig(baseModuleName); if (!baseModule) { warning(false, 'Base module "%s" does not exist', baseModuleName); baseModuleName = null; } else { bubblingEventTypes = { ...baseModule.bubblingEventTypes, ...bubblingEventTypes, }; directEventTypes = { ...baseModule.directEventTypes, ...directEventTypes, }; nativeProps = { ...baseModule.NativeProps, ...nativeProps, }; baseModuleName = baseModule.baseModuleName; } } const validAttributes = {}; for (const key in nativeProps) { const typeName = nativeProps[key]; const diff = getDifferForType(typeName); const process = getProcessorForType(typeName); validAttributes[key] = diff == null && process == null ? true : {diff, process}; } // Unfortunately, the current setup declares style properties as top-level // props. This makes it so we allow style properties in the `style` prop. // TODO: Move style properties into a `style` prop and disallow them as // top-level props on the native side. validAttributes.style = ReactNativeStyleAttributes; Object.assign(viewConfig, { uiViewClassName, validAttributes, bubblingEventTypes, directEventTypes, }); if (!hasAttachedDefaultEventTypes) { attachDefaultEventTypes(viewConfig); hasAttachedDefaultEventTypes = true; } return viewConfig; }