精华内容
下载资源
问答
  • 本篇文章主要介绍了探究react-native 源码的图片缓存问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • react-native 源码地址有吗?(github)上的,要最新版本的不要6以下的
  • react-native源码分析系列一

    千次阅读 2015-12-08 14:34:41
    react-native源码目前我看到过的最好的分析文章是qq空间分析的这篇。 http://zhuanlan.zhihu.com/magilu/20259704 不得不说大厂推出的分析确实牛逼,逻辑清晰。 不过由于博主自己也读过源码,也做过一些分析,...

    react-native源码目前我看到过的最好的分析文章是qq空间分析的这篇。

    http://zhuanlan.zhihu.com/magilu/20259704

    不得不说大厂推出的分析确实牛逼,逻辑清晰。

    不过由于博主自己也读过源码,也做过一些分析,因此准备将这些分析整理一下放出来(这个系列会比较长。

    react-native 官网https://github.com/facebook/react-native

    这里不介绍怎么使用,有兴趣的可以参考我的个人项目https://github.com/xiaoshenke/React-Online-News

    照例我们从demo开始分析,我看的demo是react-native/Examples/Movies工程。

    以下rn代指react-native。


    可以看到初始化代码只有两行。
    先看第一行代码,初始化构建ReactInstanceManager。
    ReactInstanceManager
    mReactInstanceManager = ReactInstanceManager.builder()           //builder模式
    .setApplication(getApplication())
    .setBundleAssetName("MoviesApp.android.bundle")                        //bundleName貌似可以随便取
    .setJSMainModuleName("Examples/Movies/MoviesApp.android")  //这里的名字必须是对应js文件入口的名字
    .addPackage(new MainReactPackage())                                            //注意名字 这里就是主要的rn包//官网有说明怎么加自定义包
    .setUseDeveloperSupport(true)                                                      //manifest中的DevSettingsActivity
    .setInitialLifecycleState(LifecycleState.RESUMED)
    .build();

    public ReactInstanceManager build() {
      return new ReactInstanceManager(
         Assertions.assertNotNull(
             mApplication,
             "Application property has not been set with this builder"),
         mBundleAssetName,
         mJSMainModuleName,
         mPackages,
         mUseDeveloperSupport,
         mBridgeIdleDebugListener,
         Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"));
    }

    private ReactInstanceManager(                                          //ReactInstanceManager构造函数 
       Context applicationContext,
       @Nullable String bundleAssetName,
       @Nullable String jsMainModuleName,
       List<ReactPackage> packages,
       boolean useDeveloperSupport,
       @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
       LifecycleState initialLifecycleState) {
      initializeSoLoaderIfNecessary(applicationContext);
      mApplicationContext = applicationContext;
      mBundleAssetName = bundleAssetName;                         //这里传入的是“MoviesApp.android.bundle"
      mJSMainModuleName = jsMainModuleName;                  //这里传入的是“Examples/Movies/MoviesApp.android
      mPackages = packages;                
      mUseDeveloperSupport = useDeveloperSupport;              //这里传入的是true
      // We need to instantiate DevSupportManager regardless to the useDeveloperSupport option,
      // although will prevent dev support manager from displaying any options or dialogs by
      // checking useDeveloperSupport option before calling setDevSupportEnabled on this manager
      // TODO(6803830): Don't instantiate devsupport manager when useDeveloperSupport is false
      mDevSupportManager = new DevSupportManager(         //调试用 这里不分析
         applicationContext,
         mDevInterface,
         mJSMainModuleName,
         useDeveloperSupport);
      mBridgeIdleDebugListener = bridgeIdleDebugListener;
      mLifecycleState = initialLifecycleState;
    }

                    通过以上过程构造出了ReactInstanceManager
    然后是第二行代码。ReactRootView.startReactApplication
    我们看一下ReactRootView是干啥的。
         所以ReactRootView是React工程中的rootview,并且负责分发各种touch事件。
    public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
    public class SizeMonitoringFrameLayout extends FrameLayout
    public interface RootView {
      void onChildStartedNativeGesture(MotionEvent androidEvent);
    }

    继续看真正的初始化入口startReactApplication函数。
    /**
    * Schedule rendering of the react component rendered by the JS application from the given JS
    * module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the
    * JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
    * properties for the react component.    
    */
    public void startReactApplication(ReactInstanceManager reactInstanceManager,
       String moduleName,@Nullable Bundle launchOptions) {         //这里的moduleName传入的是“MoviesApp”
            //如果你懂一点React.js的话 js代码大概长这样 module.exports=MoviesApp-->就是说这是一个可以被其他mudule调用的对象
      mReactInstanceManager = reactInstanceManager;
      mJSModuleName = moduleName;
      mLaunchOptions = launchOptions;
      if (mWasMeasured && mIsAttachedToWindow) {
       mReactInstanceManager.attachMeasuredRootView(this);      //真正的入口
       mIsAttachedToInstance = true;
       getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
      } else {
       mAttachScheduled = true;
      }
    }   

    /**
    * Attach given {@param rootView} to a catalyst instance manager and start JS application using
    * JS module provided by {@link ReactRootView#getJSModuleName}. This view will then be tracked
    * by this manager and in case of catalyst instance restart it will be re-attached.
    */
    /* package */ void attachMeasuredRootView(ReactRootView rootView) {
      UiThreadUtil.assertOnUiThread();
      mAttachedRootViews.add(rootView);
      if (mCurrentReactContext == null) {
       initializeReactContext();       //生成重要对象ReactContext(很眼熟是不是~ 这个context继承自android的ContextWrapper
          //关于context 老罗的这篇文章http://blog.csdn.net/luoshengyang/article/details/8201936里
          //可以看到Activity是继承自ContextWrapper的  
      } else {
       attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
      }
    }

    initializeReactContext  —> createReactContext   //中间一系列调用略去

    /**
    * @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
    */
    private ReactApplicationContext createReactContext(
       JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) {
       ...
    }                              重要函数  //后面一篇文章具体分析这个函数
          //剧透一下 大概就是干了生成一个context 注册了一堆双方共用的控件 服务 其实就是打通了js native代码交互的通道
           
    createReactContext后会调用attachMeasuredRootViewToInstance
    private void attachMeasuredRootViewToInstance(
       ReactRootView rootView,CatalystInstance catalystInstance) {
      UiThreadUtil.assertOnUiThread();
      // Reset view content as it's going to be populated by the application content from JS
      rootView.removeAllViews();
      rootView.setId(View.NO_ID);
      UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class); //注意名字 nativeModule
          //统一处理js java两边对应控件的manager
          // rn是用js封装了一套native的对应控件 比如native里的DrawerLayout js那边对应的是drawerLayoutAndroid
          //因此需要一个manager来进行协调
      int rootTag = uiManagerModule.addMeasuredRootView(rootView);   
                                   //这里调用了native的addMeasuredRootView
      @Nullable Bundle launchOptions = rootView.getLaunchOptions();
      WritableMap initialProps = launchOptions != null
         ? Arguments.fromBundle(launchOptions)
         : Arguments.createMap();
      String jsAppModuleName = rootView.getJSModuleName();          //注意名字 jsModule
      WritableNativeMap appParams = new WritableNativeMap();
      appParams.putDouble("rootTag", rootTag);
      appParams.putMap("initialProps", initialProps);
      catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
                    //这里获取了js的一个模块 AppRegistry 并调用了js模块里的runApplication函数
    }  

    ctrl f搜索runApplication。可以看到对应的js文件及runApplication这个接口。


    AppRegistry.js文件。


    好了,第一篇文章先到这里。
    总结一下,这篇文章分析了react-native初始化的过程(两行代码。
    第一行代码,构造出一个reactInstanceManager类(这个类就是一个调度类。
    第二行代码,ReactRootView.startApplication(这里是初始化入口,包括生成重要的ReactContext 以及后面的打通js java native通信等等。

    展开全文
  • 写在开头近期公众号主攻下React-native,顺便我也复习下React-native,后续写作计划应该是主攻Node.js和跨平台方向、架构、Debug为主如果你感兴趣,建议关注下公...

    写在开头

    • 近期公众号主攻下React-native,顺便我也复习下React-native,后续写作计划应该是主攻Node.js和跨平台方向、架构、Debug为主

    • 如果你感兴趣,建议关注下公众号,系统的学习下,推荐阅读之前我的的年度原创文章集合:https://mp.weixin.qq.com/s/RsvI5AFzbp3rm6sOlTmiYQ

    正式开始

    • 环境准备:Node、Watchman、Xcode 和 CocoaPods & XCode ,稳定的代理工具(如果没有稳定的代理工具,基本上可以考虑放弃了)

    • 生成项目

    npx react-native init App
    cd App 
    yarn cd 
    cd ios 
    pod install (注意不要+sudo,此处必须全局开启代理,否则下载会失败)
    cd ..
    yarn ios 
    
    • 如果yarn ios后无法看到Simulator有APP,使用xCode找到这个项目的ios目录的.xcworkspace

    注意 0.60 版本之后的主项目文件是.xcworkspace,不是.xcodeproj。

    • 然后用xCode打开build,成功后模拟器就会出现APP,打开即可进入

    • ⚠️:一定不要升级xCode高版本,跟我的版本保持一致最好,因为高版本xCode的voip唤醒激活会出现电话界面

    如果你的环境是windows或者安卓,请参考官网

    正式开始

    • 启动后,发现APP这样

    • 我们打开主入口的index.js文件

    /**
     * @format
     */
    
    import {AppRegistry} from 'react-native';
    import App from './App';
    import {name as appName} from './app.json';
    
    AppRegistry.registerComponent(appName, () => App);
    
    
    • 默认使用AppRegistry.registerComponent帮我们注册了一个组件(今天不对原理做过多讲解,有兴趣的可以自己搭建一个React-native脚手架,你会对整套运行原理、流程有一个真正的了解)

    • 接下来看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.pContainer}>
                  <Text style={styles.pTitle}>Step One</Text>
                  <Text style={styles.pDescription}>
                    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.pContainer}>
                  <Text style={styles.pTitle}>See Your Changes</Text>
                  <Text style={styles.pDescription}>
                    <ReloadInstructions />
                  </Text>
                </View>
                <View style={styles.pContainer}>
                  <Text style={styles.pTitle}>Debug</Text>
                  <Text style={styles.pDescription}>
                    <DebugInstructions />
                  </Text>
                </View>
                <View style={styles.pContainer}>
                  <Text style={styles.pTitle}>Learn More</Text>
                  <Text style={styles.pDescription}>
                    Read the docs to discover what to do next:
                  </Text>
                </View>
                <LearnMoreLinks />
              </View>
            </ScrollView>
          </SafeAreaView>
        </>
      );
    };
    
    const styles = StyleSheet.create({
     ...
    });
    
    export default App;
    
    

    我们今天只看react-native这个库,默认导出的内容.

    • 即下面这段代码

    import {
      SafeAreaView,
      StyleSheet,
      ScrollView,
      View,
      Text,
      StatusBar,
    } from 'react-native';
    
    • 打开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',
          );
        },
      });
    }
    
    
    • 我删了一些倒入和get定义,方便阅读

    • 这个源码文件大概有650行,module.export暴露出来了很多东西,但是,区分多种

      • 一种是Components组件

      • 一种是API

      • 一种是Plugins

      • 一种是Prop Types

      • 还有一种是最后的DEV环境下,

    逐个攻破

    • 首先是组件

    • 其次是API

    • 然后是Plugins

    • 然后是Prop types

    • 最后是DEV环境下的对旧版本的部分API使用方式警告

    可以看到入口文件中的一些API

    • 例如

      get AppRegistry(): AppRegistry {
        return require('./Libraries/ReactNative/AppRegistry');
      },
    
    • 图片

      get Image(): Image {
        return require('./Libraries/Image/Image');
      },
    

    拿Image组件源码示例

    • 找到./Libraries/Image/Image源码

    • 脚手架应该根据是react-native run ios 还是 安卓,选择加载对应js,我们找到Image.ios.js文件,只有200行,今天重点主攻下

    • 默认暴露

    module.exports = ((Image: any): React.AbstractComponent<
      ImagePropsType,
      React.ElementRef<typeof RCTImageView>,
    > &
      ImageComponentStatics);
    
    • Image对象

    • Image组件真正展示的

      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找到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];
      },
    
    • viewManagerConfigs初始化是一个空对象,key-value形式存储、管理这些原生视图配置

    • 我突然发现我错了路线,因为React-native虽然是用js写代码,不过最终都是转换成原生控件,回到主题的第一个代码底部

      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

    • 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;
    
    • 跟我预想一样,向register函数传入name和cb,注册成功后触发callback(getNativeComponentAttributes)

    • 找到ReactNativePrivateInterface.js里面的ReactNativeViewConfigRegistry

      get ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry {
        return require('../Renderer/shims/ReactNativeViewConfigRegistry');
      },
    
    • 再找到register方法

    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;
    };
    
    
    • 重点:viewConfigCallbacks.set(name, callback);viewConfigCallbacks是一个Map类型(ES6),key-value数据结构,怎么理解这段代码,看注释:

    按名称注册本机视图/组件。
    提供了一个回调函数来从UIManager加载视图配置。
    回调被延迟直到视图被实际呈现。
    
    • 至此,加载原生组件逻辑配合之前的UImanager,getViewManagerConfig那块源码就解析完了。

    • 这是我们传入的cb(回调函数),获取原生组件属性

    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;
    }
    
    • 至此,一个完整的React-native组件解析从加载、注册、展现整个过程就解析完了。

    写在最后

    • 本文gitHub源码仓库:https://github.com/JinJieTan/chunchao,记得给个star

    • 我是Peter,架构设计过20万人端到端加密超级群功能的桌面IM软件,现在是一名前端架构师。

      如果你对性能优化有很深的研究,可以跟我一起交流交流,今天这里写得比较浅,但是大部分人都够用,之前问我的朋友,我让它写了一个定时器定时消费队列,最后也能用。哈哈

      另外欢迎收藏我的资料网站:前端生活社区:https://qianduan.life,感觉对你有帮助,可以右下角点个在看,关注一波公众号:[前端巅峰]

    展开全文
  • React Native releases are discussed in the React Native Community, @react-native-community/react-native-releases. How to Contribute The main purpose of this repository is to continue evolving React ...
  • react-native-maps-demo:react-native-maps-demo
  • curso-react-native:Curso React-native-Mateus Fraga
  • React-Native-Spotify-Lite:使用react-native的Spotify克隆
  • 这个软件包的灵感来自惊人的但是增加了一些功能,当我们尝试在我们的react-native应用程序中缓存图像时,我们会缺少这些功能。 安装 npm install react-native-cached-image --save - or - yarn add react-native-...
  • react-native-training:我的react-native训练文件
  • react-native-collapsing-工具栏 react-native包装器,易于与Animated.Event和FlatList集成。 入门 确保同时安装react-native-collapsing-toolbar和react-native-nested-scroll-view 。 $ npm install react-native...
  • React本机Tabbar交互 使用React Native进行的滑动插入式FAB的精美Tabbar交互。 在Béhance上查看( ) 在Dribbble上查看( ) ...import TabBar from "@mindinventory/react-native-tab-bar-interacti
  • react-native-ART-doc:react react-native ART模块的非官方文档
  • react-native-zeroconf React-native的基本Zeroconf实现 使用Zeroconf实现(例如Avahi,Bonjour或NSD)来宣传自己的运行服务。 安装 yarn add react-native-zeroconf # for react-native < 0> = 7上可用。
  • React-Native-源码

    2021-03-04 09:57:40
    React-Native
  • 但是对于React-Native,只有一个组件是a)易于设置和b)高度可定制的: react-native-onboarding-swiper 。 您的新用户不应陷入困境。 首先给他们一个愉快,愉快的介绍,然后让他们探索您的真棒应用程序。 使一切...
  • react-native-material-kit> = 0.4.0仅支持react-native> = 0.40.0 react-native-material-kit <0> = 0.29 npm install -S react-native-material-kit react-native link react-native-material-kit 手动地 将...
  • react-native-maps-directions Directions组件–在两个坐标之间绘制路线,由Google Maps Directions API提供支持 安装 使用以下任一方法将react-native-maps-directions安装为依赖项 npm install react-native-...
  • react-native-qrcode-svg 基于react-native-svg和javascript-qrcode的React Native QR码生成器。 讨论: : 产品特点 轻松渲染QR码图像 (可选)嵌入徽标 安卓 的iOS 安装 安装依赖包 yarn add react-native-svg ...
  • react-native编写。 预告片 镜子: 要求 0.61.1 (遵循iOS和Android指南) 建立 提供了一个开发环境。 您可以调试代码,从命令选项板快速运行react-native命令,并使用IntelliSense浏览React Native API的对象,...
  • react-native-dva-starter 由和驱动的React Native启动器 开始 npm install dva-native-cli -g dvanative git app cd app react-native run-ios # or react-native run-android 非常感谢@ xuan45提供的cli工具 ,...
  • react-native link @sentry/react-native # OR, if self hosting SENTRY_WIZARD_URL=http://sentry.acme.com/ react-native link @sentry/react-native 如何使用它: import * as Sentry from "@sentry/react-...
  • react-native-css React-native-css将有效CSS转换为CSS的Facebook子集。 Babel插件 很棒的@ danilosterrapid7为React-native-css创建了一个babel插件: 版本2 随着版本2的到来,新的变化: 删除sass / scss支持,...
  • Quizex-React-Native-源码

    2021-02-17 21:36:47
    Quizex-React-Native
  • Prueba-React-Native-源码

    2021-02-17 07:35:52
    Prueba-React-Native
  • react-native-keyboard-spacer 即插即用iOS react-native键盘间隔视图。 快速开始 安装视图: npm install --save react-native-keyboard-spacer 用法示例 当键盘出现或消失时,视图将自动执行布局动画。 import ...
  • gamezone-React-Native-源码

    2021-02-12 18:21:27
    gamezone-React-Native

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,193
精华内容 1,277
关键字:

react-native源码