Write at the beginning
- Recently, the official account of React-native is the main attack. By the way, I will review the next React-native. Node.js And cross platform direction, architecture and Debug
- If you are interested in the proposal, we should recommend the following official account. https://mp.weixin.qq.com/s/RsvI5AFzbp3rm6sOlTmiYQ
Official start
- Environment preparation: Node, Watchman, Xcode and cocoapods & Xcode, stable agent tool (if there is no stable agent tool, it can basically be considered to give up)
- Build project
npx react-native init App cd App yarn cd cd ios pod install (Be careful not to+sudo,Agent must be turned on globally here,Otherwise, the download will fail) cd .. yarn ios
- If you can't see the Simulator has APP after yarn ios, use xCode to find the. xcworkspace of ios directory of this project
Note that the main project file after version 0.60 is. xcworkspace, not. xcodeproj.
- Then open the build with xCode. After the success, the simulator will display the APP. Open it and enter
- ⚠️ : do not upgrade the higher version of xCode. It is better to keep it consistent with my version, because the voip wake-up activation of the higher version of xCode will show a phone interface
If your environment is windows or Android, please refer to the official website
Official start
- After startup, it is found that the APP is like this
- We opened the main entrance index.js file
/** * @format */ import {AppRegistry} from 'react-native'; import App from './App'; import {name as appName} from './app.json'; AppRegistry.registerComponent(appName, () => App);
- Default use AppRegistry.registerComponent Registered a component for us (don't explain the principle too much today. If you are interested, you can build a react native scaffold by yourself. You will have a real understanding of the whole operation principle and process)
- Next let's look at the APP components
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;
Today, we only look at the react native library, which exports by default
- Here's the code
import { SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar, } from 'react-native';
- Open react native source
'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', ); }, }); }
- I deleted some pour and get definitions for easy reading
-
This source file has about 650 lines, module.export There's a lot of exposure, but there's a lot of differentiation
- One is Components
- One is API
- One is Plugins
- One is Prop Types
- Another is the last DEV environment,
One by one
- First, components
- The second is API
- And then Plugins
- Then Prop types
- Finally, some warnings on the usage of API in the old version in DEV environment
You can see some API s in the entry file
- for example
get AppRegistry(): AppRegistry { return require('./Libraries/ReactNative/AppRegistry'); },
- picture
get Image(): Image { return require('./Libraries/Image/Image'); },
Take the Image component source code example
- Find. / Libraries/Image/Image source code
- The scaffold should be loaded according to whether it is react native run IOS or Android. We can find Image.ios.js Documents, only 200 lines, focus on today
- Default exposure
module.exports = ((Image: any): React.AbstractComponent< ImagePropsType, React.ElementRef<typeof RCTImageView>, > & ImageComponentStatics);
- Image object
- What the Image component really shows
return ( <RCTImageView {...props} ref={forwardedRef} style={style} resizeMode={resizeMode} tintColor={tintColor} source={sources} /> );
- Find rctimageview, imag eViewNativeComponent.js This document
let ImageViewNativeComponent; if (global.RN$Bridgeless) { ImageViewNativeComponent = codegenNativeComponent<NativeProps>( 'RCTImageView', ); } else { ImageViewNativeComponent = requireNativeComponent<NativeProps>( 'RCTImageView', ); } module.exports = (ImageViewNativeComponent: HostComponent<NativeProps>);
- The real display is ImageViewNativeComponent. I checked some foreign materials and other source codes about the above source code, and finally found a comment
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; }
- That is, Backward compatibility layer during migration
- This CodeGen native component is the final part of the picture display. Let's see what it is
Ignore other null value warning judgment such as type and enter the topic directly
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>);
- Or first 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;
- Enter PaperUIManager to find getViewManagerConfig
getViewManagerConfig: 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]; },
- The initialization of viewManagerConfigs is an empty object, and the key-value form stores and manages these native view configurations
- I suddenly found out that I was on the wrong path, because although react native uses js to write code, it is ultimately converted into native controls, returning to the bottom of the first code of the topic
return (requireNativeComponent<Props>( componentNameInUse, ): HostComponent<Props>);
- The most important thing is: requireNativeComponent. Load the native component according to the component name and find the source code
'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;
The most important code to load the native component is found
((createReactNativeComponentClass(uiViewClassName, () => getNativeComponentAttributes(uiViewClassName), ): any): HostComponent<T>)
Resolve ` createReactNativeComponentClass
- Pass in uiViewClassName (component name) to createReactNativeComponentClass, pass in callback function, and return getNativeComponentAttributes(uiViewClassName)
- Find the source code 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;
- As I expected, pass in name and cb to the register function, and trigger callback(getNativeComponentAttributes) after successful registration
- Find reactna tivePrivateInterface.js ReactNativeViewConfigRegistry in it
get ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry { return require('../Renderer/shims/ReactNativeViewConfigRegistry'); },
- Find the register method again
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; };
- a key: viewConfigCallbacks.set(name, callback);viewConfigCallbacks is a Map type (ES6), key value data structure. How to understand this code
Register native views / components by name. A callback function is provided to load the view configuration from UIManager. The callback is delayed until the view is actually rendered.
- At this point, the source code of UImanager and getViewManagerConfig before loading the native component logic is resolved.
- This is the CB (callback function) we passed in to get the native component properties
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; }
- At this point, the whole process of a complete react native component parsing from loading, registration and presentation is finished.